Commit 90ac1840bb

90ac1840bb737629cbee8366af487c7ace300f87

parent: 3337af385a

Verified · cmc

cmc <hello@cleberg.net> · 2026-10-14 08:04 UTC

API tokens: a mint cannot be lost; the copy stays here briefly

Cancel waits while a token is minted, which would otherwise be made and
never shown. A copied token stays on this device and leaves the
clipboard after two minutes. New tokens default to read, as the server
mints them; the revoke prompt says that revoking what a token created
can sign this app out too.

Ref #34

Layout: unified · split

gitbay/Views/Account/TokensView.swift +13 −3
@@ -1,5 +1,6 @@
11import SwiftUI
22import UIKit
3import UniformTypeIdentifiers
34
45/// Your API tokens: what each may do, when it was used and stops
56/// working, and who minted it. Mint one from the + button; swipe to revoke.
@@ -62,7 +63,7 @@ struct TokensView: View {
6263 Button("Revoke with what it created", role: .destructive) { revoke(token, withCreated: true) }
6364 Button("Cancel", role: .cancel) {}
6465 } message: { _ in
65 Text("Anything using it stops working at once. If this app signed in with it, the app signs out. What it created, tokens and keys, stays unless you revoke it too.")
66 Text("Anything using it stops working at once. What it created, tokens and keys, stays unless you revoke it too. If this app signed in with it, or with one of those, the app signs out.")
6667 }
6768 .sheet(isPresented: $minting, onDismiss: { minted = nil }) {
6869 MintTokenSheet(model: model, minted: $minted)
@@ -132,7 +133,8 @@ private struct MintTokenSheet: View {
132133 @Binding var minted: String?
133134
134135 @State private var name = ""
135 @State private var scope = "full"
136 /// read, as the server mints by default; full is a choice.
137 @State private var scope = "read"
136138 @State private var ttl: String?
137139 @State private var sent = false
138140 @State private var copied = false
@@ -148,7 +150,12 @@ private struct MintTokenSheet: View {
148150 .textSelection(.enabled)
149151 .accessibilityIdentifier("token-secret")
150152 Button(copied ? "Copied" : "Copy") {
151 UIPasteboard.general.string = minted
153 // This device only, and gone in two minutes: a
154 // token is not for Universal Clipboard or for
155 // whatever is pasted next week.
156 UIPasteboard.general.setItems(
157 [[UTType.utf8PlainText.identifier: minted]],
158 options: [.localOnly: true, .expirationDate: Date.now.addingTimeInterval(120)])
152159 copied = true
153160 }
154161 .accessibilityIdentifier("token-copy")
@@ -194,7 +201,10 @@ private struct MintTokenSheet: View {
194201 }
195202 } else {
196203 ToolbarItem(placement: .cancellationAction) {
204 // Not while minting: the token would be made and
205 // never shown.
197206 Button("Cancel") { dismiss() }
207 .disabled(model.working)
198208 }
199209 ToolbarItem(placement: .confirmationAction) {
200210 if model.working {