@tempoxyz/auth 0.0.0-pr-71.sha-b89841e → 0.0.0-pr-68.sha-79216ef

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/dist/Actions.d.ts CHANGED
@@ -12,7 +12,6 @@ import type { Tokens } from './Tokens.ts';
12
12
  export * as device from './actions/device.ts';
13
13
  export * as email from './actions/email.ts';
14
14
  export * as oauth from './actions/oauth.ts';
15
- export * as passkey from './actions/passkey.ts';
16
15
  export * as provider from './actions/provider.ts';
17
16
  export * as redirect from './actions/redirect.ts';
18
17
  export * as tempoAccount from './actions/tempoAccount.ts';
package/dist/Actions.js CHANGED
@@ -4,7 +4,6 @@ import { request } from './internal/request.js';
4
4
  export * as device from './actions/device.js';
5
5
  export * as email from './actions/email.js';
6
6
  export * as oauth from './actions/oauth.js';
7
- export * as passkey from './actions/passkey.js';
8
7
  export * as provider from './actions/provider.js';
9
8
  export * as redirect from './actions/redirect.js';
10
9
  export * as tempoAccount from './actions/tempoAccount.js';
@@ -82,10 +82,7 @@ export declare namespace tempoAuth {
82
82
  email: {
83
83
  /** `Actions.email.sendCode`. */
84
84
  sendCode: (options: Actions.email.sendCode.Options) => Promise<void>;
85
- /**
86
- * `Actions.email.signIn`, which sets the client's account unless it returns a step. A step
87
- * that another sign-in or a sign-out overtook throws {@link SupersededError}.
88
- */
85
+ /** `Actions.email.signIn`, which sets the client's account unless it returns a step. */
89
86
  signIn: <const id extends number = chainId>(options: Actions.email.signIn.Options<id>) => Promise<Actions.email.signIn.ReturnType<id>>;
90
87
  };
91
88
  /** The instance the members call `Actions` with. */
@@ -98,13 +95,6 @@ export declare namespace tempoAuth {
98
95
  /** `Actions.oauth.userInfo`. */
99
96
  userInfo: (options: Actions.oauth.userInfo.Options) => Promise<User>;
100
97
  };
101
- passkey: {
102
- /**
103
- * `Actions.passkey.signIn`, which sets the client's account unless it returns a step. A step
104
- * that another sign-in or a sign-out overtook throws {@link SupersededError}.
105
- */
106
- signIn: <const id extends number = chainId>(options?: Actions.passkey.signIn.Options<id>) => Promise<Actions.passkey.signIn.ReturnType<id>>;
107
- };
108
98
  provider: {
109
99
  /** `Actions.provider.list`. */
110
100
  list: () => Promise<Actions.provider.list.ReturnType>;
package/dist/Decorator.js CHANGED
@@ -54,66 +54,42 @@ export function tempoAuth(options) {
54
54
  // sign-in or sign-out completed after it began, so a stale result never replaces a newer one,
55
55
  // and a sign-in still pending at sign-out never signs the client back in.
56
56
  let version = 0;
57
- // The version the latest sign-out produced, while no change has followed it.
58
- let signedOut;
59
57
  /**
60
- * Begins an operation that changes the client's account. `apply` applies its result: the
61
- * account to sign with, or `undefined` to stop signing. `check` accepts a result that changes
62
- * no account, such as a step. Applying an account, or checking a step, that a newer change
63
- * overtook throws {@link SupersededError}, so the caller never holds a result the client's
64
- * newer state contradicts. `fail` rethrows the operation's error, or throws `SupersededError`
65
- * when a newer change overtook it, so the caller never acts on a stale refusal. A sign-in
66
- * refused after its code or passkey verified already set a server session, which `fail` ends.
58
+ * Begins an operation that changes the client's account, and returns the function that applies
59
+ * its result: the account to sign with, or `undefined` to stop signing. Applying an account that
60
+ * a newer change overtook throws {@link SupersededError}, so the caller never holds an
61
+ * authorization the client does not sign with.
67
62
  */
68
63
  const begin = () => {
69
64
  const started = version;
70
- // A sign-in that finishes after sign-out may have set a new server session, so it ends that
71
- // session again, unless a newer change already followed the sign-out.
72
- const superseded = async () => {
73
- if (signedOut === version)
74
- await Actions.signOut(auth);
75
- return new SupersededError();
76
- };
77
- return {
78
- async apply(account) {
79
- if (version !== started) {
80
- if (account)
81
- throw await superseded();
82
- return;
83
- }
84
- state.account = account;
85
- version++;
86
- },
87
- async check() {
88
- if (version !== started)
89
- throw await superseded();
90
- },
91
- // An arrow function, which `catch` takes unbound.
92
- fail: async (error) => {
93
- if (version !== started)
94
- throw await superseded();
95
- throw error;
96
- },
65
+ return (account) => {
66
+ if (version !== started) {
67
+ if (account)
68
+ throw new SupersededError();
69
+ return;
70
+ }
71
+ state.account = account;
72
+ version++;
97
73
  };
98
74
  };
99
75
  return {
100
76
  auth: {
101
77
  async authorize(options = {}) {
102
- const change = begin();
78
+ const apply = begin();
103
79
  const authorization = await Actions.authorize(auth, options);
104
80
  // Without a session, the client stops signing as the user it last authorized.
105
- await change.apply(authorization?.account);
81
+ apply(authorization?.account);
106
82
  return authorization;
107
83
  },
108
84
  device: {
109
85
  async authorize(options = {}) {
110
- const change = begin();
86
+ const apply = begin();
111
87
  const device = await Actions.device.authorize(auth, options);
112
88
  return {
113
89
  ...device,
114
90
  async wait() {
115
91
  const authorization = await device.wait();
116
- await change.apply(authorization.account);
92
+ apply(authorization.account);
117
93
  return authorization;
118
94
  },
119
95
  };
@@ -122,14 +98,10 @@ export function tempoAuth(options) {
122
98
  email: {
123
99
  sendCode: (options) => Actions.email.sendCode(auth, options),
124
100
  async signIn(options) {
125
- const change = begin();
126
- const result = await Actions.email
127
- .signIn(auth, options)
128
- .catch(change.fail);
129
- if ('step' in result)
130
- await change.check();
131
- else
132
- await change.apply(result.account);
101
+ const apply = begin();
102
+ const result = await Actions.email.signIn(auth, options);
103
+ if (!('step' in result))
104
+ apply(result.account);
133
105
  return result;
134
106
  },
135
107
  },
@@ -139,37 +111,24 @@ export function tempoAuth(options) {
139
111
  jwks: () => Actions.oauth.jwks(auth),
140
112
  userInfo: (options) => Actions.oauth.userInfo(auth, options),
141
113
  },
142
- passkey: {
143
- async signIn(options = {}) {
144
- const change = begin();
145
- const result = await Actions.passkey
146
- .signIn(auth, options)
147
- .catch(change.fail);
148
- if ('step' in result)
149
- await change.check();
150
- else
151
- await change.apply(result.account);
152
- return result;
153
- },
154
- },
155
114
  provider: {
156
115
  list: () => Actions.provider.list(auth),
157
116
  signIn: (options) => Actions.provider.signIn(auth, options),
158
117
  },
159
118
  redirect: {
160
119
  async complete(options = {}) {
161
- const change = begin();
120
+ const apply = begin();
162
121
  const authorization = await Actions.redirect.complete(auth, options);
163
122
  // `undefined` means the page is not a callback, which says nothing about the session.
164
123
  if (authorization)
165
- await change.apply(authorization.account);
124
+ apply(authorization.account);
166
125
  return authorization;
167
126
  },
168
127
  createUrl: (options = {}) => Actions.redirect.createUrl(auth, options),
169
128
  async handleCallback(options) {
170
- const change = begin();
129
+ const apply = begin();
171
130
  const authorization = await Actions.redirect.handleCallback(auth, options);
172
- await change.apply(authorization.account);
131
+ apply(authorization.account);
173
132
  return authorization;
174
133
  },
175
134
  start: (options = {}) => Actions.redirect.start(auth, options),
@@ -179,8 +138,7 @@ export function tempoAuth(options) {
179
138
  // A sign-in stored before `Key.chainId` existed gains it here.
180
139
  const key = Key.from(authorization.key);
181
140
  const account = Key.toAccount(key, options);
182
- state.account = account;
183
- version++;
141
+ begin()(account);
184
142
  return { ...authorization, account, key };
185
143
  },
186
144
  async signOut() {
@@ -188,7 +146,6 @@ export function tempoAuth(options) {
188
146
  // pending is discarded, since the user asked to leave.
189
147
  state.account = undefined;
190
148
  version++;
191
- signedOut = version;
192
149
  await Actions.signOut(auth);
193
150
  },
194
151
  tempoAccount: {
@@ -44,11 +44,6 @@ export declare namespace signIn {
44
44
  code: string;
45
45
  /** The address the code was sent to. */
46
46
  email: string;
47
- /**
48
- * The `link` from `Actions.passkey.signIn`'s email step, which links that wallet.tempo passkey
49
- * to the user.
50
- */
51
- link?: string | undefined;
52
47
  /** What the key may do. It must fit the headless ceiling, which fills what it leaves out. */
53
48
  permissions?: Permissions<chainId> | undefined;
54
49
  };
@@ -23,14 +23,13 @@ export async function sendCode(auth, options) {
23
23
  * ```
24
24
  */
25
25
  export async function signIn(auth, options) {
26
- const { code, email, link, permissions } = options;
26
+ const { code, email, permissions } = options;
27
27
  const key = await AccessKey.resolve(options.accessKey);
28
28
  const body = await Api.json(await Api.post(auth, '/v1/auth/code/verify', {
29
29
  json: {
30
30
  authorize: request(auth, { accessKey: key.accessKey, permissions }),
31
31
  code,
32
32
  email,
33
- ...(link ? { link } : {}),
34
33
  },
35
34
  }));
36
35
  if ('step' in body)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tempoxyz/auth",
3
- "version": "0.0.0-pr-71.sha-b89841e",
3
+ "version": "0.0.0-pr-68.sha-79216ef",
4
4
  "description": "Signs users in to Tempo through Tempo Auth, and returns Viem access key accounts on their Tempo Accounts.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -1,55 +0,0 @@
1
- /**
2
- * Signs the user in with a passkey from the app's own sign-in form. The browser offers every passkey
3
- * it holds for Tempo's domain, so the app needs no email first.
4
- * @module
5
- */
6
- import { Authentication } from 'ox/webauthn';
7
- import type { Account } from 'viem/tempo';
8
- import type { Auth } from '../Auth.ts';
9
- import type { Authorization } from '../Authorization.ts';
10
- import type { Permissions } from '../Permissions.ts';
11
- /**
12
- * Signs the user in with a passkey, and authorizes the app in the same call. Returns the email step
13
- * for a wallet.tempo passkey whose account has no email: the app signs the user in with
14
- * `Actions.email.signIn`, passing the step's `link`, which links the passkey. Throws a `RequestError` with reason
15
- * `webauthn_required` when a passkey owns the Tempo Account the key acts for, since a passkey signs a
16
- * key authorization only on Tempo Auth's pages. `Actions.redirect.start` authorizes it there, where
17
- * the user is already signed in.
18
- *
19
- * @example
20
- * ```ts
21
- * const result = await Actions.passkey.signIn(auth)
22
- * if ('step' in result) showEmailForm()
23
- * ```
24
- */
25
- export declare function signIn<const chainId extends number = number>(auth: Auth, options?: signIn.Options<chainId>): Promise<signIn.ReturnType<chainId>>;
26
- export declare namespace signIn {
27
- type Options<chainId extends number = number> = {
28
- /**
29
- * The key to authorize, as a Viem account. Its `access` names the Tempo Account, which
30
- * defaults to the user's first. Defaults to a new key.
31
- */
32
- accessKey?: Account.Account | undefined;
33
- /**
34
- * Runs the WebAuthn ceremony in place of `navigator.credentials.get`, such as a native passkey
35
- * module. Its assertion must report a web origin on the relying party's domain that the app
36
- * registered, so an Android app's facet origin is refused. A native app's session stays in its
37
- * own `fetch`, so after `webauthn_required` it authorizes the key through
38
- * `Actions.redirect.createUrl` in the system browser, where the user signs in again.
39
- */
40
- getFn?: Authentication.sign.Options['getFn'] | undefined;
41
- /** What the key may do. It must fit the headless ceiling, which fills what it leaves out. */
42
- permissions?: Permissions<chainId> | undefined;
43
- };
44
- /** The step a wallet.tempo passkey without an email needs: an emailed code, which links it. */
45
- type EmailStep = {
46
- /** Passed as `link` to `Actions.email.signIn`, which links the passkey to that user. */
47
- link: string;
48
- /** The kind of step. */
49
- type: 'email';
50
- };
51
- /** The authorization, or the email step. */
52
- type ReturnType<chainId extends number = number> = Authorization<chainId> | {
53
- step: EmailStep;
54
- };
55
- }
@@ -1,41 +0,0 @@
1
- /**
2
- * Signs the user in with a passkey from the app's own sign-in form. The browser offers every passkey
3
- * it holds for Tempo's domain, so the app needs no email first.
4
- * @module
5
- */
6
- import { Authentication } from 'ox/webauthn';
7
- import * as AccessKey from '../internal/accessKey.js';
8
- import * as Api from '../internal/api.js';
9
- import { request } from '../internal/request.js';
10
- /**
11
- * Signs the user in with a passkey, and authorizes the app in the same call. Returns the email step
12
- * for a wallet.tempo passkey whose account has no email: the app signs the user in with
13
- * `Actions.email.signIn`, passing the step's `link`, which links the passkey. Throws a `RequestError` with reason
14
- * `webauthn_required` when a passkey owns the Tempo Account the key acts for, since a passkey signs a
15
- * key authorization only on Tempo Auth's pages. `Actions.redirect.start` authorizes it there, where
16
- * the user is already signed in.
17
- *
18
- * @example
19
- * ```ts
20
- * const result = await Actions.passkey.signIn(auth)
21
- * if ('step' in result) showEmailForm()
22
- * ```
23
- */
24
- export async function signIn(auth, options = {}) {
25
- const { getFn, permissions } = options;
26
- const key = await AccessKey.resolve(options.accessKey);
27
- const requested = await Api.json(await Api.post(auth, '/v1/auth/passkey/options', { json: {} }));
28
- const deserialized = Authentication.deserializeOptions(requested.options);
29
- const assertion = Authentication.serializeResponse(await Authentication.sign(
30
- // ox reads `getFn` beside request options at runtime, which its types admit only beside a challenge.
31
- getFn ? { ...deserialized, getFn } : deserialized));
32
- const body = await Api.json(await Api.post(auth, '/v1/auth/passkey/verify', {
33
- json: {
34
- assertion,
35
- authorize: request(auth, { accessKey: key.accessKey, permissions }),
36
- },
37
- }));
38
- if ('step' in body)
39
- return { step: body.step };
40
- return Api.authorization(body.authorization, key);
41
- }