@tempoxyz/auth 0.0.0-pr-19.sha-2c8fa8e → 0.0.0-pr-22.sha-291bbcb

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -52,15 +52,15 @@ The CLI device grant needs neither. It signs in as Tempo Auth's own `tempo-cli`
52
52
 
53
53
  Each sign-in creates a key for the app and asks Tempo Auth to authorize it on the user's Tempo Account. The `accessKey` option says what the key may do, with viem's `Expiry` and `Scopes` helpers:
54
54
 
55
- | Field | Meaning |
56
- | --------- | -------------------------------------------------------------------------------------------- |
57
- | `account` | Optional: which of the user's Tempo Accounts, from `getTempoAccounts`. Defaults to the first |
58
- | `chainId` | The chain the key acts on, such as `tempoTestnet.id` |
59
- | `expiry` | When the key expires, in Unix seconds, such as `Expiry.days(1)` |
60
- | `limits` | One-time spending limits per token, such as `[{ limit: 10_000_000n, token }]` |
61
- | `scopes` | The calls the key may make, such as `[Scopes.tip20(token).transfer()]` |
55
+ | Field | Meaning |
56
+ | --------- | --------------------------------------------------------------------------------------------- |
57
+ | `account` | Optional: which of the user's Tempo Accounts, from `tempoAccount.list`. Defaults to the first |
58
+ | `chainId` | The chain the key acts on, such as `tempoTestnet.id` |
59
+ | `expiry` | When the key expires, in Unix seconds, such as `Expiry.days(1)` |
60
+ | `limits` | One-time spending limits per token, such as `[{ limit: 10_000_000n, token }]` |
61
+ | `scopes` | The calls the key may make, such as `[Scopes.tip20(token).transfer()]` |
62
62
 
63
- Flows without a Tempo Auth page (`authorize`, `verifyCode`) must stay within Tempo Auth's ceiling, about $1,000 per token. Flows with a page (`redirect`, `createAuthorizationUrl`, `authorizeDevice`) show the limits to the user, who approves them, so they may go beyond it.
63
+ Flows without a Tempo Auth page (`authorize`, `email.signIn`) must stay within Tempo Auth's ceiling, about $1,000 per token. Flows with a page (`redirect.start`, `redirect.createUrl`, `device.authorize`) show the limits to the user, who approves them, so they may go beyond it.
64
64
 
65
65
  The returned `account` attaches the key's authorization to its first transaction, which registers the key on chain.
66
66
 
@@ -75,18 +75,18 @@ For a browser app that renders its own email form. The page calls Tempo Auth wit
75
75
  const authorization = await Actions.authorize(auth, { accessKey })
76
76
 
77
77
  // Otherwise, sign in with an emailed code.
78
- await Actions.sendCode(auth, { email })
79
- const result = await Actions.verifyCode(auth, { accessKey, code, email })
78
+ await Actions.email.sendCode(auth, { email })
79
+ const result = await Actions.email.signIn(auth, { accessKey, code, email })
80
80
 
81
81
  // An address that requires SSO continues at its connection, and returns to this page.
82
- if ('step' in result) Actions.signIn(auth, { url: result.step.url })
82
+ if ('step' in result) Actions.provider.signIn(auth, { url: result.step.url })
83
83
  ```
84
84
 
85
85
  To offer OAuth providers such as Google, list the ones Tempo Auth enables and send the browser to one. It returns to the current page signed in, where `authorize` finishes.
86
86
 
87
87
  ```ts
88
- const providers = await Actions.getProviders(auth) // [{ id: 'google', name: 'Google' }]
89
- Actions.signIn(auth, { provider: 'google' })
88
+ const providers = await Actions.provider.list(auth) // [{ id: 'google', name: 'Google' }]
89
+ Actions.provider.signIn(auth, { provider: 'google' })
90
90
  ```
91
91
 
92
92
  ### Tempo Auth's pages
@@ -94,13 +94,13 @@ Actions.signIn(auth, { provider: 'google' })
94
94
  For a browser app without its own sign-in UI. The browser leaves for Tempo Auth's sign-in page, which shows the key's limits, and returns with a code.
95
95
 
96
96
  ```ts
97
- await Actions.redirect(auth, { accessKey })
97
+ await Actions.redirect.start(auth, { accessKey })
98
98
 
99
99
  // On the page the browser returns to, and on every load:
100
- const authorization = await Actions.handleRedirect(auth)
100
+ const authorization = await Actions.redirect.complete(auth)
101
101
  ```
102
102
 
103
- `redirect` keeps its state in session storage until the browser returns. Pass `storage` to `Auth.create` to keep it elsewhere.
103
+ `redirect.start` keeps its state in session storage until the browser returns. Pass `storage` to `Auth.create` to keep it elsewhere.
104
104
 
105
105
  ### A mobile app
106
106
 
@@ -116,16 +116,16 @@ const auth = Auth.create({
116
116
  })
117
117
 
118
118
  // Your own sign-in UI, as in a browser.
119
- await Actions.sendCode(auth, { email })
120
- const result = await Actions.verifyCode(auth, { accessKey, code, email })
119
+ await Actions.email.sendCode(auth, { email })
120
+ const result = await Actions.email.signIn(auth, { accessKey, code, email })
121
121
 
122
122
  // Or Tempo Auth's pages, which also offer Google and SSO.
123
- const { pending, url } = await Actions.createAuthorizationUrl(auth, {
123
+ const { pending, url } = await Actions.redirect.createUrl(auth, {
124
124
  accessKey,
125
125
  })
126
126
  const session = await WebBrowser.openAuthSessionAsync(url, auth.redirectUri)
127
127
  if (session.type === 'success')
128
- authorization = await Actions.handleCallback(auth, {
128
+ authorization = await Actions.redirect.handleCallback(auth, {
129
129
  pending,
130
130
  url: session.url,
131
131
  })
@@ -141,14 +141,14 @@ For an app whose server holds a client secret. The server builds the URL, keeps
141
141
  const auth = Auth.create({ clientId, clientSecret, url })
142
142
 
143
143
  // GET /auth/start
144
- const { pending, url } = await Actions.createAuthorizationUrl(auth, {
144
+ const { pending, url } = await Actions.redirect.createUrl(auth, {
145
145
  accessKey,
146
146
  redirectUri: 'https://app.example/auth/callback',
147
147
  })
148
148
  // Keep `pending` in an encrypted, HttpOnly cookie, then redirect to `url`.
149
149
 
150
150
  // GET /auth/callback
151
- const authorization = await Actions.handleCallback(auth, {
151
+ const authorization = await Actions.redirect.handleCallback(auth, {
152
152
  pending,
153
153
  url: request.url,
154
154
  })
@@ -159,7 +159,7 @@ const authorization = await Actions.handleCallback(auth, {
159
159
  For a command-line tool. The user approves a code in any browser, and the CLI waits.
160
160
 
161
161
  ```ts
162
- const device = await Actions.authorizeDevice(auth, {
162
+ const device = await Actions.device.authorize(auth, {
163
163
  accessKey,
164
164
  device: os.hostname(),
165
165
  })
@@ -174,8 +174,8 @@ const authorization = await device.wait()
174
174
  A user starts with one Tempo Account and can have more. Add one, then name it in an access key's `account`:
175
175
 
176
176
  ```ts
177
- const tempoAccount = await Actions.createTempoAccount(auth)
178
- const accounts = await Actions.getTempoAccounts(auth) // the default first
177
+ const tempoAccount = await Actions.tempoAccount.create(auth)
178
+ const accounts = await Actions.tempoAccount.list(auth) // the default first
179
179
 
180
180
  await Actions.authorize(auth, {
181
181
  accessKey: { ...accessKey, account: tempoAccount.address },
@@ -205,7 +205,7 @@ A request Tempo Auth refuses throws `Auth.RequestError`, with its `status` and `
205
205
 
206
206
  ```ts
207
207
  try {
208
- await Actions.verifyCode(auth, { accessKey, code, email })
208
+ await Actions.email.signIn(auth, { accessKey, code, email })
209
209
  } catch (error) {
210
210
  if (error instanceof Auth.RequestError && error.status === 401)
211
211
  showWrongCode()
@@ -223,27 +223,29 @@ try {
223
223
  | `clientSecret` | The app's client secret. Only a backend holds one |
224
224
  | `redirectUri` | Where Tempo Auth redirects back to. Defaults to the page's origin |
225
225
  | `scope` | Scopes beyond `openid` and `email`, which every instance includes. Defaults to `offline_access` |
226
- | `storage` | Where `redirect` keeps its state. Defaults to session storage |
226
+ | `storage` | Where `redirect.start` keeps its state. Defaults to session storage |
227
227
  | `fetch` | Calls Tempo Auth. Defaults to the global `fetch` |
228
228
 
229
229
  ### `Actions`
230
230
 
231
- | Action | Returns |
232
- | ---------------------------------------------------------- | -------------------------------------------------------------------------- |
233
- | `authorize(auth, { accessKey })` | An `Authorization` for a user already signed in, or `undefined` |
234
- | `authorizeDevice(auth, { accessKey, device })` | The code to show and a `wait()` that resolves with an `Authorization` |
235
- | `createAuthorizationUrl(auth, { accessKey, redirectUri })` | The `url` to send the browser to, and the `pending` state to keep |
236
- | `createTempoAccount(auth)` | Another Tempo Account for the signed-in user |
237
- | `getProviders(auth)` | The OAuth providers Tempo Auth offers, as `{ id, name }` |
238
- | `getTempoAccounts(auth)` | The signed-in user's Tempo Accounts, the default first |
239
- | `handleCallback(auth, { pending, url })` | The `Authorization` for a redirect's callback |
240
- | `handleRedirect(auth)` | The `Authorization` when the page is a redirect's callback, or `undefined` |
241
- | `redirect(auth, { accessKey })` | Nothing. The browser leaves for Tempo Auth's pages |
242
- | `refresh(auth, { refreshToken })` | New `Tokens` |
243
- | `sendCode(auth, { email })` | Nothing. Tempo Auth emails a code |
244
- | `signIn(auth, { provider } \| { url })` | Nothing. The browser leaves for the provider or SSO connection |
245
- | `signOut(auth)` | Nothing. The user's Tempo Auth session ends |
246
- | `verifyCode(auth, { accessKey, code, email })` | An `Authorization`, or the `step` the address requires first |
231
+ Each sign-in flow has its own namespace. The actions every flow shares sit at the top level.
232
+
233
+ | Action | Returns |
234
+ | ------------------------------------------------------ | -------------------------------------------------------------------------- |
235
+ | `authorize(auth, { accessKey })` | An `Authorization` for a user already signed in, or `undefined` |
236
+ | `refresh(auth, { refreshToken })` | New `Tokens` |
237
+ | `signOut(auth)` | Nothing. The user's Tempo Auth session ends |
238
+ | `device.authorize(auth, { accessKey, device })` | The code to show and a `wait()` that resolves with an `Authorization` |
239
+ | `email.sendCode(auth, { email })` | Nothing. Tempo Auth emails a code |
240
+ | `email.signIn(auth, { accessKey, code, email })` | An `Authorization`, or the `step` the address requires first |
241
+ | `provider.list(auth)` | The OAuth providers Tempo Auth offers, as `{ id, name }` |
242
+ | `provider.signIn(auth, { provider } \| { url })` | Nothing. The browser leaves for the provider or SSO connection |
243
+ | `redirect.complete(auth)` | The `Authorization` when the page is a redirect's callback, or `undefined` |
244
+ | `redirect.createUrl(auth, { accessKey, redirectUri })` | The `url` to send the browser to, and the `pending` state to keep |
245
+ | `redirect.handleCallback(auth, { pending, url })` | The `Authorization` for a redirect's callback |
246
+ | `redirect.start(auth, { accessKey })` | Nothing. The browser leaves for Tempo Auth's pages |
247
+ | `tempoAccount.create(auth)` | Another Tempo Account for the signed-in user |
248
+ | `tempoAccount.list(auth)` | The signed-in user's Tempo Accounts, the default first |
247
249
 
248
250
  ### `Authorization`
249
251
 
package/dist/Actions.d.ts CHANGED
@@ -1,4 +1,9 @@
1
- import { type AccessKey, type Auth, type Authorization, type Pending, type Provider, type Step, type TempoAccount, type Tokens } from './Auth.ts';
1
+ import type { AccessKey, Auth, Authorization, Tokens } from './Auth.ts';
2
+ export * as device from './actions/device.ts';
3
+ export * as email from './actions/email.ts';
4
+ export * as provider from './actions/provider.ts';
5
+ export * as redirect from './actions/redirect.ts';
6
+ export * as tempoAccount from './actions/tempoAccount.ts';
2
7
  /**
3
8
  * Authorizes the app for a user already signed in to Tempo Auth, from any app. Returns
4
9
  * `undefined` without a session, so the app shows sign-in. The key must fit Tempo Auth's headless
@@ -19,168 +24,6 @@ export declare namespace authorize {
19
24
  /** The authorization, or `undefined` without a session. */
20
25
  type ReturnType = Authorization | undefined;
21
26
  }
22
- /**
23
- * Starts the device grant for a CLI: the user approves `userCode` at `verificationUri` from any
24
- * browser, and `wait` resolves once they do.
25
- *
26
- * @example
27
- * ```ts
28
- * const device = await Actions.authorizeDevice(auth, { accessKey, device: Os.hostname() })
29
- * console.log(`Open ${device.verificationUriComplete}`)
30
- * const authorization = await device.wait()
31
- * ```
32
- */
33
- export declare function authorizeDevice(auth: Auth, options: authorizeDevice.Options): Promise<authorizeDevice.ReturnType>;
34
- export declare namespace authorizeDevice {
35
- type Options = {
36
- /** What the CLI's key may do. The approval page shows it beside the device. */
37
- accessKey: AccessKey;
38
- /** The name the approval page shows, such as the machine's hostname. */
39
- device: string;
40
- };
41
- type ReturnType = {
42
- /** Seconds until the code expires. */
43
- expiresIn: number;
44
- /** The code the user confirms in the browser. */
45
- userCode: string;
46
- /** Where the user enters the code. */
47
- verificationUri: string;
48
- /** Where the user approves the code, with it already filled in. */
49
- verificationUriComplete: string;
50
- /** Resolves with the authorization once the user approves. */
51
- wait: () => Promise<Authorization>;
52
- };
53
- }
54
- /**
55
- * Returns the URL that signs the user in on Tempo Auth's pages and redirects back with a code,
56
- * and the {@link Pending} state to keep until then, such as in a sealed cookie.
57
- * {@link handleCallback} redeems the code for the tokens and the key authorization.
58
- *
59
- * @example
60
- * ```ts
61
- * const { pending, url } = await Actions.createAuthorizationUrl(auth, {
62
- * accessKey,
63
- * redirectUri: 'https://app.example/auth/callback',
64
- * })
65
- * await seal(c, 'oauth', pending)
66
- * return c.redirect(url)
67
- * ```
68
- */
69
- export declare function createAuthorizationUrl(auth: Auth, options: createAuthorizationUrl.Options): Promise<createAuthorizationUrl.ReturnType>;
70
- export declare namespace createAuthorizationUrl {
71
- type Options = {
72
- /** What the app's key may do. Tempo Auth's pages show it, and the user approves it. */
73
- accessKey: AccessKey;
74
- /** Where Tempo Auth redirects back to. Defaults to the instance's `redirectUri`. */
75
- redirectUri?: string | undefined;
76
- };
77
- type ReturnType = {
78
- /** What to keep until the callback, such as in a sealed cookie. */
79
- pending: Pending;
80
- /** Where to send the browser. */
81
- url: string;
82
- };
83
- }
84
- /**
85
- * Creates another Tempo Account for the signed-in user, and returns it. Pass its `address` as an
86
- * access key's `account` to authorize keys on it.
87
- *
88
- * @example
89
- * ```ts
90
- * const tempoAccount = await Actions.createTempoAccount(auth)
91
- * await Actions.authorize(auth, {
92
- * accessKey: { ...accessKey, account: tempoAccount.address },
93
- * })
94
- * ```
95
- */
96
- export declare function createTempoAccount(auth: Auth): Promise<createTempoAccount.ReturnType>;
97
- export declare namespace createTempoAccount {
98
- /** The new account. */
99
- type ReturnType = TempoAccount;
100
- }
101
- /**
102
- * Lists the OAuth providers Tempo Auth offers, for an app that shows its own sign-in buttons.
103
- *
104
- * @example
105
- * ```ts
106
- * const providers = await Actions.getProviders(auth)
107
- * // [{ id: 'google', name: 'Google' }]
108
- * ```
109
- */
110
- export declare function getProviders(auth: Auth): Promise<getProviders.ReturnType>;
111
- export declare namespace getProviders {
112
- type ReturnType = readonly {
113
- /** The provider's short name, which `signIn` takes. */
114
- id: Provider;
115
- /** The name to show, such as `Google`. */
116
- name: string;
117
- }[];
118
- }
119
- /**
120
- * Lists the signed-in user's Tempo Accounts, their default first.
121
- *
122
- * @example
123
- * ```ts
124
- * const [account, ...others] = await Actions.getTempoAccounts(auth)
125
- * ```
126
- */
127
- export declare function getTempoAccounts(auth: Auth): Promise<getTempoAccounts.ReturnType>;
128
- export declare namespace getTempoAccounts {
129
- /** The user's accounts, their default first. */
130
- type ReturnType = readonly TempoAccount[];
131
- }
132
- /**
133
- * Finishes a sign-in redirect: checks its state, and redeems the code with the PKCE verifier (and
134
- * the client secret, for a backend) for the tokens and the key authorization.
135
- *
136
- * @example
137
- * ```ts
138
- * const authorization = await Actions.handleCallback(auth, {
139
- * pending: await unseal(c, 'oauth'),
140
- * url: c.req.url,
141
- * })
142
- * ```
143
- */
144
- export declare function handleCallback(auth: Auth, options: handleCallback.Options): Promise<Authorization>;
145
- export declare namespace handleCallback {
146
- type Options = {
147
- /** What `createAuthorizationUrl` returned to keep. */
148
- pending: Pending;
149
- /** The callback URL Tempo Auth redirected to. */
150
- url: string | URL;
151
- };
152
- }
153
- /**
154
- * Finishes a {@link redirect} when the page loads with Tempo Auth's callback, and returns
155
- * `undefined` otherwise. The code is redeemed once, however often the page calls this.
156
- *
157
- * @example
158
- * ```ts
159
- * const authorization =
160
- * (await Actions.handleRedirect(auth)) ??
161
- * (await Actions.authorize(auth, { accessKey }))
162
- * ```
163
- */
164
- export declare function handleRedirect(auth: Auth): Promise<handleRedirect.ReturnType>;
165
- export declare namespace handleRedirect {
166
- /** The authorization, or `undefined` when the page is not a redirect's callback. */
167
- type ReturnType = Authorization | undefined;
168
- }
169
- /**
170
- * Sends the browser to Tempo Auth's pages to sign in, keeping the redirect's state in `storage`.
171
- *
172
- * @example
173
- * ```ts
174
- * await Actions.redirect(auth, { accessKey })
175
- * ```
176
- */
177
- export declare function redirect(auth: Auth, options: redirect.Options): Promise<void>;
178
- export declare namespace redirect {
179
- type Options = {
180
- /** What the app's key may do. Tempo Auth's pages show it, and the user approves it. */
181
- accessKey: AccessKey;
182
- };
183
- }
184
27
  /**
185
28
  * Exchanges a refresh token for new tokens. Refresh tokens rotate, so store the new one.
186
29
  *
@@ -198,43 +41,6 @@ export declare namespace refresh {
198
41
  refreshToken: string;
199
42
  };
200
43
  }
201
- /**
202
- * Emails the user a sign-in code. The response is the same for every address.
203
- *
204
- * @example
205
- * ```ts
206
- * await Actions.sendCode(auth, { email: 'user@example.com' })
207
- * ```
208
- */
209
- export declare function sendCode(auth: Auth, options: sendCode.Options): Promise<void>;
210
- export declare namespace sendCode {
211
- type Options = {
212
- /** The user's email address. */
213
- email: string;
214
- };
215
- }
216
- /**
217
- * Sends the browser to an OAuth provider, or to the SSO connection a {@link Step} names, and back to
218
- * `returnTo` signed in to Tempo Auth. The app then calls {@link authorize}.
219
- *
220
- * @example
221
- * ```ts
222
- * Actions.signIn(auth, { provider: 'google' })
223
- * ```
224
- */
225
- export declare function signIn(auth: Auth, options: signIn.Options): void;
226
- export declare namespace signIn {
227
- type Options = ({
228
- /** The OAuth provider to sign in with, from `getProviders`. */
229
- provider: Provider;
230
- } | {
231
- /** The URL of a `Step`, such as the SSO connection an address requires. */
232
- url: string;
233
- }) & {
234
- /** Where to return. Defaults to the current page. It must be on an internal origin. */
235
- returnTo?: string | undefined;
236
- };
237
- }
238
44
  /**
239
45
  * Ends the user's Tempo Auth session, which signs them out of every app that relies on it.
240
46
  *
@@ -244,28 +50,3 @@ export declare namespace signIn {
244
50
  * ```
245
51
  */
246
52
  export declare function signOut(auth: Auth): Promise<void>;
247
- /**
248
- * Signs the user in with the code they were emailed, and authorizes the app in the same call.
249
- * Returns a {@link Step} when the address requires SSO, which {@link signIn} continues.
250
- *
251
- * @example
252
- * ```ts
253
- * const result = await Actions.verifyCode(auth, { accessKey, code, email })
254
- * if ('step' in result) Actions.signIn(auth, { url: result.step.url })
255
- * ```
256
- */
257
- export declare function verifyCode(auth: Auth, options: verifyCode.Options): Promise<verifyCode.ReturnType>;
258
- export declare namespace verifyCode {
259
- type Options = {
260
- /** What the app's key may do. It must fit the headless ceiling. */
261
- accessKey: AccessKey;
262
- /** The 6-digit code the user was emailed. */
263
- code: string;
264
- /** The address the code was sent to. */
265
- email: string;
266
- };
267
- /** The authorization, or the step the address requires first. */
268
- type ReturnType = Authorization | {
269
- step: Step;
270
- };
271
- }