@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
package/dist/esm/client/db.js
CHANGED
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
import { openDB } from 'idb';
|
|
2
|
-
|
|
2
|
+
// Increment when the shape of what is stored changes.
|
|
3
|
+
export const DB_VERSION = 1;
|
|
3
4
|
const AUTH_DB_NAME = 'auth-client-db';
|
|
4
5
|
const OBJECT_STORE_NAME = 'ic-keyval';
|
|
5
|
-
const _openDbStore = async (dbName = AUTH_DB_NAME, storeName = OBJECT_STORE_NAME, version) => {
|
|
6
|
-
// Clear legacy stored delegations
|
|
7
|
-
if (globalThis.localStorage?.getItem(KEY_STORAGE_DELEGATION)) {
|
|
8
|
-
globalThis.localStorage.removeItem(KEY_STORAGE_DELEGATION);
|
|
9
|
-
globalThis.localStorage.removeItem(KEY_STORAGE_KEY);
|
|
10
|
-
}
|
|
6
|
+
const _openDbStore = async (dbName = AUTH_DB_NAME, storeName = OBJECT_STORE_NAME, version, onTerminated) => {
|
|
11
7
|
return await openDB(dbName, version, {
|
|
12
8
|
upgrade: (database) => {
|
|
13
9
|
if (database.objectStoreNames.contains(storeName)) {
|
|
@@ -15,6 +11,7 @@ const _openDbStore = async (dbName = AUTH_DB_NAME, storeName = OBJECT_STORE_NAME
|
|
|
15
11
|
}
|
|
16
12
|
database.createObjectStore(storeName);
|
|
17
13
|
},
|
|
14
|
+
terminated: onTerminated,
|
|
18
15
|
});
|
|
19
16
|
};
|
|
20
17
|
async function _getValue(db, storeName, key) {
|
|
@@ -31,8 +28,9 @@ async function _removeValue(db, storeName, key) {
|
|
|
31
28
|
* Defaults to `'auth-client-db'` with an object store of `'ic-keyval'`
|
|
32
29
|
*/
|
|
33
30
|
export class IdbKeyVal {
|
|
34
|
-
|
|
31
|
+
_dbName;
|
|
35
32
|
_storeName;
|
|
33
|
+
_version;
|
|
36
34
|
/**
|
|
37
35
|
* @param {DBCreateOptions} options - DBCreateOptions
|
|
38
36
|
* @param {DBCreateOptions['dbName']} options.dbName name for the indexeddb database
|
|
@@ -43,13 +41,56 @@ export class IdbKeyVal {
|
|
|
43
41
|
*/
|
|
44
42
|
static async create(options) {
|
|
45
43
|
const { dbName = AUTH_DB_NAME, storeName = OBJECT_STORE_NAME, version = DB_VERSION, } = options ?? {};
|
|
46
|
-
const
|
|
47
|
-
|
|
44
|
+
const keyVal = new IdbKeyVal(dbName, storeName, version);
|
|
45
|
+
// Open eagerly so an unavailable database fails at creation time.
|
|
46
|
+
await keyVal._openDb();
|
|
47
|
+
return keyVal;
|
|
48
48
|
}
|
|
49
|
+
_dbPromise = null;
|
|
49
50
|
// Do not use - instead prefer create
|
|
50
|
-
constructor(
|
|
51
|
-
this.
|
|
51
|
+
constructor(_dbName, _storeName, _version) {
|
|
52
|
+
this._dbName = _dbName;
|
|
52
53
|
this._storeName = _storeName;
|
|
54
|
+
this._version = _version;
|
|
55
|
+
}
|
|
56
|
+
// The browser can force-close the connection at any time; the `terminated`
|
|
57
|
+
// hook drops it so the next operation opens a fresh one.
|
|
58
|
+
_openDb() {
|
|
59
|
+
if (this._dbPromise === null) {
|
|
60
|
+
const promise = _openDbStore(this._dbName, this._storeName, this._version, () => {
|
|
61
|
+
if (this._dbPromise === promise) {
|
|
62
|
+
this._dbPromise = null;
|
|
63
|
+
}
|
|
64
|
+
}).catch((error) => {
|
|
65
|
+
if (this._dbPromise === promise) {
|
|
66
|
+
this._dbPromise = null;
|
|
67
|
+
}
|
|
68
|
+
throw error;
|
|
69
|
+
});
|
|
70
|
+
this._dbPromise = promise;
|
|
71
|
+
}
|
|
72
|
+
return this._dbPromise;
|
|
73
|
+
}
|
|
74
|
+
// Chrome can force-close a connection without firing `terminated`; the next
|
|
75
|
+
// transaction then throws an InvalidStateError. Drop the dead connection and
|
|
76
|
+
// retry once on a fresh one.
|
|
77
|
+
async _withDb(action) {
|
|
78
|
+
const promise = this._openDb();
|
|
79
|
+
const db = await promise;
|
|
80
|
+
try {
|
|
81
|
+
return await action(db);
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
// Matched by name: a DOMException does not inherit from Error.
|
|
85
|
+
const name = error !== null && typeof error === 'object' && 'name' in error && error.name;
|
|
86
|
+
if (name !== 'InvalidStateError') {
|
|
87
|
+
throw error;
|
|
88
|
+
}
|
|
89
|
+
if (this._dbPromise === promise) {
|
|
90
|
+
this._dbPromise = null;
|
|
91
|
+
}
|
|
92
|
+
return await action(await this._openDb());
|
|
93
|
+
}
|
|
53
94
|
}
|
|
54
95
|
/**
|
|
55
96
|
* Basic setter
|
|
@@ -58,7 +99,7 @@ export class IdbKeyVal {
|
|
|
58
99
|
* @returns void
|
|
59
100
|
*/
|
|
60
101
|
async set(key, value) {
|
|
61
|
-
return await _setValue(
|
|
102
|
+
return await this._withDb((db) => _setValue(db, this._storeName, key, value));
|
|
62
103
|
}
|
|
63
104
|
/**
|
|
64
105
|
* Basic getter
|
|
@@ -69,7 +110,7 @@ export class IdbKeyVal {
|
|
|
69
110
|
* await get<string>('exampleKey') -> 'exampleValue'
|
|
70
111
|
*/
|
|
71
112
|
async get(key) {
|
|
72
|
-
return (await _getValue(
|
|
113
|
+
return await this._withDb(async (db) => (await _getValue(db, this._storeName, key)) ?? null);
|
|
73
114
|
}
|
|
74
115
|
/**
|
|
75
116
|
* Remove a key
|
|
@@ -77,7 +118,7 @@ export class IdbKeyVal {
|
|
|
77
118
|
* @returns void
|
|
78
119
|
*/
|
|
79
120
|
async remove(key) {
|
|
80
|
-
return await _removeValue(
|
|
121
|
+
return await this._withDb((db) => _removeValue(db, this._storeName, key));
|
|
81
122
|
}
|
|
82
123
|
}
|
|
83
124
|
//# sourceMappingURL=db.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"db.js","sourceRoot":"","sources":["../../../src/client/db.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,MAAM,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"db.js","sourceRoot":"","sources":["../../../src/client/db.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Calls back when the page is shown or the window regains focus.
|
|
3
|
+
*
|
|
4
|
+
* Its own module because it is the one part of the session machinery that needs a
|
|
5
|
+
* DOM. `AuthClient` runs outside a browser too, so nothing else may assume these
|
|
6
|
+
* APIs exist: where they do not, this and {@link watchActivity} return
|
|
7
|
+
* `undefined` and hook nothing.
|
|
8
|
+
*
|
|
9
|
+
* A backgrounded tab has its timers throttled, so a session's scheduled refresh
|
|
10
|
+
* may not run and its delegation lapses while nobody is looking. Coming back to
|
|
11
|
+
* the tab happens a second or two before the user's next click, which is room
|
|
12
|
+
* enough to mint in. Whether a mint is actually due is not decided here.
|
|
13
|
+
* @param onForeground - Invoked when the page becomes visible or regains focus.
|
|
14
|
+
* @returns A function that unhooks the listeners, or `undefined` where there is
|
|
15
|
+
* no DOM to hook.
|
|
16
|
+
*/
|
|
17
|
+
export declare function watchForeground(onForeground: () => void): (() => void) | undefined;
|
|
18
|
+
/**
|
|
19
|
+
* Calls back when somebody is using the page.
|
|
20
|
+
*
|
|
21
|
+
* A user moving a mouse is very likely about to do something that needs a
|
|
22
|
+
* delegation, so this is the same claim {@link watchForeground} makes — this
|
|
23
|
+
* page is in front of a person right now — from the other direction. Whether a
|
|
24
|
+
* mint is actually due is not decided here, and the identity's pre-mint
|
|
25
|
+
* threshold is what keeps a resting hand from costing more than one mint per
|
|
26
|
+
* refresh interval.
|
|
27
|
+
*
|
|
28
|
+
* It matters beyond saving the first request a mint: a session bounded by how
|
|
29
|
+
* long it goes unminted would otherwise end under a user who is reading rather
|
|
30
|
+
* than clicking.
|
|
31
|
+
* @param onActivity - Invoked on any sign of use.
|
|
32
|
+
* @returns A function that unhooks the listeners, or `undefined` where there is
|
|
33
|
+
* no DOM to hook.
|
|
34
|
+
*/
|
|
35
|
+
export declare function watchActivity(onActivity: () => void): (() => void) | undefined;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Calls back when the page is shown or the window regains focus.
|
|
3
|
+
*
|
|
4
|
+
* Its own module because it is the one part of the session machinery that needs a
|
|
5
|
+
* DOM. `AuthClient` runs outside a browser too, so nothing else may assume these
|
|
6
|
+
* APIs exist: where they do not, this and {@link watchActivity} return
|
|
7
|
+
* `undefined` and hook nothing.
|
|
8
|
+
*
|
|
9
|
+
* A backgrounded tab has its timers throttled, so a session's scheduled refresh
|
|
10
|
+
* may not run and its delegation lapses while nobody is looking. Coming back to
|
|
11
|
+
* the tab happens a second or two before the user's next click, which is room
|
|
12
|
+
* enough to mint in. Whether a mint is actually due is not decided here.
|
|
13
|
+
* @param onForeground - Invoked when the page becomes visible or regains focus.
|
|
14
|
+
* @returns A function that unhooks the listeners, or `undefined` where there is
|
|
15
|
+
* no DOM to hook.
|
|
16
|
+
*/
|
|
17
|
+
export function watchForeground(onForeground) {
|
|
18
|
+
// Every API this hooks, checked one at a time: a partial DOM polyfill can
|
|
19
|
+
// define `document` without the event methods, and throwing there would
|
|
20
|
+
// contradict the promise that this hooks nothing where there is no DOM.
|
|
21
|
+
if (typeof document === 'undefined' ||
|
|
22
|
+
typeof document.addEventListener !== 'function' ||
|
|
23
|
+
typeof document.removeEventListener !== 'function' ||
|
|
24
|
+
typeof globalThis.addEventListener !== 'function' ||
|
|
25
|
+
typeof globalThis.removeEventListener !== 'function') {
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
const onVisible = () => {
|
|
29
|
+
if (document.visibilityState === 'visible')
|
|
30
|
+
onForeground();
|
|
31
|
+
};
|
|
32
|
+
document.addEventListener('visibilitychange', onVisible);
|
|
33
|
+
// `focus` catches two visible windows side by side, where moving between them
|
|
34
|
+
// is not a visibility change. `pageshow` catches a page restored from the
|
|
35
|
+
// back-forward cache, which resumes with timers that never ran.
|
|
36
|
+
globalThis.addEventListener('focus', onForeground);
|
|
37
|
+
globalThis.addEventListener('pageshow', onForeground);
|
|
38
|
+
return () => {
|
|
39
|
+
document.removeEventListener('visibilitychange', onVisible);
|
|
40
|
+
globalThis.removeEventListener('focus', onForeground);
|
|
41
|
+
globalThis.removeEventListener('pageshow', onForeground);
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
// `pointerdown` covers a mouse, a finger and a pen in one, which is the only
|
|
45
|
+
// press event a phone reliably produces. The list the retired idle timer
|
|
46
|
+
// listened to, put to the opposite use: it ended a session on their absence,
|
|
47
|
+
// and they now keep one alive by their presence.
|
|
48
|
+
const ACTIVITY_EVENTS = ['pointerdown', 'mousedown', 'mousemove', 'keydown', 'touchstart', 'wheel'];
|
|
49
|
+
/**
|
|
50
|
+
* Calls back when somebody is using the page.
|
|
51
|
+
*
|
|
52
|
+
* A user moving a mouse is very likely about to do something that needs a
|
|
53
|
+
* delegation, so this is the same claim {@link watchForeground} makes — this
|
|
54
|
+
* page is in front of a person right now — from the other direction. Whether a
|
|
55
|
+
* mint is actually due is not decided here, and the identity's pre-mint
|
|
56
|
+
* threshold is what keeps a resting hand from costing more than one mint per
|
|
57
|
+
* refresh interval.
|
|
58
|
+
*
|
|
59
|
+
* It matters beyond saving the first request a mint: a session bounded by how
|
|
60
|
+
* long it goes unminted would otherwise end under a user who is reading rather
|
|
61
|
+
* than clicking.
|
|
62
|
+
* @param onActivity - Invoked on any sign of use.
|
|
63
|
+
* @returns A function that unhooks the listeners, or `undefined` where there is
|
|
64
|
+
* no DOM to hook.
|
|
65
|
+
*/
|
|
66
|
+
export function watchActivity(onActivity) {
|
|
67
|
+
if (typeof document === 'undefined' ||
|
|
68
|
+
typeof document.addEventListener !== 'function' ||
|
|
69
|
+
typeof document.removeEventListener !== 'function') {
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
// Passive: these never call `preventDefault`, and saying so keeps a listener on
|
|
73
|
+
// `mousemove` and `wheel` off the path that decides whether a scroll may start.
|
|
74
|
+
for (const event of ACTIVITY_EVENTS) {
|
|
75
|
+
document.addEventListener(event, onActivity, { passive: true });
|
|
76
|
+
}
|
|
77
|
+
return () => {
|
|
78
|
+
for (const event of ACTIVITY_EVENTS) {
|
|
79
|
+
document.removeEventListener(event, onActivity);
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=foreground-refresh.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"foreground-refresh.js","sourceRoot":"","sources":["../../../src/client/foreground-refresh.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,eAAe,CAAC,YAAwB;IACtD,0EAA0E;IAC1E,wEAAwE;IACxE,wEAAwE;IACxE,IACE,OAAO,QAAQ,KAAK,WAAW;QAC/B,OAAO,QAAQ,CAAC,gBAAgB,KAAK,UAAU;QAC/C,OAAO,QAAQ,CAAC,mBAAmB,KAAK,UAAU;QAClD,OAAO,UAAU,CAAC,gBAAgB,KAAK,UAAU;QACjD,OAAO,UAAU,CAAC,mBAAmB,KAAK,UAAU,EACpD,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,SAAS,GAAG,GAAS,EAAE;QAC3B,IAAI,QAAQ,CAAC,eAAe,KAAK,SAAS;YAAE,YAAY,EAAE,CAAC;IAC7D,CAAC,CAAC;IAEF,QAAQ,CAAC,gBAAgB,CAAC,kBAAkB,EAAE,SAAS,CAAC,CAAC;IACzD,8EAA8E;IAC9E,0EAA0E;IAC1E,gEAAgE;IAChE,UAAU,CAAC,gBAAgB,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;IACnD,UAAU,CAAC,gBAAgB,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;IAEtD,OAAO,GAAG,EAAE;QACV,QAAQ,CAAC,mBAAmB,CAAC,kBAAkB,EAAE,SAAS,CAAC,CAAC;QAC5D,UAAU,CAAC,mBAAmB,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;QACtD,UAAU,CAAC,mBAAmB,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;IAC3D,CAAC,CAAC;AACJ,CAAC;AAED,6EAA6E;AAC7E,yEAAyE;AACzE,6EAA6E;AAC7E,iDAAiD;AACjD,MAAM,eAAe,GAAG,CAAC,aAAa,EAAE,WAAW,EAAE,WAAW,EAAE,SAAS,EAAE,YAAY,EAAE,OAAO,CAAC,CAAC;AAEpG;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,aAAa,CAAC,UAAsB;IAClD,IACE,OAAO,QAAQ,KAAK,WAAW;QAC/B,OAAO,QAAQ,CAAC,gBAAgB,KAAK,UAAU;QAC/C,OAAO,QAAQ,CAAC,mBAAmB,KAAK,UAAU,EAClD,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,gFAAgF;IAChF,gFAAgF;IAChF,KAAK,MAAM,KAAK,IAAI,eAAe,EAAE,CAAC;QACpC,QAAQ,CAAC,gBAAgB,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;IAClE,CAAC;IAED,OAAO,GAAG,EAAE;QACV,KAAK,MAAM,KAAK,IAAI,eAAe,EAAE,CAAC;YACpC,QAAQ,CAAC,mBAAmB,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;QAClD,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { ECDSAKeyIdentity } from '@icp-sdk/core/identity';
|
|
2
|
+
import type { Credential, CredentialStorage } from './credential-storage.js';
|
|
3
|
+
import { type DBCreateOptions } from './db.js';
|
|
4
|
+
/**
|
|
5
|
+
* Credentials in IndexedDB, which is what a browser offers that can hold a key
|
|
6
|
+
* whose private half is unreadable.
|
|
7
|
+
*
|
|
8
|
+
* A non-extractable `CryptoKeyPair` survives a structured clone, so what another
|
|
9
|
+
* tab reads is a handle that signs and cannot be exported — no key material is
|
|
10
|
+
* written anywhere.
|
|
11
|
+
* @see implements {@link CredentialStorage}
|
|
12
|
+
*/
|
|
13
|
+
export declare class IdbCredentialStorage implements CredentialStorage<ECDSAKeyIdentity> {
|
|
14
|
+
#private;
|
|
15
|
+
readonly shared = true;
|
|
16
|
+
readonly durable = true;
|
|
17
|
+
/**
|
|
18
|
+
* @param options - Database name, store name and version.
|
|
19
|
+
*/
|
|
20
|
+
constructor(options?: DBCreateOptions);
|
|
21
|
+
create(): Promise<ECDSAKeyIdentity>;
|
|
22
|
+
get(slot: string): Promise<Credential<ECDSAKeyIdentity> | null>;
|
|
23
|
+
set(slot: string, credential: Credential<ECDSAKeyIdentity>): Promise<void>;
|
|
24
|
+
remove(slot: string): Promise<void>;
|
|
25
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { DelegationChain, ECDSAKeyIdentity } from '@icp-sdk/core/identity';
|
|
2
|
+
import { IdbKeyVal } from './db.js';
|
|
3
|
+
/**
|
|
4
|
+
* Credentials in IndexedDB, which is what a browser offers that can hold a key
|
|
5
|
+
* whose private half is unreadable.
|
|
6
|
+
*
|
|
7
|
+
* A non-extractable `CryptoKeyPair` survives a structured clone, so what another
|
|
8
|
+
* tab reads is a handle that signs and cannot be exported — no key material is
|
|
9
|
+
* written anywhere.
|
|
10
|
+
* @see implements {@link CredentialStorage}
|
|
11
|
+
*/
|
|
12
|
+
export class IdbCredentialStorage {
|
|
13
|
+
shared = true;
|
|
14
|
+
durable = true;
|
|
15
|
+
#options;
|
|
16
|
+
/**
|
|
17
|
+
* @param options - Database name, store name and version.
|
|
18
|
+
*/
|
|
19
|
+
constructor(options) {
|
|
20
|
+
this.#options = options ?? {};
|
|
21
|
+
}
|
|
22
|
+
// Opened on first use, and forgotten again if opening failed, so a later call
|
|
23
|
+
// retries rather than reusing a rejected promise.
|
|
24
|
+
#dbPromise = null;
|
|
25
|
+
get #db() {
|
|
26
|
+
if (this.#dbPromise === null) {
|
|
27
|
+
const promise = IdbKeyVal.create(this.#options).catch((error) => {
|
|
28
|
+
if (this.#dbPromise === promise) {
|
|
29
|
+
this.#dbPromise = null;
|
|
30
|
+
}
|
|
31
|
+
throw error;
|
|
32
|
+
});
|
|
33
|
+
this.#dbPromise = promise;
|
|
34
|
+
}
|
|
35
|
+
return this.#dbPromise;
|
|
36
|
+
}
|
|
37
|
+
create() {
|
|
38
|
+
return ECDSAKeyIdentity.generate({ extractable: false });
|
|
39
|
+
}
|
|
40
|
+
async get(slot) {
|
|
41
|
+
const db = await this.#db;
|
|
42
|
+
const stored = await db.get(slot);
|
|
43
|
+
if (!stored)
|
|
44
|
+
return null;
|
|
45
|
+
try {
|
|
46
|
+
const identity = await ECDSAKeyIdentity.fromKeyPair(stored.keyPair);
|
|
47
|
+
const chain = stored.chain === undefined ? undefined : DelegationChain.fromJSON(stored.chain);
|
|
48
|
+
return { identity, chain };
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
// A record written by an incompatible version, or one the browser has
|
|
52
|
+
// truncated. Reporting nothing lets the caller acquire again, which is
|
|
53
|
+
// safer than throwing on a page load.
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
async set(slot, credential) {
|
|
58
|
+
const db = await this.#db;
|
|
59
|
+
await db.set(slot, {
|
|
60
|
+
keyPair: credential.identity.getKeyPair(),
|
|
61
|
+
chain: credential.chain && JSON.stringify(credential.chain.toJSON()),
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
async remove(slot) {
|
|
65
|
+
const db = await this.#db;
|
|
66
|
+
await db.remove(slot);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=idb-credential-storage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"idb-credential-storage.js","sourceRoot":"","sources":["../../../src/client/idb-credential-storage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE3E,OAAO,EAAwB,SAAS,EAAE,MAAM,SAAS,CAAC;AAQ1D;;;;;;;;GAQG;AACH,MAAM,OAAO,oBAAoB;IACf,MAAM,GAAG,IAAI,CAAC;IACd,OAAO,GAAG,IAAI,CAAC;IAE/B,QAAQ,CAAkB;IAE1B;;OAEG;IACH,YAAY,OAAyB;QACnC,IAAI,CAAC,QAAQ,GAAG,OAAO,IAAI,EAAE,CAAC;IAChC,CAAC;IAED,8EAA8E;IAC9E,kDAAkD;IAClD,UAAU,GAA8B,IAAI,CAAC;IAC7C,IAAI,GAAG;QACL,IAAI,IAAI,CAAC,UAAU,KAAK,IAAI,EAAE,CAAC;YAC7B,MAAM,OAAO,GAAG,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;gBAC9D,IAAI,IAAI,CAAC,UAAU,KAAK,OAAO,EAAE,CAAC;oBAChC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;gBACzB,CAAC;gBACD,MAAM,KAAK,CAAC;YACd,CAAC,CAAC,CAAC;YACH,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC;QAC5B,CAAC;QACD,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;IAEM,MAAM;QACX,OAAO,gBAAgB,CAAC,QAAQ,CAAC,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC,CAAC;IAC3D,CAAC;IAEM,KAAK,CAAC,GAAG,CAAC,IAAY;QAC3B,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC;QAC1B,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,GAAG,CAAmB,IAAI,CAAC,CAAC;QACpD,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC;QAEzB,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,gBAAgB,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YACpE,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,eAAe,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAC9F,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;QAC7B,CAAC;QAAC,MAAM,CAAC;YACP,sEAAsE;YACtE,uEAAuE;YACvE,sCAAsC;YACtC,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAEM,KAAK,CAAC,GAAG,CAAC,IAAY,EAAE,UAAwC;QACrE,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC;QAC1B,MAAM,EAAE,CAAC,GAAG,CAAmB,IAAI,EAAE;YACnC,OAAO,EAAE,UAAU,CAAC,QAAQ,CAAC,UAAU,EAAE;YACzC,KAAK,EAAE,UAAU,CAAC,KAAK,IAAI,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;SACrE,CAAC,CAAC;IACL,CAAC;IAEM,KAAK,CAAC,MAAM,CAAC,IAAY;QAC9B,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC;QAC1B,MAAM,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACxB,CAAC;CACF"}
|
|
@@ -1,7 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @module api/client
|
|
3
3
|
*/
|
|
4
|
+
export { type AppDelegationSource, SessionGoneError } from './app-delegation-source.js';
|
|
4
5
|
export * from './auth-client.js';
|
|
5
|
-
export { type
|
|
6
|
-
export
|
|
7
|
-
export {
|
|
6
|
+
export { CookieStateStorage, type CookieStateStorageOptions, } from './cookie-state-storage.js';
|
|
7
|
+
export type { Credential, CredentialStorage } from './credential-storage.js';
|
|
8
|
+
export { DB_VERSION, type DBCreateOptions, IdbKeyVal } from './db.js';
|
|
9
|
+
export { watchActivity, watchForeground } from './foreground-refresh.js';
|
|
10
|
+
export { IdbCredentialStorage } from './idb-credential-storage.js';
|
|
11
|
+
export { LocalCredentialStorage } from './local-credential-storage.js';
|
|
12
|
+
export { MemoryCredentialStorage } from './memory-credential-storage.js';
|
|
13
|
+
export { InteractionRequiredError, requestSessionDelegation } from './session-delegation.js';
|
|
14
|
+
export { SessionIdentity, type SessionIdentityOptions } from './session-identity.js';
|
|
15
|
+
export { SessionMinter, type SessionMinterOptions } from './session-minter.js';
|
|
16
|
+
export { DEFAULT_SHARED_MEMORY_NAME, SharedMemoryCredentialStorage, } from './shared-memory-credential-storage.js';
|
|
17
|
+
export { isValidSsoDomain } from './sso.js';
|
|
18
|
+
export { LocalStateStorage, MemoryStateStorage, type SessionState, type StateStorage, } from './state-storage.js';
|
package/dist/esm/client/index.js
CHANGED
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @module api/client
|
|
3
3
|
*/
|
|
4
|
+
export { SessionGoneError } from './app-delegation-source.js';
|
|
4
5
|
export * from './auth-client.js';
|
|
5
|
-
export {
|
|
6
|
-
export
|
|
7
|
-
export {
|
|
6
|
+
export { CookieStateStorage, } from './cookie-state-storage.js';
|
|
7
|
+
export { DB_VERSION, IdbKeyVal } from './db.js';
|
|
8
|
+
export { watchActivity, watchForeground } from './foreground-refresh.js';
|
|
9
|
+
export { IdbCredentialStorage } from './idb-credential-storage.js';
|
|
10
|
+
export { LocalCredentialStorage } from './local-credential-storage.js';
|
|
11
|
+
export { MemoryCredentialStorage } from './memory-credential-storage.js';
|
|
12
|
+
export { InteractionRequiredError, requestSessionDelegation } from './session-delegation.js';
|
|
13
|
+
export { SessionIdentity } from './session-identity.js';
|
|
14
|
+
export { SessionMinter } from './session-minter.js';
|
|
15
|
+
export { DEFAULT_SHARED_MEMORY_NAME, SharedMemoryCredentialStorage, } from './shared-memory-credential-storage.js';
|
|
16
|
+
export { isValidSsoDomain } from './sso.js';
|
|
17
|
+
export { LocalStateStorage, MemoryStateStorage, } from './state-storage.js';
|
|
8
18
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAwB,SAAS,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAA4B,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AACxF,cAAc,kBAAkB,CAAC;AACjC,OAAO,EACL,kBAAkB,GAEnB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EAAE,UAAU,EAAwB,SAAS,EAAE,MAAM,SAAS,CAAC;AACtE,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AACzE,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AACvE,OAAO,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AACzE,OAAO,EAAE,wBAAwB,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AAC7F,OAAO,EAAE,eAAe,EAA+B,MAAM,uBAAuB,CAAC;AACrF,OAAO,EAAE,aAAa,EAA6B,MAAM,qBAAqB,CAAC;AAC/E,OAAO,EACL,0BAA0B,EAC1B,6BAA6B,GAC9B,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,EACL,iBAAiB,EACjB,kBAAkB,GAGnB,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Ed25519KeyIdentity } from '@icp-sdk/core/identity';
|
|
2
|
+
import type { Credential, CredentialStorage } from './credential-storage.js';
|
|
3
|
+
/**
|
|
4
|
+
* Credentials in `localStorage`, for where IndexedDB is unavailable.
|
|
5
|
+
*
|
|
6
|
+
* `localStorage` holds strings, so the key has to be one: this generates an
|
|
7
|
+
* Ed25519 identity, whose private bytes serialise, rather than the
|
|
8
|
+
* non-extractable key {@link IdbCredentialStorage} uses. That is the trade the
|
|
9
|
+
* medium imposes, and it is why generation belongs to the store.
|
|
10
|
+
* @see implements {@link CredentialStorage}
|
|
11
|
+
*/
|
|
12
|
+
export declare class LocalCredentialStorage implements CredentialStorage<Ed25519KeyIdentity> {
|
|
13
|
+
#private;
|
|
14
|
+
readonly prefix: string;
|
|
15
|
+
readonly shared = true;
|
|
16
|
+
readonly durable = true;
|
|
17
|
+
/**
|
|
18
|
+
* @param prefix - Prepended to every slot, so two clients under one origin can
|
|
19
|
+
* be kept apart.
|
|
20
|
+
*/
|
|
21
|
+
constructor(prefix?: string);
|
|
22
|
+
create(): Promise<Ed25519KeyIdentity>;
|
|
23
|
+
get(slot: string): Promise<Credential<Ed25519KeyIdentity> | null>;
|
|
24
|
+
set(slot: string, credential: Credential<Ed25519KeyIdentity>): Promise<void>;
|
|
25
|
+
remove(slot: string): Promise<void>;
|
|
26
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { DelegationChain, Ed25519KeyIdentity } from '@icp-sdk/core/identity';
|
|
2
|
+
/**
|
|
3
|
+
* Credentials in `localStorage`, for where IndexedDB is unavailable.
|
|
4
|
+
*
|
|
5
|
+
* `localStorage` holds strings, so the key has to be one: this generates an
|
|
6
|
+
* Ed25519 identity, whose private bytes serialise, rather than the
|
|
7
|
+
* non-extractable key {@link IdbCredentialStorage} uses. That is the trade the
|
|
8
|
+
* medium imposes, and it is why generation belongs to the store.
|
|
9
|
+
* @see implements {@link CredentialStorage}
|
|
10
|
+
*/
|
|
11
|
+
export class LocalCredentialStorage {
|
|
12
|
+
prefix;
|
|
13
|
+
shared = true;
|
|
14
|
+
durable = true;
|
|
15
|
+
/**
|
|
16
|
+
* @param prefix - Prepended to every slot, so two clients under one origin can
|
|
17
|
+
* be kept apart.
|
|
18
|
+
*/
|
|
19
|
+
constructor(prefix = 'ic-') {
|
|
20
|
+
this.prefix = prefix;
|
|
21
|
+
}
|
|
22
|
+
create() {
|
|
23
|
+
return Promise.resolve(Ed25519KeyIdentity.generate());
|
|
24
|
+
}
|
|
25
|
+
get(slot) {
|
|
26
|
+
const raw = this.#localStorage().getItem(this.prefix + slot);
|
|
27
|
+
if (raw === null)
|
|
28
|
+
return Promise.resolve(null);
|
|
29
|
+
try {
|
|
30
|
+
const stored = JSON.parse(raw);
|
|
31
|
+
const identity = Ed25519KeyIdentity.fromJSON(stored.key);
|
|
32
|
+
const chain = stored.chain === undefined ? undefined : DelegationChain.fromJSON(stored.chain);
|
|
33
|
+
return Promise.resolve({ identity, chain });
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return Promise.resolve(null);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
set(slot, credential) {
|
|
40
|
+
const stored = {
|
|
41
|
+
key: JSON.stringify(credential.identity.toJSON()),
|
|
42
|
+
chain: credential.chain && JSON.stringify(credential.chain.toJSON()),
|
|
43
|
+
};
|
|
44
|
+
this.#localStorage().setItem(this.prefix + slot, JSON.stringify(stored));
|
|
45
|
+
return Promise.resolve();
|
|
46
|
+
}
|
|
47
|
+
remove(slot) {
|
|
48
|
+
this.#localStorage().removeItem(this.prefix + slot);
|
|
49
|
+
return Promise.resolve();
|
|
50
|
+
}
|
|
51
|
+
#localStorage() {
|
|
52
|
+
const ls = globalThis.localStorage;
|
|
53
|
+
if (!ls) {
|
|
54
|
+
throw new Error('Could not find local storage.');
|
|
55
|
+
}
|
|
56
|
+
return ls;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=local-credential-storage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local-credential-storage.js","sourceRoot":"","sources":["../../../src/client/local-credential-storage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAS7E;;;;;;;;GAQG;AACH,MAAM,OAAO,sBAAsB;IAQL;IAPZ,MAAM,GAAG,IAAI,CAAC;IACd,OAAO,GAAG,IAAI,CAAC;IAE/B;;;OAGG;IACH,YAA4B,SAAS,KAAK;QAAd,WAAM,GAAN,MAAM,CAAQ;IAAG,CAAC;IAEvC,MAAM;QACX,OAAO,OAAO,CAAC,OAAO,CAAC,kBAAkB,CAAC,QAAQ,EAAE,CAAC,CAAC;IACxD,CAAC;IAEM,GAAG,CAAC,IAAY;QACrB,MAAM,GAAG,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;QAC7D,IAAI,GAAG,KAAK,IAAI;YAAE,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAE/C,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAqB,CAAC;YACnD,MAAM,QAAQ,GAAG,kBAAkB,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACzD,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,eAAe,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAC9F,OAAO,OAAO,CAAC,OAAO,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC;QAC9C,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC/B,CAAC;IACH,CAAC;IAEM,GAAG,CAAC,IAAY,EAAE,UAA0C;QACjE,MAAM,MAAM,GAAqB;YAC/B,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;YACjD,KAAK,EAAE,UAAU,CAAC,KAAK,IAAI,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;SACrE,CAAC;QACF,IAAI,CAAC,aAAa,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,GAAG,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;QACzE,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;IAC3B,CAAC;IAEM,MAAM,CAAC,IAAY;QACxB,IAAI,CAAC,aAAa,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;QACpD,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;IAC3B,CAAC;IAED,aAAa;QACX,MAAM,EAAE,GAAG,UAAU,CAAC,YAAY,CAAC;QACnC,IAAI,CAAC,EAAE,EAAE,CAAC;YACR,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;QACnD,CAAC;QACD,OAAO,EAAE,CAAC;IACZ,CAAC;CACF"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { ECDSAKeyIdentity } from '@icp-sdk/core/identity';
|
|
2
|
+
import type { Credential, CredentialStorage } from './credential-storage.js';
|
|
3
|
+
/**
|
|
4
|
+
* Credentials held for the lifetime of the instance and shared with nothing.
|
|
5
|
+
*
|
|
6
|
+
* For tests and for environments with neither IndexedDB nor `localStorage`. A
|
|
7
|
+
* reload starts from nothing, and other tabs see nothing, so every tab of an
|
|
8
|
+
* origin is alone.
|
|
9
|
+
*
|
|
10
|
+
* Records are handed back as they were given rather than round-tripped through
|
|
11
|
+
* an encoding, which is what lets this hold a non-extractable key at all.
|
|
12
|
+
* @see implements {@link CredentialStorage}
|
|
13
|
+
*/
|
|
14
|
+
export declare class MemoryCredentialStorage implements CredentialStorage<ECDSAKeyIdentity> {
|
|
15
|
+
#private;
|
|
16
|
+
readonly shared = false;
|
|
17
|
+
readonly durable = false;
|
|
18
|
+
create(): Promise<ECDSAKeyIdentity>;
|
|
19
|
+
get(slot: string): Promise<Credential<ECDSAKeyIdentity> | null>;
|
|
20
|
+
set(slot: string, credential: Credential<ECDSAKeyIdentity>): Promise<void>;
|
|
21
|
+
remove(slot: string): Promise<void>;
|
|
22
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { ECDSAKeyIdentity } from '@icp-sdk/core/identity';
|
|
2
|
+
/**
|
|
3
|
+
* Credentials held for the lifetime of the instance and shared with nothing.
|
|
4
|
+
*
|
|
5
|
+
* For tests and for environments with neither IndexedDB nor `localStorage`. A
|
|
6
|
+
* reload starts from nothing, and other tabs see nothing, so every tab of an
|
|
7
|
+
* origin is alone.
|
|
8
|
+
*
|
|
9
|
+
* Records are handed back as they were given rather than round-tripped through
|
|
10
|
+
* an encoding, which is what lets this hold a non-extractable key at all.
|
|
11
|
+
* @see implements {@link CredentialStorage}
|
|
12
|
+
*/
|
|
13
|
+
export class MemoryCredentialStorage {
|
|
14
|
+
shared = false;
|
|
15
|
+
durable = false;
|
|
16
|
+
// On the instance and never on the module: two clients on one page must not
|
|
17
|
+
// see each other's slots, and no state may carry between tests in one process.
|
|
18
|
+
#credentials = new Map();
|
|
19
|
+
create() {
|
|
20
|
+
// Nothing is serialised here, so the key never has to be readable.
|
|
21
|
+
return ECDSAKeyIdentity.generate({ extractable: false });
|
|
22
|
+
}
|
|
23
|
+
get(slot) {
|
|
24
|
+
return Promise.resolve(this.#credentials.get(slot) ?? null);
|
|
25
|
+
}
|
|
26
|
+
set(slot, credential) {
|
|
27
|
+
this.#credentials.set(slot, credential);
|
|
28
|
+
return Promise.resolve();
|
|
29
|
+
}
|
|
30
|
+
remove(slot) {
|
|
31
|
+
this.#credentials.delete(slot);
|
|
32
|
+
return Promise.resolve();
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=memory-credential-storage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-credential-storage.js","sourceRoot":"","sources":["../../../src/client/memory-credential-storage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAG1D;;;;;;;;;;GAUG;AACH,MAAM,OAAO,uBAAuB;IAClB,MAAM,GAAG,KAAK,CAAC;IACf,OAAO,GAAG,KAAK,CAAC;IAEhC,4EAA4E;IAC5E,+EAA+E;IAC/E,YAAY,GAAG,IAAI,GAAG,EAAwC,CAAC;IAExD,MAAM;QACX,mEAAmE;QACnE,OAAO,gBAAgB,CAAC,QAAQ,CAAC,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC,CAAC;IAC3D,CAAC;IAEM,GAAG,CAAC,IAAY;QACrB,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC;IAC9D,CAAC;IAEM,GAAG,CAAC,IAAY,EAAE,UAAwC;QAC/D,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;QACxC,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;IAC3B,CAAC;IAEM,MAAM,CAAC,IAAY;QACxB,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAC/B,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;IAC3B,CAAC;CACF"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { DerEncodedPublicKey } from '@icp-sdk/core/agent';
|
|
2
|
+
import { DelegationChain } from '@icp-sdk/core/identity';
|
|
3
|
+
import type { Signer } from '@icp-sdk/signer';
|
|
4
|
+
/**
|
|
5
|
+
* Internet Identity had nothing to answer a silent request from.
|
|
6
|
+
*
|
|
7
|
+
* The one denial that is not a failure: it says a ceremony is needed, so the
|
|
8
|
+
* caller signs in interactively rather than retrying. It is also the only
|
|
9
|
+
* dependable evidence that a shared record is stale rather than replaced, which
|
|
10
|
+
* is what lets an origin retract a record it did not write.
|
|
11
|
+
*
|
|
12
|
+
* Thrown only for a request that asked not to render anything. An interactive
|
|
13
|
+
* sign-in that the user abandons is a different outcome and is not this.
|
|
14
|
+
*/
|
|
15
|
+
export declare class InteractionRequiredError extends Error {
|
|
16
|
+
/**
|
|
17
|
+
* Why, where Internet Identity said: `login_required` where it holds no
|
|
18
|
+
* session, `account_selection_required` where it holds more than one and the
|
|
19
|
+
* request named none.
|
|
20
|
+
*
|
|
21
|
+
* A caller may word its prompt from this and does not have to branch on it, so
|
|
22
|
+
* it is deliberately a string rather than a union — a value this library does
|
|
23
|
+
* not know is not a reason to fail.
|
|
24
|
+
*/
|
|
25
|
+
readonly reason: string | undefined;
|
|
26
|
+
constructor(message: string, reason?: string);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Asks the identity provider for a session, and returns the chain it signed.
|
|
30
|
+
*
|
|
31
|
+
* The chain is restricted to the Internet Identity canister, so it is not
|
|
32
|
+
* something an application can call its own canisters with. It is what app
|
|
33
|
+
* delegations are minted from.
|
|
34
|
+
*/
|
|
35
|
+
export declare function requestSessionDelegation(signer: Signer, params: {
|
|
36
|
+
sessionPublicKey: DerEncodedPublicKey;
|
|
37
|
+
maxTimeToLive?: bigint;
|
|
38
|
+
maxTimeToIdle?: bigint;
|
|
39
|
+
derivationOrigin?: string;
|
|
40
|
+
}): Promise<DelegationChain>;
|