@icp-sdk/auth 8.0.3 → 10.0.0
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/esm/client/app-delegation-source.d.ts +65 -0
- package/dist/esm/client/app-delegation-source.js +58 -0
- package/dist/esm/client/app-delegation-source.js.map +1 -0
- package/dist/esm/client/app-lock.d.ts +63 -0
- package/dist/esm/client/app-lock.js +92 -0
- package/dist/esm/client/app-lock.js.map +1 -0
- package/dist/esm/client/auth-client.d.ts +270 -53
- package/dist/esm/client/auth-client.js +1017 -414
- package/dist/esm/client/auth-client.js.map +1 -1
- package/dist/esm/client/base64.d.ts +8 -0
- package/dist/esm/client/base64.js +28 -0
- package/dist/esm/client/base64.js.map +1 -0
- package/dist/esm/client/cookie-state-storage.d.ts +59 -0
- package/dist/esm/client/cookie-state-storage.js +245 -0
- package/dist/esm/client/cookie-state-storage.js.map +1 -0
- package/dist/esm/client/credential-storage.d.ts +48 -0
- package/dist/esm/client/credential-storage.js +2 -0
- package/dist/esm/client/credential-storage.js.map +1 -0
- package/dist/esm/client/db.d.ts +6 -1
- package/dist/esm/client/db.js +56 -15
- package/dist/esm/client/db.js.map +1 -1
- package/dist/esm/client/foreground-refresh.d.ts +35 -0
- package/dist/esm/client/foreground-refresh.js +83 -0
- package/dist/esm/client/foreground-refresh.js.map +1 -0
- package/dist/esm/client/idb-credential-storage.d.ts +25 -0
- package/dist/esm/client/idb-credential-storage.js +69 -0
- package/dist/esm/client/idb-credential-storage.js.map +1 -0
- package/dist/esm/client/index.d.ts +14 -3
- package/dist/esm/client/index.js +13 -3
- package/dist/esm/client/index.js.map +1 -1
- package/dist/esm/client/local-credential-storage.d.ts +26 -0
- package/dist/esm/client/local-credential-storage.js +59 -0
- package/dist/esm/client/local-credential-storage.js.map +1 -0
- package/dist/esm/client/memory-credential-storage.d.ts +22 -0
- package/dist/esm/client/memory-credential-storage.js +35 -0
- package/dist/esm/client/memory-credential-storage.js.map +1 -0
- package/dist/esm/client/session-delegation.d.ts +40 -0
- package/dist/esm/client/session-delegation.js +100 -0
- package/dist/esm/client/session-delegation.js.map +1 -0
- package/dist/esm/client/session-identity.d.ts +97 -0
- package/dist/esm/client/session-identity.js +392 -0
- package/dist/esm/client/session-identity.js.map +1 -0
- package/dist/esm/client/session-minter.d.ts +94 -0
- package/dist/esm/client/session-minter.js +178 -0
- package/dist/esm/client/session-minter.js.map +1 -0
- package/dist/esm/client/shared-memory-credential-storage.d.ts +44 -0
- package/dist/esm/client/shared-memory-credential-storage.js +243 -0
- package/dist/esm/client/shared-memory-credential-storage.js.map +1 -0
- package/dist/esm/client/slots.d.ts +59 -0
- package/dist/esm/client/slots.js +67 -0
- package/dist/esm/client/slots.js.map +1 -0
- package/dist/esm/client/sso.d.ts +33 -0
- package/dist/esm/client/sso.js +120 -0
- package/dist/esm/client/sso.js.map +1 -0
- package/dist/esm/client/state-storage.d.ts +135 -0
- package/dist/esm/client/state-storage.js +178 -0
- package/dist/esm/client/state-storage.js.map +1 -0
- package/package.json +5 -4
- package/src/client/app-delegation-source.ts +86 -0
- package/src/client/app-lock.ts +120 -0
- package/src/client/auth-client.ts +1300 -487
- package/src/client/base64.ts +28 -0
- package/src/client/cookie-state-storage.ts +277 -0
- package/src/client/credential-storage.ts +56 -0
- package/src/client/db.ts +56 -12
- package/src/client/foreground-refresh.ts +92 -0
- package/src/client/idb-credential-storage.ts +82 -0
- package/src/client/index.ts +24 -8
- package/src/client/local-credential-storage.ts +68 -0
- package/src/client/memory-credential-storage.ts +41 -0
- package/src/client/session-delegation.ts +138 -0
- package/src/client/session-identity.ts +510 -0
- package/src/client/session-minter.ts +277 -0
- package/src/client/shared-memory-credential-storage.ts +303 -0
- package/src/client/slots.ts +78 -0
- package/src/client/sso.ts +122 -0
- package/src/client/state-storage.ts +284 -0
- package/dist/esm/client/idle-manager.d.ts +0 -83
- package/dist/esm/client/idle-manager.js +0 -100
- package/dist/esm/client/idle-manager.js.map +0 -1
- package/dist/esm/client/storage.d.ts +0 -51
- package/dist/esm/client/storage.js +0 -90
- package/dist/esm/client/storage.js.map +0 -1
- package/src/client/idle-manager.ts +0 -162
- package/src/client/storage.ts +0 -114
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every name one client writes under, and the one thing that knows the whole set.
|
|
3
|
+
*
|
|
4
|
+
* Its own module rather than living beside the credential store or the state
|
|
5
|
+
* store, because it belongs to neither: the credentials and the state of a
|
|
6
|
+
* sign-in are named from one namespace, and a list that sat in either file would
|
|
7
|
+
* have to reach across to the other.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* The bare names, before a namespace is applied.
|
|
11
|
+
*
|
|
12
|
+
* One list, so a name cannot be added to some of the places that need it and
|
|
13
|
+
* missed in others. It was two lists and a hand-written mapping, and the state
|
|
14
|
+
* record was left out of the namespacing for exactly that reason.
|
|
15
|
+
*/
|
|
16
|
+
const SLOT_NAMES = {
|
|
17
|
+
/** The session's key and the chain issued to it. */
|
|
18
|
+
session: 'session',
|
|
19
|
+
/**
|
|
20
|
+
* Where a sign-in ceremony keeps its key until it returns.
|
|
21
|
+
*
|
|
22
|
+
* Its own name rather than {@link SLOT_NAMES.session}, so a ceremony that is
|
|
23
|
+
* cancelled or never comes back cannot take a working session with it.
|
|
24
|
+
*/
|
|
25
|
+
sessionPending: 'session-pending',
|
|
26
|
+
/** The key an application signs with, and its delegation. */
|
|
27
|
+
app: 'app',
|
|
28
|
+
/**
|
|
29
|
+
* Where a ceremony keeps the credential it minted until its session is stored.
|
|
30
|
+
*
|
|
31
|
+
* The store reaches every tab of the origin, so writing to the app name during
|
|
32
|
+
* a ceremony — or clearing it first — would change what those tabs act with
|
|
33
|
+
* before the sign-in has succeeded. This is promoted instead, once there is a
|
|
34
|
+
* session behind it.
|
|
35
|
+
*/
|
|
36
|
+
appPending: 'app-pending',
|
|
37
|
+
/**
|
|
38
|
+
* The record of who is signed in here and until when.
|
|
39
|
+
*
|
|
40
|
+
* Not a credential slot, and named here anyway: a namespace that moved the
|
|
41
|
+
* credentials and left this fixed would give two clients their own keys and one
|
|
42
|
+
* shared answer to who is signed in.
|
|
43
|
+
*/
|
|
44
|
+
state: 'ic-session-state',
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* What separates a namespace from the name it prefixes.
|
|
48
|
+
*
|
|
49
|
+
* A cross-origin contract rather than a detail: a sibling subdomain reads the
|
|
50
|
+
* state record by name, so both sides have to agree on this byte.
|
|
51
|
+
*/
|
|
52
|
+
const NAMESPACE_SEPARATOR = ':';
|
|
53
|
+
/**
|
|
54
|
+
* The names one client writes under, prefixed where a namespace is set.
|
|
55
|
+
*
|
|
56
|
+
* Assigned in one place rather than defaulted by each store: implementations
|
|
57
|
+
* choosing their own is what produced three colliding slots in the arrangement
|
|
58
|
+
* this replaces, and one assigner cannot collide with itself. A namespace moves
|
|
59
|
+
* all of them at once, so an application running two clients under one origin
|
|
60
|
+
* separates them with one string rather than renaming some and missing others.
|
|
61
|
+
* @param namespace - Prefix for every name, or `undefined` for the bare ones.
|
|
62
|
+
*/
|
|
63
|
+
export function slotsFor(namespace) {
|
|
64
|
+
const prefix = namespace === undefined ? '' : `${namespace}${NAMESPACE_SEPARATOR}`;
|
|
65
|
+
return Object.fromEntries(Object.entries(SLOT_NAMES).map(([key, name]) => [key, `${prefix}${name}`]));
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=slots.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slots.js","sourceRoot":"","sources":["../../../src/client/slots.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;;;;;GAMG;AACH,MAAM,UAAU,GAAG;IACjB,oDAAoD;IACpD,OAAO,EAAE,SAAS;IAElB;;;;;OAKG;IACH,cAAc,EAAE,iBAAiB;IAEjC,6DAA6D;IAC7D,GAAG,EAAE,KAAK;IAEV;;;;;;;OAOG;IACH,UAAU,EAAE,aAAa;IAEzB;;;;;;OAMG;IACH,KAAK,EAAE,kBAAkB;CACjB,CAAC;AAEX;;;;;GAKG;AACH,MAAM,mBAAmB,GAAG,GAAG,CAAC;AAKhC;;;;;;;;;GASG;AACH,MAAM,UAAU,QAAQ,CAAC,SAAkB;IACzC,MAAM,MAAM,GAAG,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,SAAS,GAAG,mBAAmB,EAAE,CAAC;IACnF,OAAO,MAAM,CAAC,WAAW,CACvB,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,GAAG,MAAM,GAAG,IAAI,EAAE,CAAC,CAAC,CAClE,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalizes an SSO domain to the authority the identity provider fetches the
|
|
3
|
+
* discovery document from: lowercased, IDNA-encoded, host and optional port.
|
|
4
|
+
*
|
|
5
|
+
* @param domain - The organization domain.
|
|
6
|
+
* @returns The normalized authority.
|
|
7
|
+
* @throws When `domain` is not a domain with an optional port.
|
|
8
|
+
*/
|
|
9
|
+
export declare function normalizeSsoDomain(domain: string): string;
|
|
10
|
+
/**
|
|
11
|
+
* Checks whether an organization domain can be used for SSO sign-in: it is a
|
|
12
|
+
* domain name, and it publishes `/.well-known/ii-openid-configuration` with a
|
|
13
|
+
* `client_id` and an `openid_configuration` URL.
|
|
14
|
+
*
|
|
15
|
+
* The document must be served with `Access-Control-Allow-Origin: *`.
|
|
16
|
+
*
|
|
17
|
+
* The check waits on a third-party server and has no deadline of its own, so
|
|
18
|
+
* `signal` sets one. Pass `AbortSignal.timeout(...)` for a one-shot check, or a
|
|
19
|
+
* controller you abort per keystroke when checking as the user types.
|
|
20
|
+
*
|
|
21
|
+
* @param domain - The organization domain.
|
|
22
|
+
* @param signal - Bounds the check.
|
|
23
|
+
* @returns Whether the domain is usable for SSO sign-in.
|
|
24
|
+
* @throws When `signal` aborts — an abandoned check is not a verdict on the
|
|
25
|
+
* domain, so it must not be read as `false`.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* if (await isValidSsoDomain(input.value, AbortSignal.timeout(5_000))) {
|
|
29
|
+
* const authClient = new AuthClient({ ssoDomain: input.value });
|
|
30
|
+
* await authClient.signIn();
|
|
31
|
+
* }
|
|
32
|
+
*/
|
|
33
|
+
export declare function isValidSsoDomain(domain: string, signal: AbortSignal): Promise<boolean>;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
// The identity provider's cap on the domain it will fetch.
|
|
2
|
+
const MAX_AUTHORITY_LENGTH = 255;
|
|
3
|
+
const WELL_KNOWN_PATH = '/.well-known/ii-openid-configuration';
|
|
4
|
+
/**
|
|
5
|
+
* `localhost` / `127.0.0.1`, optionally followed by `:<port>`. IPv6 loopback
|
|
6
|
+
* (`[::1]`, etc.) is intentionally not handled — the identity provider doesn't
|
|
7
|
+
* recognise it either, and its e2e setup uses the hostname form.
|
|
8
|
+
*/
|
|
9
|
+
function isLoopbackHost(host) {
|
|
10
|
+
let url;
|
|
11
|
+
try {
|
|
12
|
+
// Parse as a URL authority so the optional `:<port>` is split off for us
|
|
13
|
+
// rather than by hand. Invalid input throws and is treated as non-loopback.
|
|
14
|
+
url = new URL(`http://${host}`);
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
// A bare `host[:port]` has no path/query/fragment; reject e.g. `localhost/x`.
|
|
20
|
+
if (url.pathname !== '/' || url.search !== '' || url.hash !== '') {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
return url.hostname === 'localhost' || url.hostname === '127.0.0.1';
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Normalizes an SSO domain to the authority the identity provider fetches the
|
|
27
|
+
* discovery document from: lowercased, IDNA-encoded, host and optional port.
|
|
28
|
+
*
|
|
29
|
+
* @param domain - The organization domain.
|
|
30
|
+
* @returns The normalized authority.
|
|
31
|
+
* @throws When `domain` is not a domain with an optional port.
|
|
32
|
+
*/
|
|
33
|
+
export function normalizeSsoDomain(domain) {
|
|
34
|
+
const trimmed = domain.trim();
|
|
35
|
+
if (trimmed.length === 0) {
|
|
36
|
+
throw new Error('ssoDomain cannot be empty');
|
|
37
|
+
}
|
|
38
|
+
let url;
|
|
39
|
+
try {
|
|
40
|
+
url = new URL(`https://${trimmed}`);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
throw new Error(`ssoDomain is not a domain: ${trimmed}`);
|
|
44
|
+
}
|
|
45
|
+
const authority = url.host;
|
|
46
|
+
// Rebuilding from the host and port alone has to reproduce what was parsed,
|
|
47
|
+
// so anything else the domain carried shows up as a difference.
|
|
48
|
+
if (new URL(`https://${authority}`).href !== url.href) {
|
|
49
|
+
throw new Error(`ssoDomain must be a domain and optional port, nothing else: ${trimmed}`);
|
|
50
|
+
}
|
|
51
|
+
if (authority.length > MAX_AUTHORITY_LENGTH) {
|
|
52
|
+
throw new Error(`ssoDomain exceeds ${MAX_AUTHORITY_LENGTH} characters`);
|
|
53
|
+
}
|
|
54
|
+
// A bare hostname is a half-typed domain, not something worth a request.
|
|
55
|
+
if (!authority.includes('.') && !isLoopbackHost(authority)) {
|
|
56
|
+
throw new Error(`ssoDomain is not a domain name: ${trimmed}`);
|
|
57
|
+
}
|
|
58
|
+
return authority;
|
|
59
|
+
}
|
|
60
|
+
function publishesSsoConfiguration(value) {
|
|
61
|
+
if (typeof value !== 'object' || value === null) {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
const { client_id, openid_configuration } = value;
|
|
65
|
+
return typeof client_id === 'string' && typeof openid_configuration === 'string';
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Checks whether an organization domain can be used for SSO sign-in: it is a
|
|
69
|
+
* domain name, and it publishes `/.well-known/ii-openid-configuration` with a
|
|
70
|
+
* `client_id` and an `openid_configuration` URL.
|
|
71
|
+
*
|
|
72
|
+
* The document must be served with `Access-Control-Allow-Origin: *`.
|
|
73
|
+
*
|
|
74
|
+
* The check waits on a third-party server and has no deadline of its own, so
|
|
75
|
+
* `signal` sets one. Pass `AbortSignal.timeout(...)` for a one-shot check, or a
|
|
76
|
+
* controller you abort per keystroke when checking as the user types.
|
|
77
|
+
*
|
|
78
|
+
* @param domain - The organization domain.
|
|
79
|
+
* @param signal - Bounds the check.
|
|
80
|
+
* @returns Whether the domain is usable for SSO sign-in.
|
|
81
|
+
* @throws When `signal` aborts — an abandoned check is not a verdict on the
|
|
82
|
+
* domain, so it must not be read as `false`.
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* if (await isValidSsoDomain(input.value, AbortSignal.timeout(5_000))) {
|
|
86
|
+
* const authClient = new AuthClient({ ssoDomain: input.value });
|
|
87
|
+
* await authClient.signIn();
|
|
88
|
+
* }
|
|
89
|
+
*/
|
|
90
|
+
export async function isValidSsoDomain(domain, signal) {
|
|
91
|
+
signal.throwIfAborted();
|
|
92
|
+
let normalized;
|
|
93
|
+
try {
|
|
94
|
+
normalized = normalizeSsoDomain(domain);
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
const scheme = isLoopbackHost(normalized) ? 'http' : 'https';
|
|
100
|
+
try {
|
|
101
|
+
const response = await fetch(`${scheme}://${normalized}${WELL_KNOWN_PATH}`, {
|
|
102
|
+
headers: { Accept: 'application/json' },
|
|
103
|
+
signal,
|
|
104
|
+
});
|
|
105
|
+
if (!response.ok) {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
const valid = publishesSsoConfiguration(await response.json());
|
|
109
|
+
signal.throwIfAborted();
|
|
110
|
+
return valid;
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
if (signal.aborted) {
|
|
114
|
+
throw error;
|
|
115
|
+
}
|
|
116
|
+
// A DNS, TLS, CORS, or parse failure all leave the domain unusable.
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=sso.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sso.js","sourceRoot":"","sources":["../../../src/client/sso.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { Principal } from '@icp-sdk/core/principal';
|
|
2
|
+
/**
|
|
3
|
+
* What a client knows about its sign-in without holding a credential: which
|
|
4
|
+
* account is signed in here, and when that ends.
|
|
5
|
+
*
|
|
6
|
+
* Both fields are public. The principal is what an application's canisters see
|
|
7
|
+
* as the caller, and the expiration is what the identity provider granted, so
|
|
8
|
+
* neither is a secret and both can live somewhere a page reads synchronously.
|
|
9
|
+
*/
|
|
10
|
+
export interface SessionState {
|
|
11
|
+
principal: Principal;
|
|
12
|
+
/** Nanoseconds since the epoch, matching `Delegation.expiration`. */
|
|
13
|
+
expiration: bigint;
|
|
14
|
+
/**
|
|
15
|
+
* Whether this origin holds a credential for that account, or only knows the
|
|
16
|
+
* sign-in exists.
|
|
17
|
+
*
|
|
18
|
+
* Derived on read and never published: where the record reaches further than
|
|
19
|
+
* one origin, the record itself cannot carry a per-origin fact. A sibling
|
|
20
|
+
* subdomain that has not acquired its own credential reads the sign-in with
|
|
21
|
+
* `held: false`, which is what tells it to acquire one.
|
|
22
|
+
*/
|
|
23
|
+
held: boolean;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Holds the state of a sign-in.
|
|
27
|
+
*
|
|
28
|
+
* This is what decides whether this origin is signed in and as whom. It is a
|
|
29
|
+
* store of its own because it has to be readable without awaiting, which a
|
|
30
|
+
* credential store does not.
|
|
31
|
+
*
|
|
32
|
+
* A record can be stale — a session revoked at the canister still reads as
|
|
33
|
+
* signed in until something calls and is refused — so it is the state as this
|
|
34
|
+
* browser knows it rather than an authority against the network.
|
|
35
|
+
*/
|
|
36
|
+
export interface StateStorage {
|
|
37
|
+
/** The state, or `null` where nothing is signed in within this store's reach. */
|
|
38
|
+
get(key: string): SessionState | null;
|
|
39
|
+
set(key: string, state: Omit<SessionState, 'held'>): void;
|
|
40
|
+
/**
|
|
41
|
+
* Removes the state, and anything this store publishes beyond this origin.
|
|
42
|
+
*
|
|
43
|
+
* What signing out does: the user ended the sign-in, so a sibling reading a
|
|
44
|
+
* shared record must stop seeing one.
|
|
45
|
+
*/
|
|
46
|
+
remove(key: string): void;
|
|
47
|
+
/**
|
|
48
|
+
* Removes what this origin holds, leaving anything shared alone.
|
|
49
|
+
*
|
|
50
|
+
* What finding out does, which is a different act. An origin whose chain turns
|
|
51
|
+
* out to be dead cannot tell whether the session was revoked or replaced by a
|
|
52
|
+
* sibling signing in — and in the second case the shared record was written by
|
|
53
|
+
* that sibling a moment ago, so retracting it would tell it that the session it
|
|
54
|
+
* just obtained is gone.
|
|
55
|
+
*
|
|
56
|
+
* A store that publishes nothing beyond this origin answers it with
|
|
57
|
+
* {@link remove}, which is one line and the right answer.
|
|
58
|
+
*/
|
|
59
|
+
discard(key: string): void;
|
|
60
|
+
/**
|
|
61
|
+
* Whether a sign-in kept in this store may be resumed without a ceremony.
|
|
62
|
+
*
|
|
63
|
+
* True only where the record reaches past this origin, because that is the
|
|
64
|
+
* case a silent re-issue exists for: a sibling reads the sign-in, holds no
|
|
65
|
+
* credential of its own, and needs the identity provider to remember the
|
|
66
|
+
* session it can extend. A store the origin keeps to itself has nothing to
|
|
67
|
+
* gain and would only be asking the provider to persist a sign-in on a device
|
|
68
|
+
* where nothing wanted one kept.
|
|
69
|
+
*
|
|
70
|
+
* Absent means false, which is the answer for a store that never considered
|
|
71
|
+
* the question.
|
|
72
|
+
*/
|
|
73
|
+
readonly resumable?: boolean;
|
|
74
|
+
/**
|
|
75
|
+
* Registers a listener fired when the state under `key` changes, including
|
|
76
|
+
* when something other than this client changed it. Returns a function that
|
|
77
|
+
* unregisters it.
|
|
78
|
+
*
|
|
79
|
+
* Required rather than optional: two clients can share one store, even a
|
|
80
|
+
* store with no medium behind it, and a client that cannot be told the record
|
|
81
|
+
* changed under it goes on answering for a sign-in that is no longer there.
|
|
82
|
+
*/
|
|
83
|
+
subscribe(key: string, listener: () => void): () => void;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* State held for the lifetime of the instance and shared with nothing.
|
|
87
|
+
*
|
|
88
|
+
* For tests and for environments with no `localStorage`, where a client is
|
|
89
|
+
* alone and a reload starts from nothing.
|
|
90
|
+
* @see implements {@link StateStorage}
|
|
91
|
+
*/
|
|
92
|
+
export declare class MemoryStateStorage implements StateStorage {
|
|
93
|
+
#private;
|
|
94
|
+
get(key: string): SessionState | null;
|
|
95
|
+
set(key: string, state: Omit<SessionState, 'held'>): void;
|
|
96
|
+
remove(key: string): void;
|
|
97
|
+
/** The same as {@link remove}: this store publishes nothing at all. */
|
|
98
|
+
discard(key: string): void;
|
|
99
|
+
/**
|
|
100
|
+
* Fires when the state changes, which here means when something writes to
|
|
101
|
+
* this instance.
|
|
102
|
+
*
|
|
103
|
+
* Nothing outside the process can reach a `Map`, so a medium is not what makes
|
|
104
|
+
* this worth having: two clients can be handed the same instance, and this is
|
|
105
|
+
* how the one that did not write finds out. Each listener is told only when the
|
|
106
|
+
* record actually changed since it was last told, so a client's own writes do
|
|
107
|
+
* not come back to it as news.
|
|
108
|
+
*/
|
|
109
|
+
subscribe(key: string, listener: () => void): () => void;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* State in `localStorage`, so every tab of an origin agrees on it and it
|
|
113
|
+
* survives a reload.
|
|
114
|
+
*
|
|
115
|
+
* The two fields are written as `<principal>|<expiration>` rather than JSON,
|
|
116
|
+
* because an expiration is a `bigint` and JSON has no way to carry one.
|
|
117
|
+
* @see implements {@link StateStorage}
|
|
118
|
+
*/
|
|
119
|
+
export declare class LocalStateStorage implements StateStorage {
|
|
120
|
+
#private;
|
|
121
|
+
get(key: string): SessionState | null;
|
|
122
|
+
set(key: string, state: Omit<SessionState, 'held'>): void;
|
|
123
|
+
remove(key: string): void;
|
|
124
|
+
/** The same as {@link remove}: this store publishes nothing beyond the origin. */
|
|
125
|
+
discard(key: string): void;
|
|
126
|
+
/**
|
|
127
|
+
* Fires when the state changes, here or in another tab of this origin.
|
|
128
|
+
*
|
|
129
|
+
* `localStorage` raises `storage` in every tab except the one that wrote, so
|
|
130
|
+
* that event carries a change between tabs and a same-tab write is announced
|
|
131
|
+
* directly. Either way the record is readable before it is announced, so a
|
|
132
|
+
* listener asking who is signed in sees what it was told about.
|
|
133
|
+
*/
|
|
134
|
+
subscribe(key: string, listener: () => void): () => void;
|
|
135
|
+
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { Principal } from '@icp-sdk/core/principal';
|
|
2
|
+
/**
|
|
3
|
+
* State held for the lifetime of the instance and shared with nothing.
|
|
4
|
+
*
|
|
5
|
+
* For tests and for environments with no `localStorage`, where a client is
|
|
6
|
+
* alone and a reload starts from nothing.
|
|
7
|
+
* @see implements {@link StateStorage}
|
|
8
|
+
*/
|
|
9
|
+
export class MemoryStateStorage {
|
|
10
|
+
#state = new Map();
|
|
11
|
+
#subscribers = new Map();
|
|
12
|
+
get(key) {
|
|
13
|
+
// Nothing reaches past this instance, so a record here is always one it holds.
|
|
14
|
+
const state = this.#state.get(key);
|
|
15
|
+
return state === undefined ? null : { ...state, held: true };
|
|
16
|
+
}
|
|
17
|
+
set(key, state) {
|
|
18
|
+
this.#state.set(key, state);
|
|
19
|
+
this.#fire(key);
|
|
20
|
+
}
|
|
21
|
+
remove(key) {
|
|
22
|
+
this.#state.delete(key);
|
|
23
|
+
this.#fire(key);
|
|
24
|
+
}
|
|
25
|
+
/** The same as {@link remove}: this store publishes nothing at all. */
|
|
26
|
+
discard(key) {
|
|
27
|
+
this.remove(key);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Fires when the state changes, which here means when something writes to
|
|
31
|
+
* this instance.
|
|
32
|
+
*
|
|
33
|
+
* Nothing outside the process can reach a `Map`, so a medium is not what makes
|
|
34
|
+
* this worth having: two clients can be handed the same instance, and this is
|
|
35
|
+
* how the one that did not write finds out. Each listener is told only when the
|
|
36
|
+
* record actually changed since it was last told, so a client's own writes do
|
|
37
|
+
* not come back to it as news.
|
|
38
|
+
*/
|
|
39
|
+
subscribe(key, listener) {
|
|
40
|
+
let last = this.#raw(key);
|
|
41
|
+
const check = () => {
|
|
42
|
+
const now = this.#raw(key);
|
|
43
|
+
if (now !== last) {
|
|
44
|
+
last = now;
|
|
45
|
+
listener();
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
const listeners = this.#subscribers.get(key) ?? new Set();
|
|
49
|
+
listeners.add(check);
|
|
50
|
+
this.#subscribers.set(key, listeners);
|
|
51
|
+
return () => {
|
|
52
|
+
listeners.delete(check);
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
#fire(key) {
|
|
56
|
+
for (const check of [...(this.#subscribers.get(key) ?? [])])
|
|
57
|
+
check();
|
|
58
|
+
}
|
|
59
|
+
// The same shape `LocalStateStorage` keeps, so "changed" means the same thing
|
|
60
|
+
// in both: a record is one string, and comparing strings needs no field walk.
|
|
61
|
+
#raw(key) {
|
|
62
|
+
const state = this.#state.get(key);
|
|
63
|
+
return state === undefined
|
|
64
|
+
? null
|
|
65
|
+
: `${state.principal.toText()}|${state.expiration.toString()}`;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* State in `localStorage`, so every tab of an origin agrees on it and it
|
|
70
|
+
* survives a reload.
|
|
71
|
+
*
|
|
72
|
+
* The two fields are written as `<principal>|<expiration>` rather than JSON,
|
|
73
|
+
* because an expiration is a `bigint` and JSON has no way to carry one.
|
|
74
|
+
* @see implements {@link StateStorage}
|
|
75
|
+
*/
|
|
76
|
+
export class LocalStateStorage {
|
|
77
|
+
// Fired on a same-tab write: `storage` reaches the other tabs of an origin and
|
|
78
|
+
// deliberately not the one that wrote. Per key, because one store answers for
|
|
79
|
+
// whatever key it is handed.
|
|
80
|
+
#subscribers = new Map();
|
|
81
|
+
get(key) {
|
|
82
|
+
const raw = this.#localStorage().getItem(key);
|
|
83
|
+
if (raw === null)
|
|
84
|
+
return null;
|
|
85
|
+
const [principalText, expiration] = raw.split('|');
|
|
86
|
+
if (principalText === undefined || expiration === undefined)
|
|
87
|
+
return null;
|
|
88
|
+
try {
|
|
89
|
+
return {
|
|
90
|
+
principal: Principal.fromText(principalText),
|
|
91
|
+
expiration: BigInt(expiration),
|
|
92
|
+
// This record is written by this origin and read by no other, so having
|
|
93
|
+
// one and holding a credential for it are the same thing.
|
|
94
|
+
held: true,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
set(key, state) {
|
|
102
|
+
this.#localStorage().setItem(key, `${state.principal.toText()}|${state.expiration.toString()}`);
|
|
103
|
+
this.#fire(key);
|
|
104
|
+
}
|
|
105
|
+
remove(key) {
|
|
106
|
+
this.#localStorage().removeItem(key);
|
|
107
|
+
this.#fire(key);
|
|
108
|
+
}
|
|
109
|
+
/** The same as {@link remove}: this store publishes nothing beyond the origin. */
|
|
110
|
+
discard(key) {
|
|
111
|
+
this.remove(key);
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Fires when the state changes, here or in another tab of this origin.
|
|
115
|
+
*
|
|
116
|
+
* `localStorage` raises `storage` in every tab except the one that wrote, so
|
|
117
|
+
* that event carries a change between tabs and a same-tab write is announced
|
|
118
|
+
* directly. Either way the record is readable before it is announced, so a
|
|
119
|
+
* listener asking who is signed in sees what it was told about.
|
|
120
|
+
*/
|
|
121
|
+
subscribe(key, listener) {
|
|
122
|
+
let last = this.#raw(key);
|
|
123
|
+
const check = () => {
|
|
124
|
+
const now = this.#raw(key);
|
|
125
|
+
if (now !== last) {
|
|
126
|
+
last = now;
|
|
127
|
+
listener();
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
const listeners = this.#subscribers.get(key) ?? new Set();
|
|
131
|
+
listeners.add(check);
|
|
132
|
+
this.#subscribers.set(key, listeners);
|
|
133
|
+
const onStorage = (event) => {
|
|
134
|
+
// `key` is null when a tab cleared the whole store, which changes this too.
|
|
135
|
+
if (event.key === key || event.key === null)
|
|
136
|
+
check();
|
|
137
|
+
};
|
|
138
|
+
globalThis.addEventListener('storage', onStorage);
|
|
139
|
+
// `storage` is the only notice this store gets, and a page restored from the
|
|
140
|
+
// back-forward cache was not listening while it was frozen. So the record is
|
|
141
|
+
// re-read when the page is shown or its window regains focus, which is when
|
|
142
|
+
// somebody is about to act on the answer.
|
|
143
|
+
const onVisible = () => {
|
|
144
|
+
if (document.visibilityState === 'visible')
|
|
145
|
+
check();
|
|
146
|
+
};
|
|
147
|
+
const hooked = typeof document !== 'undefined';
|
|
148
|
+
if (hooked) {
|
|
149
|
+
document.addEventListener('visibilitychange', onVisible);
|
|
150
|
+
globalThis.addEventListener('pageshow', check);
|
|
151
|
+
globalThis.addEventListener('focus', check);
|
|
152
|
+
}
|
|
153
|
+
return () => {
|
|
154
|
+
listeners.delete(check);
|
|
155
|
+
globalThis.removeEventListener('storage', onStorage);
|
|
156
|
+
if (hooked) {
|
|
157
|
+
document.removeEventListener('visibilitychange', onVisible);
|
|
158
|
+
globalThis.removeEventListener('pageshow', check);
|
|
159
|
+
globalThis.removeEventListener('focus', check);
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
#fire(key) {
|
|
164
|
+
for (const check of [...(this.#subscribers.get(key) ?? [])])
|
|
165
|
+
check();
|
|
166
|
+
}
|
|
167
|
+
#raw(key) {
|
|
168
|
+
return this.#localStorage().getItem(key);
|
|
169
|
+
}
|
|
170
|
+
#localStorage() {
|
|
171
|
+
const ls = globalThis.localStorage;
|
|
172
|
+
if (!ls) {
|
|
173
|
+
throw new Error('Could not find local storage.');
|
|
174
|
+
}
|
|
175
|
+
return ls;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
//# sourceMappingURL=state-storage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state-storage.js","sourceRoot":"","sources":["../../../src/client/state-storage.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@icp-sdk/auth",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "10.0.0",
|
|
4
4
|
"author": "DFINITY Stiftung <sdk@dfinity.org>",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"description": "Authentication library for Internet Computer web apps",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
],
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@biomejs/biome": "^2.5.6",
|
|
46
|
-
"@icp-sdk/core": "^
|
|
46
|
+
"@icp-sdk/core": "^6.1.0",
|
|
47
47
|
"fake-indexeddb": "^6.2.5",
|
|
48
48
|
"jsdom": "^27.4.0",
|
|
49
49
|
"publint": "^0.3.22",
|
|
@@ -51,15 +51,16 @@
|
|
|
51
51
|
"vitest": "4.0.14"
|
|
52
52
|
},
|
|
53
53
|
"peerDependencies": {
|
|
54
|
-
"@icp-sdk/core": "^
|
|
54
|
+
"@icp-sdk/core": "^6"
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
|
-
"@icp-sdk/signer": "^
|
|
57
|
+
"@icp-sdk/signer": "^6.0.0",
|
|
58
58
|
"idb": "^7.1.1"
|
|
59
59
|
},
|
|
60
60
|
"scripts": {
|
|
61
61
|
"build": "tsc -p tsconfig.build.json && pnpm lint:package",
|
|
62
62
|
"test": "vitest",
|
|
63
|
+
"typecheck": "tsc -p tsconfig.test.json --noEmit",
|
|
63
64
|
"codestyle:check": "biome check",
|
|
64
65
|
"codestyle:fix": "biome check --write",
|
|
65
66
|
"lint:package": "publint --strict"
|