@tempoxyz/auth 0.0.0-pr-87.sha-7bb5600 → 0.0.0-pr-73.sha-aff982f
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 +1 -6
- package/dist/Actions.js +0 -1
- package/dist/Auth.d.ts +2 -3
- package/dist/Step.d.ts +1 -13
- package/dist/actions/device.d.ts +1 -1
- package/dist/actions/email.d.ts +5 -10
- package/dist/actions/email.js +4 -24
- package/dist/actions/passkey.d.ts +11 -37
- package/dist/actions/passkey.js +19 -44
- package/dist/actions/redirect.d.ts +1 -1
- package/dist/actions/redirect.js +10 -4
- package/dist/actions/tempoAccount.d.ts +1 -2
- package/dist/actions/tempoAccount.js +10 -4
- package/dist/internal/accessKey.d.ts +0 -5
- package/dist/internal/accessKey.js +0 -11
- package/dist/internal/api.d.ts +0 -22
- package/dist/internal/api.js +0 -7
- package/dist/internal/request.d.ts +0 -2
- package/dist/internal/request.js +1 -3
- package/package.json +1 -1
- package/dist/internal/link.d.ts +0 -38
- package/dist/internal/link.js +0 -44
- package/dist/internal/store.d.ts +0 -11
- package/dist/internal/store.js +0 -14
package/dist/Actions.d.ts
CHANGED
|
@@ -32,14 +32,9 @@ export declare namespace authorize {
|
|
|
32
32
|
type Options<chainId extends number = number> = {
|
|
33
33
|
/**
|
|
34
34
|
* The key to authorize, as a Viem account. Its `access` names the Tempo Account, which
|
|
35
|
-
* defaults to the
|
|
35
|
+
* defaults to the user's first. Defaults to a new key.
|
|
36
36
|
*/
|
|
37
37
|
accessKey?: Account.Account | undefined;
|
|
38
|
-
/**
|
|
39
|
-
* The address of the user's Tempo Account the key acts for, such as the one the user chose from
|
|
40
|
-
* a sign-in's account step. Takes the place of `accessKey`'s `access`.
|
|
41
|
-
*/
|
|
42
|
-
account?: `0x${string}` | undefined;
|
|
43
38
|
/**
|
|
44
39
|
* A message the owner's signature binds, such as a sign-in challenge the app's backend issued.
|
|
45
40
|
* Tempo Auth binds its EIP-191 hash as the key authorization's `witness`. Only an app with the
|
package/dist/Actions.js
CHANGED
|
@@ -30,7 +30,6 @@ export async function authorize(auth, options = {}) {
|
|
|
30
30
|
const response = await Api.post(auth, '/v1/authorize', {
|
|
31
31
|
json: request(auth, {
|
|
32
32
|
accessKey: key.accessKey,
|
|
33
|
-
account: options.account,
|
|
34
33
|
challenge: options.challenge,
|
|
35
34
|
permissions: options.permissions,
|
|
36
35
|
}),
|
package/dist/Auth.d.ts
CHANGED
|
@@ -51,9 +51,8 @@ export type Auth = {
|
|
|
51
51
|
/** The scopes the app's tokens carry. */
|
|
52
52
|
scope: string;
|
|
53
53
|
/**
|
|
54
|
-
* Where `Actions.redirect.start` keeps its state until Tempo Auth redirects back
|
|
55
|
-
*
|
|
56
|
-
* are, since a browser key is a `CryptoKey`. Defaults to IndexedDB.
|
|
54
|
+
* Where `Actions.redirect.start` keeps its state until Tempo Auth redirects back. It must keep
|
|
55
|
+
* objects as they are, since a browser key is a `CryptoKey`. Defaults to IndexedDB.
|
|
57
56
|
*/
|
|
58
57
|
storage?: Storage | undefined;
|
|
59
58
|
/** Where Tempo Auth runs, such as `https://auth.tempo.xyz`. */
|
package/dist/Step.d.ts
CHANGED
|
@@ -2,20 +2,8 @@
|
|
|
2
2
|
* A step a sign-in needs before it finishes, such as the SSO connection an address requires.
|
|
3
3
|
* @module
|
|
4
4
|
*/
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* A step a sign-in needs before it finishes: the SSO connection an address requires, or the choice
|
|
8
|
-
* of account for a user with more than one Tempo Account.
|
|
9
|
-
*/
|
|
5
|
+
/** A step a sign-in needs before it finishes, such as the SSO connection an address requires. */
|
|
10
6
|
export type Step = {
|
|
11
|
-
/** The user's Tempo Accounts: the one the browser selected before first, then oldest first. */
|
|
12
|
-
accounts: readonly TempoAccount[];
|
|
13
|
-
/**
|
|
14
|
-
* The kind of step. The user is signed in, and chooses an account, whose `address`
|
|
15
|
-
* `Actions.authorize` takes as `account`.
|
|
16
|
-
*/
|
|
17
|
-
type: 'account';
|
|
18
|
-
} | {
|
|
19
7
|
/** The kind of step. */
|
|
20
8
|
type: 'sso';
|
|
21
9
|
/** Where `Actions.provider.signIn` sends the browser to take the step. */
|
package/dist/actions/device.d.ts
CHANGED
|
@@ -18,7 +18,7 @@ export declare namespace authorize {
|
|
|
18
18
|
type Options<chainId extends number = number> = {
|
|
19
19
|
/**
|
|
20
20
|
* The key to authorize, as a Viem account. Its `access` names the Tempo Account, which
|
|
21
|
-
* defaults to the
|
|
21
|
+
* defaults to the user's first. Defaults to a new key.
|
|
22
22
|
*/
|
|
23
23
|
accessKey?: Account.Account | undefined;
|
|
24
24
|
/**
|
package/dist/actions/email.d.ts
CHANGED
|
@@ -24,15 +24,12 @@ export declare namespace sendCode {
|
|
|
24
24
|
}
|
|
25
25
|
/**
|
|
26
26
|
* Signs the user in with the code they were emailed, and authorizes the app in the same call.
|
|
27
|
-
* Returns a {@link Step} when the address requires SSO, which `Actions.provider.signIn` continues
|
|
28
|
-
* or when the user has more than one Tempo Account, whose chosen `address` `Actions.authorize` takes
|
|
29
|
-
* as `account`.
|
|
27
|
+
* Returns a {@link Step} when the address requires SSO, which `Actions.provider.signIn` continues.
|
|
30
28
|
*
|
|
31
29
|
* @example
|
|
32
30
|
* ```ts
|
|
33
31
|
* const result = await Actions.email.signIn(auth, { code, email })
|
|
34
|
-
* if ('step' in result
|
|
35
|
-
* Actions.provider.signIn(auth, { url: result.step.url })
|
|
32
|
+
* if ('step' in result) Actions.provider.signIn(auth, { url: result.step.url })
|
|
36
33
|
* ```
|
|
37
34
|
*/
|
|
38
35
|
export declare function signIn<const chainId extends number = number>(auth: Auth, options: signIn.Options<chainId>): Promise<signIn.ReturnType<chainId>>;
|
|
@@ -40,7 +37,7 @@ export declare namespace signIn {
|
|
|
40
37
|
type Options<chainId extends number = number> = {
|
|
41
38
|
/**
|
|
42
39
|
* The key to authorize, as a Viem account. Its `access` names the Tempo Account, which
|
|
43
|
-
* defaults to the
|
|
40
|
+
* defaults to the user's first. Defaults to a new key.
|
|
44
41
|
*/
|
|
45
42
|
accessKey?: Account.Account | undefined;
|
|
46
43
|
/**
|
|
@@ -55,15 +52,13 @@ export declare namespace signIn {
|
|
|
55
52
|
email: string;
|
|
56
53
|
/**
|
|
57
54
|
* The `link` from `Actions.passkey.signIn`'s email step, which links that wallet.tempo passkey
|
|
58
|
-
* to the user.
|
|
59
|
-
* authorized with its assertion, given the same `accessKey`, `challenge`, and `permissions`.
|
|
60
|
-
* Another `accessKey` replaces the key that sign-in generated.
|
|
55
|
+
* to the user.
|
|
61
56
|
*/
|
|
62
57
|
link?: string | undefined;
|
|
63
58
|
/** What the key may do. It must fit the headless ceiling, which fills what it leaves out. */
|
|
64
59
|
permissions?: Permissions<chainId> | undefined;
|
|
65
60
|
};
|
|
66
|
-
/** The authorization, or the step the address
|
|
61
|
+
/** The authorization, or the step the address requires first. */
|
|
67
62
|
type ReturnType<chainId extends number = number> = Authorization<chainId> | {
|
|
68
63
|
step: Step;
|
|
69
64
|
};
|
package/dist/actions/email.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import * as AccessKey from '../internal/accessKey.js';
|
|
2
2
|
import * as Api from '../internal/api.js';
|
|
3
|
-
import * as Link from '../internal/link.js';
|
|
4
3
|
import { request } from '../internal/request.js';
|
|
5
4
|
/**
|
|
6
5
|
* Emails the user a sign-in code. The response is the same for every address.
|
|
@@ -15,23 +14,17 @@ export async function sendCode(auth, options) {
|
|
|
15
14
|
}
|
|
16
15
|
/**
|
|
17
16
|
* Signs the user in with the code they were emailed, and authorizes the app in the same call.
|
|
18
|
-
* Returns a {@link Step} when the address requires SSO, which `Actions.provider.signIn` continues
|
|
19
|
-
* or when the user has more than one Tempo Account, whose chosen `address` `Actions.authorize` takes
|
|
20
|
-
* as `account`.
|
|
17
|
+
* Returns a {@link Step} when the address requires SSO, which `Actions.provider.signIn` continues.
|
|
21
18
|
*
|
|
22
19
|
* @example
|
|
23
20
|
* ```ts
|
|
24
21
|
* const result = await Actions.email.signIn(auth, { code, email })
|
|
25
|
-
* if ('step' in result
|
|
26
|
-
* Actions.provider.signIn(auth, { url: result.step.url })
|
|
22
|
+
* if ('step' in result) Actions.provider.signIn(auth, { url: result.step.url })
|
|
27
23
|
* ```
|
|
28
24
|
*/
|
|
29
25
|
export async function signIn(auth, options) {
|
|
30
26
|
const { challenge, code, email, link, permissions } = options;
|
|
31
|
-
|
|
32
|
-
// returns once this request names it again. A key the app supplies replaces it.
|
|
33
|
-
const kept = link && !options.accessKey ? await Link.get(auth, { link }) : undefined;
|
|
34
|
-
const key = kept ?? (await AccessKey.resolve(options.accessKey));
|
|
27
|
+
const key = await AccessKey.resolve(options.accessKey);
|
|
35
28
|
const body = await Api.json(await Api.post(auth, '/v1/auth/code/verify', {
|
|
36
29
|
json: {
|
|
37
30
|
authorize: request(auth, {
|
|
@@ -44,22 +37,9 @@ export async function signIn(auth, options) {
|
|
|
44
37
|
...(link ? { link } : {}),
|
|
45
38
|
},
|
|
46
39
|
}));
|
|
47
|
-
|
|
48
|
-
// the parked request, so a failed removal keeps the stale key, which the next email step replaces,
|
|
49
|
-
// rather than discard the authorization.
|
|
50
|
-
if (kept && link)
|
|
51
|
-
await Link.release(auth, { link }).catch(() => undefined);
|
|
52
|
-
if ('step' in body) {
|
|
53
|
-
if (body.step.type === 'account')
|
|
54
|
-
return {
|
|
55
|
-
step: {
|
|
56
|
-
accounts: body.step.accounts.map(Api.tempoAccount),
|
|
57
|
-
type: 'account',
|
|
58
|
-
},
|
|
59
|
-
};
|
|
40
|
+
if ('step' in body)
|
|
60
41
|
return {
|
|
61
42
|
step: { ...body.step, url: new URL(body.step.url, auth.url).href },
|
|
62
43
|
};
|
|
63
|
-
}
|
|
64
44
|
return Api.authorization(body.authorization, key);
|
|
65
45
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Signs the user in with a passkey from the app's own sign-in form
|
|
3
|
-
*
|
|
4
|
-
* app needs no email first. A non-empty `credentialId` narrows that to the passkeys it names.
|
|
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.
|
|
5
4
|
* @module
|
|
6
5
|
*/
|
|
7
6
|
import { Authentication } from 'ox/webauthn';
|
|
@@ -9,19 +8,14 @@ import type { Account } from 'viem/tempo';
|
|
|
9
8
|
import type { Auth } from '../Auth.ts';
|
|
10
9
|
import type { Authorization } from '../Authorization.ts';
|
|
11
10
|
import type { Permissions } from '../Permissions.ts';
|
|
12
|
-
import type { Step } from '../Step.ts';
|
|
13
11
|
/**
|
|
14
|
-
* Signs the user in with a passkey, and authorizes the app in the same call.
|
|
15
|
-
*
|
|
16
|
-
* the ceremony's challenge is the key's digest. A native app, which sends no `Origin`, signs in
|
|
17
|
-
* with a random challenge and authorizes as a headless sign-in does. Returns the email step for a
|
|
18
|
-
* wallet.tempo passkey whose account has no email: the app signs the user in with
|
|
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
|
|
19
14
|
* `Actions.email.signIn`, passing the step's `link`, which links the passkey, and the same
|
|
20
|
-
* `accessKey`, `challenge`, and `permissions
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* the
|
|
24
|
-
* for the signed-in user.
|
|
15
|
+
* `accessKey`, `challenge`, and `permissions`, which this call does not authorize. Throws a `RequestError` with reason
|
|
16
|
+
* `webauthn_required` when a passkey owns the Tempo Account the key acts for, since a passkey signs a
|
|
17
|
+
* key authorization only on Tempo Auth's pages. `Actions.redirect.start` authorizes it there, where
|
|
18
|
+
* the user is already signed in.
|
|
25
19
|
*
|
|
26
20
|
* @example
|
|
27
21
|
* ```ts
|
|
@@ -34,7 +28,7 @@ export declare namespace signIn {
|
|
|
34
28
|
type Options<chainId extends number = number> = {
|
|
35
29
|
/**
|
|
36
30
|
* The key to authorize, as a Viem account. Its `access` names the Tempo Account, which
|
|
37
|
-
* defaults to the
|
|
31
|
+
* defaults to the user's first. Defaults to a new key.
|
|
38
32
|
*/
|
|
39
33
|
accessKey?: Account.Account | undefined;
|
|
40
34
|
/**
|
|
@@ -43,13 +37,6 @@ export declare namespace signIn {
|
|
|
43
37
|
* `tempo-auth:sign` scope may send one.
|
|
44
38
|
*/
|
|
45
39
|
challenge?: string | undefined;
|
|
46
|
-
/**
|
|
47
|
-
* The credential ID, or up to 16 IDs, the browser offers. Each is unpadded base64url, as
|
|
48
|
-
* `PublicKeyCredential.id` reports it, of 1 to 1,023 bytes. A padded or otherwise non-canonical
|
|
49
|
-
* encoding throws a `RequestError` with reason `invalid_field`. Defaults to every passkey the
|
|
50
|
-
* browser holds for Tempo's domain, as an empty list does.
|
|
51
|
-
*/
|
|
52
|
-
credentialId?: readonly string[] | string | undefined;
|
|
53
40
|
/**
|
|
54
41
|
* Runs the WebAuthn ceremony in place of `navigator.credentials.get`, such as a native passkey
|
|
55
42
|
* module. Its assertion must report a web origin on the relying party's domain that the app
|
|
@@ -58,8 +45,6 @@ export declare namespace signIn {
|
|
|
58
45
|
* `Actions.redirect.createUrl` in the system browser, where the user signs in again.
|
|
59
46
|
*/
|
|
60
47
|
getFn?: Authentication.sign.Options['getFn'] | undefined;
|
|
61
|
-
/** How the browser mediates the ceremony, such as `conditional` for passkey autofill. Defaults to the browser's own mediation. */
|
|
62
|
-
mediation?: 'conditional' | 'optional' | 'required' | 'silent' | undefined;
|
|
63
48
|
/** What the key may do. It must fit the headless ceiling, which fills what it leaves out. */
|
|
64
49
|
permissions?: Permissions<chainId> | undefined;
|
|
65
50
|
};
|
|
@@ -67,22 +52,11 @@ export declare namespace signIn {
|
|
|
67
52
|
type EmailStep = {
|
|
68
53
|
/** Passed as `link` to `Actions.email.signIn`, which links the passkey to that user. */
|
|
69
54
|
link: string;
|
|
70
|
-
/**
|
|
71
|
-
* Whether the assertion already signed the key, which `Actions.email.signIn` returns with
|
|
72
|
-
* `link`. Otherwise that call authorizes the key itself.
|
|
73
|
-
*/
|
|
74
|
-
signed: boolean;
|
|
75
55
|
/** The kind of step. */
|
|
76
56
|
type: 'email';
|
|
77
57
|
};
|
|
78
|
-
/**
|
|
79
|
-
* The authorization, the email step, or the account step of a user with more than one Tempo
|
|
80
|
-
* Account. A passkey that owns one of the user's accounts selects it, so only a passkey that owns
|
|
81
|
-
* none returns the account step.
|
|
82
|
-
*/
|
|
58
|
+
/** The authorization, or the email step. */
|
|
83
59
|
type ReturnType<chainId extends number = number> = Authorization<chainId> | {
|
|
84
|
-
step: EmailStep
|
|
85
|
-
type: 'account';
|
|
86
|
-
}>;
|
|
60
|
+
step: EmailStep;
|
|
87
61
|
};
|
|
88
62
|
}
|
package/dist/actions/passkey.js
CHANGED
|
@@ -1,26 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Signs the user in with a passkey from the app's own sign-in form
|
|
3
|
-
*
|
|
4
|
-
* app needs no email first. A non-empty `credentialId` narrows that to the passkeys it names.
|
|
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.
|
|
5
4
|
* @module
|
|
6
5
|
*/
|
|
7
6
|
import { Authentication } from 'ox/webauthn';
|
|
8
7
|
import * as AccessKey from '../internal/accessKey.js';
|
|
9
8
|
import * as Api from '../internal/api.js';
|
|
10
|
-
import * as Link from '../internal/link.js';
|
|
11
9
|
import { request } from '../internal/request.js';
|
|
12
10
|
/**
|
|
13
|
-
* Signs the user in with a passkey, and authorizes the app in the same call.
|
|
14
|
-
*
|
|
15
|
-
* the ceremony's challenge is the key's digest. A native app, which sends no `Origin`, signs in
|
|
16
|
-
* with a random challenge and authorizes as a headless sign-in does. Returns the email step for a
|
|
17
|
-
* wallet.tempo passkey whose account has no email: the app signs the user in with
|
|
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
|
|
18
13
|
* `Actions.email.signIn`, passing the step's `link`, which links the passkey, and the same
|
|
19
|
-
* `accessKey`, `challenge`, and `permissions
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* the
|
|
23
|
-
* for the signed-in user.
|
|
14
|
+
* `accessKey`, `challenge`, and `permissions`, which this call does not authorize. 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.
|
|
24
18
|
*
|
|
25
19
|
* @example
|
|
26
20
|
* ```ts
|
|
@@ -29,43 +23,24 @@ import { request } from '../internal/request.js';
|
|
|
29
23
|
* ```
|
|
30
24
|
*/
|
|
31
25
|
export async function signIn(auth, options = {}) {
|
|
32
|
-
const { challenge,
|
|
26
|
+
const { challenge, getFn, permissions } = options;
|
|
33
27
|
const key = await AccessKey.resolve(options.accessKey);
|
|
34
|
-
const
|
|
35
|
-
accessKey: key.accessKey,
|
|
36
|
-
challenge,
|
|
37
|
-
permissions,
|
|
38
|
-
});
|
|
39
|
-
// A page sends the request with the options, so the challenge is its key's digest. A native app
|
|
40
|
-
// sends no `Origin` to bind one-tap to, so it sends the request with the assertion instead.
|
|
41
|
-
const native = !/^https?:/i.test(authorize.redirect_uri);
|
|
42
|
-
const requested = await Api.json(await Api.post(auth, '/v1/auth/passkey/options', {
|
|
43
|
-
json: {
|
|
44
|
-
...(native ? {} : { authorize }),
|
|
45
|
-
...(credentialId === undefined ? {} : { credentialId }),
|
|
46
|
-
...(mediation === undefined ? {} : { mediation }),
|
|
47
|
-
},
|
|
48
|
-
}));
|
|
28
|
+
const requested = await Api.json(await Api.post(auth, '/v1/auth/passkey/options', { json: {} }));
|
|
49
29
|
const deserialized = Authentication.deserializeOptions(requested.options);
|
|
50
30
|
const assertion = Authentication.serializeResponse(await Authentication.sign(
|
|
51
31
|
// ox reads `getFn` beside request options at runtime, which its types admit only beside a challenge.
|
|
52
32
|
getFn ? { ...deserialized, getFn } : deserialized));
|
|
53
33
|
const body = await Api.json(await Api.post(auth, '/v1/auth/passkey/verify', {
|
|
54
|
-
json:
|
|
34
|
+
json: {
|
|
35
|
+
assertion,
|
|
36
|
+
authorize: request(auth, {
|
|
37
|
+
accessKey: key.accessKey,
|
|
38
|
+
challenge,
|
|
39
|
+
permissions,
|
|
40
|
+
}),
|
|
41
|
+
},
|
|
55
42
|
}));
|
|
56
|
-
if ('step' in body)
|
|
57
|
-
if (body.step.type === 'account')
|
|
58
|
-
return {
|
|
59
|
-
step: {
|
|
60
|
-
accounts: body.step.accounts.map(Api.tempoAccount),
|
|
61
|
-
type: 'account',
|
|
62
|
-
},
|
|
63
|
-
};
|
|
64
|
-
// The emailed-code sign-in that sends the link returns this key's authorization, so a key the
|
|
65
|
-
// library generated waits for it in `storage`.
|
|
66
|
-
if (body.step.signed && !options.accessKey)
|
|
67
|
-
await Link.keep(auth, { link: body.step.link, material: key.material });
|
|
43
|
+
if ('step' in body)
|
|
68
44
|
return { step: body.step };
|
|
69
|
-
}
|
|
70
45
|
return Api.authorization(body.authorization, key);
|
|
71
46
|
}
|
|
@@ -42,7 +42,7 @@ export declare namespace createUrl {
|
|
|
42
42
|
type Options = {
|
|
43
43
|
/**
|
|
44
44
|
* The key to authorize, as a Viem account. Its `access` names the Tempo Account, which
|
|
45
|
-
* defaults to the
|
|
45
|
+
* defaults to the user's first. Defaults to a new key, whose material `pending` keeps.
|
|
46
46
|
*/
|
|
47
47
|
accessKey?: Account.Account | undefined;
|
|
48
48
|
/**
|
package/dist/actions/redirect.js
CHANGED
|
@@ -8,8 +8,8 @@ import { Base64, Bytes, Hash } from 'ox';
|
|
|
8
8
|
import { RequestError } from '../Auth.js';
|
|
9
9
|
import * as AccessKey from '../internal/accessKey.js';
|
|
10
10
|
import * as Api from '../internal/api.js';
|
|
11
|
+
import * as IndexedDb from '../internal/indexedDb.js';
|
|
11
12
|
import { request } from '../internal/request.js';
|
|
12
|
-
import * as Store from '../internal/store.js';
|
|
13
13
|
import * as Pending from '../Pending.js';
|
|
14
14
|
/**
|
|
15
15
|
* Finishes a {@link start} when the page loads with Tempo Auth's callback, and returns `undefined`
|
|
@@ -24,8 +24,8 @@ import * as Pending from '../Pending.js';
|
|
|
24
24
|
*/
|
|
25
25
|
export function complete(auth, options = {}) {
|
|
26
26
|
completed ??= (async () => {
|
|
27
|
-
const stored = (await
|
|
28
|
-
await
|
|
27
|
+
const stored = (await storage(auth).getItem(storageKey));
|
|
28
|
+
await storage(auth).removeItem(storageKey);
|
|
29
29
|
const url = new URL(window.location.href);
|
|
30
30
|
if (!url.searchParams.has('code'))
|
|
31
31
|
return undefined;
|
|
@@ -148,12 +148,18 @@ export async function handleCallback(auth, options) {
|
|
|
148
148
|
*/
|
|
149
149
|
export async function start(auth, options = {}) {
|
|
150
150
|
const { pending, url } = await createUrl(auth, options);
|
|
151
|
-
await
|
|
151
|
+
await storage(auth).setItem(storageKey, pending);
|
|
152
152
|
window.location.assign(url);
|
|
153
153
|
}
|
|
154
154
|
/** Unpadded URL-safe Base64, as PKCE and `key_authorization` use. */
|
|
155
155
|
const base64url = { pad: false, url: true };
|
|
156
156
|
/** The redirect this page load returns from, so its code is redeemed once. */
|
|
157
157
|
let completed;
|
|
158
|
+
/** The default `storage`, opened on first use. */
|
|
159
|
+
let indexedDb;
|
|
158
160
|
/** Where a redirect keeps its state in `storage`. */
|
|
159
161
|
const storageKey = 'tempo-auth:redirect';
|
|
162
|
+
/** Where a redirect keeps its state: the instance's `storage`, or IndexedDB. */
|
|
163
|
+
function storage(auth) {
|
|
164
|
+
return auth.storage ?? (indexedDb ??= IndexedDb.create());
|
|
165
|
+
}
|
|
@@ -22,8 +22,7 @@ export declare namespace create {
|
|
|
22
22
|
type ReturnType = TempoAccount;
|
|
23
23
|
}
|
|
24
24
|
/**
|
|
25
|
-
* Lists the signed-in user's Tempo Accounts, their default first
|
|
26
|
-
* or else their oldest.
|
|
25
|
+
* Lists the signed-in user's Tempo Accounts, their default first.
|
|
27
26
|
*
|
|
28
27
|
* @example
|
|
29
28
|
* ```ts
|
|
@@ -12,11 +12,10 @@ import * as Api from '../internal/api.js';
|
|
|
12
12
|
* ```
|
|
13
13
|
*/
|
|
14
14
|
export async function create(auth) {
|
|
15
|
-
return
|
|
15
|
+
return toTempoAccount(await Api.json(await Api.post(auth, '/v1/account/tempo', { json: {} })));
|
|
16
16
|
}
|
|
17
17
|
/**
|
|
18
|
-
* Lists the signed-in user's Tempo Accounts, their default first
|
|
19
|
-
* or else their oldest.
|
|
18
|
+
* Lists the signed-in user's Tempo Accounts, their default first.
|
|
20
19
|
*
|
|
21
20
|
* @example
|
|
22
21
|
* ```ts
|
|
@@ -27,5 +26,12 @@ export async function list(auth) {
|
|
|
27
26
|
const { tempo } = await Api.json(await (auth.fetch ?? fetch)(new URL('/v1/account', auth.url), {
|
|
28
27
|
credentials: 'include',
|
|
29
28
|
}));
|
|
30
|
-
return tempo.map(
|
|
29
|
+
return tempo.map(toTempoAccount);
|
|
30
|
+
}
|
|
31
|
+
/** Reads the account's address and the type of its owner, which the API lists first. */
|
|
32
|
+
function toTempoAccount(account) {
|
|
33
|
+
return {
|
|
34
|
+
address: account.address,
|
|
35
|
+
owner: account.config.owners[0].type,
|
|
36
|
+
};
|
|
31
37
|
}
|
|
@@ -5,11 +5,6 @@ import type * as Key from '../Key.ts';
|
|
|
5
5
|
* a `CryptoKey` across page loads. Elsewhere a CLI or native app must store the key as a string.
|
|
6
6
|
*/
|
|
7
7
|
export declare function resolve(accessKey: Account.Account | undefined): Promise<resolve.ReturnType>;
|
|
8
|
-
/**
|
|
9
|
-
* Rebuilds a key that {@link resolve} generated from its material. Returns `undefined` for the
|
|
10
|
-
* empty material of a key the app supplied.
|
|
11
|
-
*/
|
|
12
|
-
export declare function restore(material: Key.Material): resolve.ReturnType | undefined;
|
|
13
8
|
export declare namespace resolve {
|
|
14
9
|
type ReturnType = {
|
|
15
10
|
/** The key, as a Viem account that signs with it. */
|
|
@@ -24,14 +24,3 @@ export async function resolve(accessKey) {
|
|
|
24
24
|
material: { privateKey },
|
|
25
25
|
};
|
|
26
26
|
}
|
|
27
|
-
/**
|
|
28
|
-
* Rebuilds a key that {@link resolve} generated from its material. Returns `undefined` for the
|
|
29
|
-
* empty material of a key the app supplied.
|
|
30
|
-
*/
|
|
31
|
-
export function restore(material) {
|
|
32
|
-
if (material.keyPair)
|
|
33
|
-
return { accessKey: Account.fromWebCryptoP256(material.keyPair), material };
|
|
34
|
-
if (material.privateKey)
|
|
35
|
-
return { accessKey: Account.fromSecp256k1(material.privateKey), material };
|
|
36
|
-
return undefined;
|
|
37
|
-
}
|
package/dist/internal/api.d.ts
CHANGED
|
@@ -3,7 +3,6 @@ import type { Account } from 'viem/tempo';
|
|
|
3
3
|
import { type Auth, RequestError } from '../Auth.ts';
|
|
4
4
|
import type { Authorization } from '../Authorization.ts';
|
|
5
5
|
import * as Key from '../Key.ts';
|
|
6
|
-
import type { TempoAccount } from '../TempoAccount.ts';
|
|
7
6
|
import type { Tokens } from '../Tokens.ts';
|
|
8
7
|
/**
|
|
9
8
|
* Builds an authorization from what Tempo Auth returned and the sign-in's key: the material the
|
|
@@ -14,14 +13,6 @@ export declare function authorization<chainId extends number = number>(body: Hea
|
|
|
14
13
|
accessKey?: Account.Account | undefined;
|
|
15
14
|
material: Key.Material;
|
|
16
15
|
}): Authorization<chainId>;
|
|
17
|
-
/**
|
|
18
|
-
* A sign-in's account step as the API returns it: the accounts of a user with more than one, who
|
|
19
|
-
* selects one before a key is authorized.
|
|
20
|
-
*/
|
|
21
|
-
export type AccountStep = {
|
|
22
|
-
accounts: readonly TempoAccountResponse[];
|
|
23
|
-
type: 'account';
|
|
24
|
-
};
|
|
25
16
|
/** What the headless API returns for an authorization. */
|
|
26
17
|
export type Headless = {
|
|
27
18
|
account: Key.Key['account'];
|
|
@@ -52,19 +43,6 @@ export declare function post(auth: Auth, path: string, options: {
|
|
|
52
43
|
* OAuth form posts to WorkOS.
|
|
53
44
|
*/
|
|
54
45
|
export declare function token(auth: Auth, body: Record<string, string>): Promise<TokenResponse>;
|
|
55
|
-
/** Reads the account's address and the type of its owner, which the API lists first. */
|
|
56
|
-
export declare function tempoAccount(account: TempoAccountResponse): TempoAccount;
|
|
57
|
-
/** A Tempo Account as the API returns it, with the fields the package reads. */
|
|
58
|
-
export type TempoAccountResponse = {
|
|
59
|
-
address: `0x${string}`;
|
|
60
|
-
config: {
|
|
61
|
-
owners: readonly [{
|
|
62
|
-
type: string;
|
|
63
|
-
}, ...{
|
|
64
|
-
type: string;
|
|
65
|
-
}[]];
|
|
66
|
-
};
|
|
67
|
-
};
|
|
68
46
|
/** What Tempo Auth's token endpoint returns. Redeeming a redirect's code adds its key authorization. */
|
|
69
47
|
export type TokenResponse = {
|
|
70
48
|
access_token: string;
|
package/dist/internal/api.js
CHANGED
|
@@ -71,13 +71,6 @@ export async function token(auth, body) {
|
|
|
71
71
|
method: 'POST',
|
|
72
72
|
}));
|
|
73
73
|
}
|
|
74
|
-
/** Reads the account's address and the type of its owner, which the API lists first. */
|
|
75
|
-
export function tempoAccount(account) {
|
|
76
|
-
return {
|
|
77
|
-
address: account.address,
|
|
78
|
-
owner: account.config.owners[0].type,
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
74
|
/** Reads a token response into `Tokens`. */
|
|
82
75
|
export function tokens(response) {
|
|
83
76
|
return {
|
|
@@ -7,8 +7,6 @@ import type { Permissions } from '../Permissions.ts';
|
|
|
7
7
|
*/
|
|
8
8
|
export declare function request(auth: Auth, options: {
|
|
9
9
|
accessKey: Account.Account;
|
|
10
|
-
/** The user's Tempo Account the key acts for, in place of the one `accessKey` names. */
|
|
11
|
-
account?: `0x${string}` | undefined;
|
|
12
10
|
/** A message the key's witness binds. */
|
|
13
11
|
challenge?: string | undefined;
|
|
14
12
|
permissions?: Permissions | undefined;
|
package/dist/internal/request.js
CHANGED
|
@@ -10,13 +10,11 @@ import { Hex } from 'ox';
|
|
|
10
10
|
*/
|
|
11
11
|
export function request(auth, options) {
|
|
12
12
|
const { accessKey, challenge, permissions = {} } = options;
|
|
13
|
-
const account = options.account ??
|
|
14
|
-
(accessKey.accessKeyAddress ? accessKey.address : undefined);
|
|
15
13
|
return {
|
|
16
14
|
...(challenge === undefined ? {} : { challenge }),
|
|
17
15
|
client_id: auth.clientId,
|
|
18
16
|
key_authorization: {
|
|
19
|
-
...(
|
|
17
|
+
...(accessKey.accessKeyAddress ? { account: accessKey.address } : {}),
|
|
20
18
|
address: accessKey.accessKeyAddress ?? accessKey.address,
|
|
21
19
|
chainId: Hex.fromNumber(permissions.chainId ?? auth.chainId),
|
|
22
20
|
expiry: permissions.expiry,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tempoxyz/auth",
|
|
3
|
-
"version": "0.0.0-pr-
|
|
3
|
+
"version": "0.0.0-pr-73.sha-aff982f",
|
|
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": {
|
package/dist/internal/link.d.ts
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import type { Auth } from '../Auth.ts';
|
|
2
|
-
import type * as Key from '../Key.ts';
|
|
3
|
-
import * as AccessKey from './accessKey.ts';
|
|
4
|
-
/**
|
|
5
|
-
* Keeps the key's material for `link`. Keeps nothing outside a browser without `storage`. Only the
|
|
6
|
-
* link's digest is stored, since the link links the passkey for whoever sends it with a code.
|
|
7
|
-
*/
|
|
8
|
-
export declare function keep(auth: Auth, options: keep.Options): Promise<void>;
|
|
9
|
-
export declare namespace keep {
|
|
10
|
-
type Options = {
|
|
11
|
-
/** The email step's `link`. */
|
|
12
|
-
link: string;
|
|
13
|
-
/** The key `AccessKey.resolve` generated. */
|
|
14
|
-
material: Key.Material;
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* Returns the key kept for `link`, or `undefined` when none is. Leaves it in place, so a refused
|
|
19
|
-
* code retries with the same key, until {@link release} removes it.
|
|
20
|
-
*/
|
|
21
|
-
export declare function get(auth: Auth, options: get.Options): Promise<AccessKey.resolve.ReturnType | undefined>;
|
|
22
|
-
export declare namespace get {
|
|
23
|
-
type Options = {
|
|
24
|
-
/** The `link` the emailed-code sign-in sends. */
|
|
25
|
-
link: string;
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
/**
|
|
29
|
-
* Removes the key kept for `link`, once the emailed-code sign-in that used it finished. A key a newer
|
|
30
|
-
* email step kept stays, so a sign-in that finishes late cannot remove it.
|
|
31
|
-
*/
|
|
32
|
-
export declare function release(auth: Auth, options: release.Options): Promise<void>;
|
|
33
|
-
export declare namespace release {
|
|
34
|
-
type Options = {
|
|
35
|
-
/** The `link` the finished sign-in sent. */
|
|
36
|
-
link: string;
|
|
37
|
-
};
|
|
38
|
-
}
|
package/dist/internal/link.js
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Keeps a new key that a one-tap passkey sign-in signed, until the emailed-code sign-in that sends
|
|
3
|
-
* its email step's `link` returns the key's authorization.
|
|
4
|
-
* @module
|
|
5
|
-
*/
|
|
6
|
-
import { Bytes, Hash } from 'ox';
|
|
7
|
-
import * as AccessKey from './accessKey.js';
|
|
8
|
-
import * as Store from './store.js';
|
|
9
|
-
/** Where the key waits in `storage`. A later email step replaces it. */
|
|
10
|
-
const storageKey = 'tempo-auth:passkey-link';
|
|
11
|
-
/**
|
|
12
|
-
* Keeps the key's material for `link`. Keeps nothing outside a browser without `storage`. Only the
|
|
13
|
-
* link's digest is stored, since the link links the passkey for whoever sends it with a code.
|
|
14
|
-
*/
|
|
15
|
-
export async function keep(auth, options) {
|
|
16
|
-
const kept = {
|
|
17
|
-
digest: digest(options.link),
|
|
18
|
-
material: options.material,
|
|
19
|
-
};
|
|
20
|
-
await Store.get(auth)?.setItem(storageKey, kept);
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* Returns the key kept for `link`, or `undefined` when none is. Leaves it in place, so a refused
|
|
24
|
-
* code retries with the same key, until {@link release} removes it.
|
|
25
|
-
*/
|
|
26
|
-
export async function get(auth, options) {
|
|
27
|
-
const kept = (await Store.get(auth)?.getItem(storageKey));
|
|
28
|
-
if (kept?.digest !== digest(options.link))
|
|
29
|
-
return undefined;
|
|
30
|
-
return AccessKey.restore(kept.material);
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* Removes the key kept for `link`, once the emailed-code sign-in that used it finished. A key a newer
|
|
34
|
-
* email step kept stays, so a sign-in that finishes late cannot remove it.
|
|
35
|
-
*/
|
|
36
|
-
export async function release(auth, options) {
|
|
37
|
-
const store = Store.get(auth);
|
|
38
|
-
const kept = (await store?.getItem(storageKey));
|
|
39
|
-
if (kept?.digest === digest(options.link))
|
|
40
|
-
await store?.removeItem(storageKey);
|
|
41
|
-
}
|
|
42
|
-
function digest(link) {
|
|
43
|
-
return Hash.sha256(Bytes.fromString(link), { as: 'Hex' });
|
|
44
|
-
}
|
package/dist/internal/store.d.ts
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Resolves where actions keep state across page loads: the instance's `storage`, or IndexedDB.
|
|
3
|
-
* @module
|
|
4
|
-
*/
|
|
5
|
-
import type { Auth } from '../Auth.ts';
|
|
6
|
-
import type { Storage } from '../Storage.ts';
|
|
7
|
-
/**
|
|
8
|
-
* Returns the instance's `storage`, or the page's IndexedDB. Returns `undefined` outside a browser
|
|
9
|
-
* when the instance names no `storage`.
|
|
10
|
-
*/
|
|
11
|
-
export declare function get(auth: Auth): Storage | undefined;
|
package/dist/internal/store.js
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import * as IndexedDb from './indexedDb.js';
|
|
2
|
-
/** The default `storage`, opened on first use. */
|
|
3
|
-
let indexedDb;
|
|
4
|
-
/**
|
|
5
|
-
* Returns the instance's `storage`, or the page's IndexedDB. Returns `undefined` outside a browser
|
|
6
|
-
* when the instance names no `storage`.
|
|
7
|
-
*/
|
|
8
|
-
export function get(auth) {
|
|
9
|
-
if (auth.storage)
|
|
10
|
-
return auth.storage;
|
|
11
|
-
if (typeof indexedDB === 'undefined')
|
|
12
|
-
return undefined;
|
|
13
|
-
return (indexedDb ??= IndexedDb.create());
|
|
14
|
-
}
|