@bounded-sh/client 0.0.44 → 0.0.48
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/auth/crossmint-signer-bridge.d.ts +1 -1
- package/dist/auth/index.d.ts +1 -1
- package/dist/auth/providers/email-auth-provider.d.ts +1 -2
- package/dist/auth/providers/guest-auth-provider.d.ts +1 -1
- package/dist/auth/providers/injected-wallet-provider.d.ts +1 -1
- package/dist/auth/providers/offchain-auth-provider.d.ts +3 -3
- package/dist/auth/providers/phantom-wallet-provider.d.ts +1 -1
- package/dist/auth/providers/privy-expo-provider.d.ts +1 -1
- package/dist/auth/providers/privy-wallet-provider.d.ts +1 -1
- package/dist/auth/providers/sol/sol-utils.d.ts +1 -1
- package/dist/auth/providers/transaction-utils.d.ts +1 -1
- package/dist/email-auth-provider-CTyjwhLb.mjs +99 -0
- package/dist/email-auth-provider-CTyjwhLb.mjs.map +1 -0
- package/dist/email-auth-provider-CmunfauO.js +102 -0
- package/dist/email-auth-provider-CmunfauO.js.map +1 -0
- package/dist/email-auth-provider-DyxZTpx6.js +102 -0
- package/dist/email-auth-provider-DyxZTpx6.js.map +1 -0
- package/dist/email-auth-provider-GRj8IOg2.mjs +99 -0
- package/dist/email-auth-provider-GRj8IOg2.mjs.map +1 -0
- package/dist/guest-auth-provider-B68XT2lU.js +193 -0
- package/dist/guest-auth-provider-B68XT2lU.js.map +1 -0
- package/dist/guest-auth-provider-C4BuTNIa.mjs +191 -0
- package/dist/guest-auth-provider-C4BuTNIa.mjs.map +1 -0
- package/dist/guest-auth-provider-D54uHbov.js +193 -0
- package/dist/guest-auth-provider-D54uHbov.js.map +1 -0
- package/dist/guest-auth-provider-Dk_km5tF.mjs +191 -0
- package/dist/guest-auth-provider-Dk_km5tF.mjs.map +1 -0
- package/dist/index-C0pR5b7j.mjs +5450 -0
- package/dist/index-C0pR5b7j.mjs.map +1 -0
- package/dist/index-JBeYYsGm.js +5508 -0
- package/dist/index-JBeYYsGm.js.map +1 -0
- package/dist/index.js +82 -6378
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2 -6283
- package/dist/index.mjs.map +1 -1
- package/dist/index.native-BZCc0gO-.js +5353 -0
- package/dist/index.native-BZCc0gO-.js.map +1 -0
- package/dist/index.native-CytJPNQV.mjs +5299 -0
- package/dist/index.native-CytJPNQV.mjs.map +1 -0
- package/dist/index.native.js +78 -6308
- package/dist/index.native.js.map +1 -1
- package/dist/index.native.mjs +2 -6217
- package/dist/index.native.mjs.map +1 -1
- package/dist/injected-evm-wallet-provider-BOMLGGHM.mjs +406 -0
- package/dist/injected-evm-wallet-provider-BOMLGGHM.mjs.map +1 -0
- package/dist/injected-evm-wallet-provider-CxAiNN7R.mjs +340 -0
- package/dist/injected-evm-wallet-provider-CxAiNN7R.mjs.map +1 -0
- package/dist/injected-evm-wallet-provider-T-EF9C3W.js +409 -0
- package/dist/injected-evm-wallet-provider-T-EF9C3W.js.map +1 -0
- package/dist/injected-evm-wallet-provider-_eeYB1ww.js +343 -0
- package/dist/injected-evm-wallet-provider-_eeYB1ww.js.map +1 -0
- package/dist/injected-wallet-provider-B1uq3r4r.mjs +260 -0
- package/dist/injected-wallet-provider-B1uq3r4r.mjs.map +1 -0
- package/dist/injected-wallet-provider-B2T7JEvn.js +263 -0
- package/dist/injected-wallet-provider-B2T7JEvn.js.map +1 -0
- package/dist/injected-wallet-provider-BBZym7YQ.js +282 -0
- package/dist/injected-wallet-provider-BBZym7YQ.js.map +1 -0
- package/dist/injected-wallet-provider-DCwygA3j.mjs +279 -0
- package/dist/injected-wallet-provider-DCwygA3j.mjs.map +1 -0
- package/package.json +10 -5
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var core = require('@bounded-sh/core');
|
|
4
|
+
var index_native = require('./index.native-BZCc0gO-.js');
|
|
5
|
+
require('react');
|
|
6
|
+
|
|
7
|
+
// guest-keystore.ts — durable per-device keypair for anonymous ("guest") auth.
|
|
8
|
+
//
|
|
9
|
+
// A guest is an ed25519 keypair generated on the device. Its public key is the
|
|
10
|
+
// Bounded account address; it signs the auth challenge exactly like a wallet.
|
|
11
|
+
//
|
|
12
|
+
// Guest auth has one backend: WebCrypto Ed25519 + IndexedDB. The key is
|
|
13
|
+
// NON-EXTRACTABLE: an XSS can ask it to sign but can never read the private
|
|
14
|
+
// bytes. If WebCrypto or IndexedDB is unavailable, guest auth fails closed
|
|
15
|
+
// instead of silently creating an extractable/stored-secret credential.
|
|
16
|
+
//
|
|
17
|
+
// The public output is a base58 address + a base64
|
|
18
|
+
// detached signature, which is the format the session endpoint already accepts.
|
|
19
|
+
const LEGACY_STORED_SECRET_KEY = 'bounded_guest_key_v1';
|
|
20
|
+
const IDB_NAME = 'bounded-guest';
|
|
21
|
+
const IDB_STORE = 'keys';
|
|
22
|
+
const IDB_KEY = 'ed25519';
|
|
23
|
+
const GUEST_KEY_REQUIREMENT = 'Guest auth requires non-extractable WebCrypto Ed25519 keys persisted in IndexedDB; ' +
|
|
24
|
+
'stored-secret guest credentials are disabled.';
|
|
25
|
+
function toBase64(bytes) {
|
|
26
|
+
let bin = '';
|
|
27
|
+
for (let i = 0; i < bytes.length; i++)
|
|
28
|
+
bin += String.fromCharCode(bytes[i]);
|
|
29
|
+
// btoa exists in browsers + RN (Hermes) ; Buffer is the Node/SSR fallback.
|
|
30
|
+
return typeof btoa !== 'undefined' ? btoa(bin) : Buffer.from(bytes).toString('base64');
|
|
31
|
+
}
|
|
32
|
+
// ----- WebCrypto (non-extractable) backend -----------------------------------
|
|
33
|
+
function subtle() {
|
|
34
|
+
const c = globalThis === null || globalThis === void 0 ? void 0 : globalThis.crypto;
|
|
35
|
+
return c && c.subtle ? c.subtle : null;
|
|
36
|
+
}
|
|
37
|
+
function idb() {
|
|
38
|
+
return new Promise((resolve, reject) => {
|
|
39
|
+
const req = globalThis.indexedDB.open(IDB_NAME, 1);
|
|
40
|
+
req.onupgradeneeded = () => req.result.createObjectStore(IDB_STORE);
|
|
41
|
+
req.onsuccess = () => resolve(req.result);
|
|
42
|
+
req.onerror = () => reject(req.error);
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
function idbGet(db, key) {
|
|
46
|
+
return new Promise((resolve, reject) => {
|
|
47
|
+
const r = db.transaction(IDB_STORE, 'readonly').objectStore(IDB_STORE).get(key);
|
|
48
|
+
r.onsuccess = () => resolve(r.result);
|
|
49
|
+
r.onerror = () => reject(r.error);
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
function idbPut(db, key, val) {
|
|
53
|
+
return new Promise((resolve, reject) => {
|
|
54
|
+
const r = db.transaction(IDB_STORE, 'readwrite').objectStore(IDB_STORE).put(val, key);
|
|
55
|
+
r.onsuccess = () => resolve();
|
|
56
|
+
r.onerror = () => reject(r.error);
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
function guestKeyUnavailable(cause) {
|
|
60
|
+
const detail = cause instanceof Error && cause.message ? ` Cause: ${cause.message}` : '';
|
|
61
|
+
return new Error(`${GUEST_KEY_REQUIREMENT}${detail}`);
|
|
62
|
+
}
|
|
63
|
+
async function webCryptoKey() {
|
|
64
|
+
const s = subtle();
|
|
65
|
+
if (!s)
|
|
66
|
+
throw guestKeyUnavailable(new Error('WebCrypto subtle crypto is unavailable.'));
|
|
67
|
+
if (typeof globalThis.indexedDB === 'undefined') {
|
|
68
|
+
throw guestKeyUnavailable(new Error('IndexedDB is unavailable.'));
|
|
69
|
+
}
|
|
70
|
+
try {
|
|
71
|
+
const db = await idb();
|
|
72
|
+
let pair = (await idbGet(db, IDB_KEY));
|
|
73
|
+
if (!pair) {
|
|
74
|
+
// Probe Ed25519 support by generating (throws on unsupported engines).
|
|
75
|
+
pair = (await s.generateKey({ name: 'Ed25519' }, /* extractable */ false, [
|
|
76
|
+
'sign',
|
|
77
|
+
'verify',
|
|
78
|
+
]));
|
|
79
|
+
await idbPut(db, IDB_KEY, pair); // CryptoKey is structured-cloneable; bytes never exposed
|
|
80
|
+
}
|
|
81
|
+
const raw = new Uint8Array(await s.exportKey('raw', pair.publicKey)); // public is exportable
|
|
82
|
+
const address = index_native.bs58.encode(raw);
|
|
83
|
+
return {
|
|
84
|
+
address,
|
|
85
|
+
nonExtractable: true,
|
|
86
|
+
async sign(message) {
|
|
87
|
+
const data = new TextEncoder().encode(message);
|
|
88
|
+
const sig = new Uint8Array(await s.sign({ name: 'Ed25519' }, pair.privateKey, data));
|
|
89
|
+
return toBase64(sig);
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
throw guestKeyUnavailable(error);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
let cached = null;
|
|
98
|
+
/** Get (or create) this device's durable guest key. */
|
|
99
|
+
async function getGuestKey() {
|
|
100
|
+
if (cached)
|
|
101
|
+
return cached;
|
|
102
|
+
cached = await webCryptoKey();
|
|
103
|
+
return cached;
|
|
104
|
+
}
|
|
105
|
+
/** Forget the guest key on this device (a fresh signInAnonymously mints a new one). */
|
|
106
|
+
async function clearGuestKey() {
|
|
107
|
+
cached = null;
|
|
108
|
+
try {
|
|
109
|
+
index_native.getPlatform().storage.removeItem(LEGACY_STORED_SECRET_KEY);
|
|
110
|
+
}
|
|
111
|
+
catch (_a) { }
|
|
112
|
+
try {
|
|
113
|
+
if (typeof globalThis.indexedDB !== 'undefined') {
|
|
114
|
+
const db = await idb();
|
|
115
|
+
await new Promise((res) => {
|
|
116
|
+
const r = db.transaction(IDB_STORE, 'readwrite').objectStore(IDB_STORE).delete(IDB_KEY);
|
|
117
|
+
r.onsuccess = () => res();
|
|
118
|
+
r.onerror = () => res();
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
catch (_b) { }
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const GUEST_TX_UNSUPPORTED = 'Guest (anonymous) auth is offchain-only — it cannot sign or submit Solana transactions. ' +
|
|
126
|
+
'Use a wallet provider for onchain actions.';
|
|
127
|
+
class GuestAuthProvider {
|
|
128
|
+
async login() {
|
|
129
|
+
index_native.setAuthLoading(true);
|
|
130
|
+
try {
|
|
131
|
+
const key = await getGuestKey();
|
|
132
|
+
// Reuse a still-valid session for this exact guest.
|
|
133
|
+
const existing = await core.getActiveSessionManager().getSession();
|
|
134
|
+
if (existing && existing.address === key.address) {
|
|
135
|
+
const user = { address: key.address, isAnonymous: true, provider: this };
|
|
136
|
+
index_native.setCurrentUser(user);
|
|
137
|
+
return user;
|
|
138
|
+
}
|
|
139
|
+
// Mint a fresh session: nonce -> canonical message -> local signature.
|
|
140
|
+
const nonce = await core.genAuthNonce();
|
|
141
|
+
const message = await core.genSolanaMessage(key.address, nonce);
|
|
142
|
+
const signature = await key.sign(message); // base64 detached ed25519
|
|
143
|
+
const result = await core.createSessionWithSignature(key.address, message, signature);
|
|
144
|
+
await core.getActiveSessionManager().storeSession(key.address, result.accessToken, result.idToken, result.refreshToken);
|
|
145
|
+
const user = { address: key.address, isAnonymous: true, provider: this };
|
|
146
|
+
index_native.setCurrentUser(user);
|
|
147
|
+
return user;
|
|
148
|
+
}
|
|
149
|
+
finally {
|
|
150
|
+
index_native.setAuthLoading(false);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
async restoreSession() {
|
|
154
|
+
const session = await core.getActiveSessionManager().getSession();
|
|
155
|
+
if (!session)
|
|
156
|
+
return null;
|
|
157
|
+
const user = { address: session.address, isAnonymous: true, provider: this };
|
|
158
|
+
index_native.setCurrentUser(user);
|
|
159
|
+
return user;
|
|
160
|
+
}
|
|
161
|
+
async logout() {
|
|
162
|
+
// Clears the SESSION but keeps the device key, so logging back in returns the
|
|
163
|
+
// SAME guest identity (and its data). Use forgetGuest() to wipe the key too.
|
|
164
|
+
core.getActiveSessionManager().clearSession();
|
|
165
|
+
index_native.setCurrentUser(null);
|
|
166
|
+
}
|
|
167
|
+
/** Wipe the device key — the next login() is a brand-new guest identity. */
|
|
168
|
+
async forgetGuest() {
|
|
169
|
+
core.getActiveSessionManager().clearSession();
|
|
170
|
+
await clearGuestKey();
|
|
171
|
+
index_native.setCurrentUser(null);
|
|
172
|
+
}
|
|
173
|
+
async signMessage(message) {
|
|
174
|
+
const key = await getGuestKey();
|
|
175
|
+
return key.sign(message);
|
|
176
|
+
}
|
|
177
|
+
// --- offchain guest: no Solana transaction surface -------------------------
|
|
178
|
+
async runTransaction(_evm, _sol, _options) {
|
|
179
|
+
throw new Error(GUEST_TX_UNSUPPORTED);
|
|
180
|
+
}
|
|
181
|
+
async signTransaction(_t) {
|
|
182
|
+
throw new Error(GUEST_TX_UNSUPPORTED);
|
|
183
|
+
}
|
|
184
|
+
async signAndSubmitTransaction() {
|
|
185
|
+
throw new Error(GUEST_TX_UNSUPPORTED);
|
|
186
|
+
}
|
|
187
|
+
async getNativeMethods() {
|
|
188
|
+
return {};
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
exports.GuestAuthProvider = GuestAuthProvider;
|
|
193
|
+
//# sourceMappingURL=guest-auth-provider-B68XT2lU.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guest-auth-provider-B68XT2lU.js","sources":["../.rollup-tmp/auth/providers/guest-keystore.js","../.rollup-tmp/auth/providers/guest-auth-provider.js"],"sourcesContent":["// guest-keystore.ts — durable per-device keypair for anonymous (\"guest\") auth.\n//\n// A guest is an ed25519 keypair generated on the device. Its public key is the\n// Bounded account address; it signs the auth challenge exactly like a wallet.\n//\n// Guest auth has one backend: WebCrypto Ed25519 + IndexedDB. The key is\n// NON-EXTRACTABLE: an XSS can ask it to sign but can never read the private\n// bytes. If WebCrypto or IndexedDB is unavailable, guest auth fails closed\n// instead of silently creating an extractable/stored-secret credential.\n//\n// The public output is a base58 address + a base64\n// detached signature, which is the format the session endpoint already accepts.\nimport bs58 from 'bs58';\nimport { getPlatform } from '../../platform';\nconst LEGACY_STORED_SECRET_KEY = 'bounded_guest_key_v1';\nconst IDB_NAME = 'bounded-guest';\nconst IDB_STORE = 'keys';\nconst IDB_KEY = 'ed25519';\nconst GUEST_KEY_REQUIREMENT = 'Guest auth requires non-extractable WebCrypto Ed25519 keys persisted in IndexedDB; ' +\n 'stored-secret guest credentials are disabled.';\nfunction toBase64(bytes) {\n let bin = '';\n for (let i = 0; i < bytes.length; i++)\n bin += String.fromCharCode(bytes[i]);\n // btoa exists in browsers + RN (Hermes) ; Buffer is the Node/SSR fallback.\n return typeof btoa !== 'undefined' ? btoa(bin) : Buffer.from(bytes).toString('base64');\n}\n// ----- WebCrypto (non-extractable) backend -----------------------------------\nfunction subtle() {\n const c = globalThis === null || globalThis === void 0 ? void 0 : globalThis.crypto;\n return c && c.subtle ? c.subtle : null;\n}\nfunction idb() {\n return new Promise((resolve, reject) => {\n const req = globalThis.indexedDB.open(IDB_NAME, 1);\n req.onupgradeneeded = () => req.result.createObjectStore(IDB_STORE);\n req.onsuccess = () => resolve(req.result);\n req.onerror = () => reject(req.error);\n });\n}\nfunction idbGet(db, key) {\n return new Promise((resolve, reject) => {\n const r = db.transaction(IDB_STORE, 'readonly').objectStore(IDB_STORE).get(key);\n r.onsuccess = () => resolve(r.result);\n r.onerror = () => reject(r.error);\n });\n}\nfunction idbPut(db, key, val) {\n return new Promise((resolve, reject) => {\n const r = db.transaction(IDB_STORE, 'readwrite').objectStore(IDB_STORE).put(val, key);\n r.onsuccess = () => resolve();\n r.onerror = () => reject(r.error);\n });\n}\nfunction guestKeyUnavailable(cause) {\n const detail = cause instanceof Error && cause.message ? ` Cause: ${cause.message}` : '';\n return new Error(`${GUEST_KEY_REQUIREMENT}${detail}`);\n}\nasync function webCryptoKey() {\n const s = subtle();\n if (!s)\n throw guestKeyUnavailable(new Error('WebCrypto subtle crypto is unavailable.'));\n if (typeof globalThis.indexedDB === 'undefined') {\n throw guestKeyUnavailable(new Error('IndexedDB is unavailable.'));\n }\n try {\n const db = await idb();\n let pair = (await idbGet(db, IDB_KEY));\n if (!pair) {\n // Probe Ed25519 support by generating (throws on unsupported engines).\n pair = (await s.generateKey({ name: 'Ed25519' }, /* extractable */ false, [\n 'sign',\n 'verify',\n ]));\n await idbPut(db, IDB_KEY, pair); // CryptoKey is structured-cloneable; bytes never exposed\n }\n const raw = new Uint8Array(await s.exportKey('raw', pair.publicKey)); // public is exportable\n const address = bs58.encode(raw);\n return {\n address,\n nonExtractable: true,\n async sign(message) {\n const data = new TextEncoder().encode(message);\n const sig = new Uint8Array(await s.sign({ name: 'Ed25519' }, pair.privateKey, data));\n return toBase64(sig);\n },\n };\n }\n catch (error) {\n throw guestKeyUnavailable(error);\n }\n}\nlet cached = null;\n/** Get (or create) this device's durable guest key. */\nexport async function getGuestKey() {\n if (cached)\n return cached;\n cached = await webCryptoKey();\n return cached;\n}\n/** Forget the guest key on this device (a fresh signInAnonymously mints a new one). */\nexport async function clearGuestKey() {\n cached = null;\n try {\n getPlatform().storage.removeItem(LEGACY_STORED_SECRET_KEY);\n }\n catch (_a) { }\n try {\n if (typeof globalThis.indexedDB !== 'undefined') {\n const db = await idb();\n await new Promise((res) => {\n const r = db.transaction(IDB_STORE, 'readwrite').objectStore(IDB_STORE).delete(IDB_KEY);\n r.onsuccess = () => res();\n r.onerror = () => res();\n });\n }\n }\n catch (_b) { }\n}\n","import { getActiveSessionManager as SessionManager, createSessionWithSignature, genAuthNonce, genSolanaMessage, } from '@bounded-sh/core';\nimport { setCurrentUser, setAuthLoading } from '../../global';\nimport { getGuestKey, clearGuestKey } from './guest-keystore';\nconst GUEST_TX_UNSUPPORTED = 'Guest (anonymous) auth is offchain-only — it cannot sign or submit Solana transactions. ' +\n 'Use a wallet provider for onchain actions.';\nexport class GuestAuthProvider {\n async login() {\n setAuthLoading(true);\n try {\n const key = await getGuestKey();\n // Reuse a still-valid session for this exact guest.\n const existing = await SessionManager().getSession();\n if (existing && existing.address === key.address) {\n const user = { address: key.address, isAnonymous: true, provider: this };\n setCurrentUser(user);\n return user;\n }\n // Mint a fresh session: nonce -> canonical message -> local signature.\n const nonce = await genAuthNonce();\n const message = await genSolanaMessage(key.address, nonce);\n const signature = await key.sign(message); // base64 detached ed25519\n const result = await createSessionWithSignature(key.address, message, signature);\n await SessionManager().storeSession(key.address, result.accessToken, result.idToken, result.refreshToken);\n const user = { address: key.address, isAnonymous: true, provider: this };\n setCurrentUser(user);\n return user;\n }\n finally {\n setAuthLoading(false);\n }\n }\n async restoreSession() {\n const session = await SessionManager().getSession();\n if (!session)\n return null;\n const user = { address: session.address, isAnonymous: true, provider: this };\n setCurrentUser(user);\n return user;\n }\n async logout() {\n // Clears the SESSION but keeps the device key, so logging back in returns the\n // SAME guest identity (and its data). Use forgetGuest() to wipe the key too.\n SessionManager().clearSession();\n setCurrentUser(null);\n }\n /** Wipe the device key — the next login() is a brand-new guest identity. */\n async forgetGuest() {\n SessionManager().clearSession();\n await clearGuestKey();\n setCurrentUser(null);\n }\n async signMessage(message) {\n const key = await getGuestKey();\n return key.sign(message);\n }\n // --- offchain guest: no Solana transaction surface -------------------------\n async runTransaction(_evm, _sol, _options) {\n throw new Error(GUEST_TX_UNSUPPORTED);\n }\n async signTransaction(_t) {\n throw new Error(GUEST_TX_UNSUPPORTED);\n }\n async signAndSubmitTransaction() {\n throw new Error(GUEST_TX_UNSUPPORTED);\n }\n async getNativeMethods() {\n return {};\n }\n}\n"],"names":["bs58","getPlatform","setAuthLoading","SessionManager","setCurrentUser","genAuthNonce","genSolanaMessage","createSessionWithSignature"],"mappings":"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"}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { getActiveSessionManager, genAuthNonce, genSolanaMessage, createSessionWithSignature } from '@bounded-sh/core';
|
|
2
|
+
import { g as getPlatform, f as bs58, c as setAuthLoading, s as setCurrentUser } from './index.native-CytJPNQV.mjs';
|
|
3
|
+
import 'react';
|
|
4
|
+
|
|
5
|
+
// guest-keystore.ts — durable per-device keypair for anonymous ("guest") auth.
|
|
6
|
+
//
|
|
7
|
+
// A guest is an ed25519 keypair generated on the device. Its public key is the
|
|
8
|
+
// Bounded account address; it signs the auth challenge exactly like a wallet.
|
|
9
|
+
//
|
|
10
|
+
// Guest auth has one backend: WebCrypto Ed25519 + IndexedDB. The key is
|
|
11
|
+
// NON-EXTRACTABLE: an XSS can ask it to sign but can never read the private
|
|
12
|
+
// bytes. If WebCrypto or IndexedDB is unavailable, guest auth fails closed
|
|
13
|
+
// instead of silently creating an extractable/stored-secret credential.
|
|
14
|
+
//
|
|
15
|
+
// The public output is a base58 address + a base64
|
|
16
|
+
// detached signature, which is the format the session endpoint already accepts.
|
|
17
|
+
const LEGACY_STORED_SECRET_KEY = 'bounded_guest_key_v1';
|
|
18
|
+
const IDB_NAME = 'bounded-guest';
|
|
19
|
+
const IDB_STORE = 'keys';
|
|
20
|
+
const IDB_KEY = 'ed25519';
|
|
21
|
+
const GUEST_KEY_REQUIREMENT = 'Guest auth requires non-extractable WebCrypto Ed25519 keys persisted in IndexedDB; ' +
|
|
22
|
+
'stored-secret guest credentials are disabled.';
|
|
23
|
+
function toBase64(bytes) {
|
|
24
|
+
let bin = '';
|
|
25
|
+
for (let i = 0; i < bytes.length; i++)
|
|
26
|
+
bin += String.fromCharCode(bytes[i]);
|
|
27
|
+
// btoa exists in browsers + RN (Hermes) ; Buffer is the Node/SSR fallback.
|
|
28
|
+
return typeof btoa !== 'undefined' ? btoa(bin) : Buffer.from(bytes).toString('base64');
|
|
29
|
+
}
|
|
30
|
+
// ----- WebCrypto (non-extractable) backend -----------------------------------
|
|
31
|
+
function subtle() {
|
|
32
|
+
const c = globalThis === null || globalThis === void 0 ? void 0 : globalThis.crypto;
|
|
33
|
+
return c && c.subtle ? c.subtle : null;
|
|
34
|
+
}
|
|
35
|
+
function idb() {
|
|
36
|
+
return new Promise((resolve, reject) => {
|
|
37
|
+
const req = globalThis.indexedDB.open(IDB_NAME, 1);
|
|
38
|
+
req.onupgradeneeded = () => req.result.createObjectStore(IDB_STORE);
|
|
39
|
+
req.onsuccess = () => resolve(req.result);
|
|
40
|
+
req.onerror = () => reject(req.error);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
function idbGet(db, key) {
|
|
44
|
+
return new Promise((resolve, reject) => {
|
|
45
|
+
const r = db.transaction(IDB_STORE, 'readonly').objectStore(IDB_STORE).get(key);
|
|
46
|
+
r.onsuccess = () => resolve(r.result);
|
|
47
|
+
r.onerror = () => reject(r.error);
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
function idbPut(db, key, val) {
|
|
51
|
+
return new Promise((resolve, reject) => {
|
|
52
|
+
const r = db.transaction(IDB_STORE, 'readwrite').objectStore(IDB_STORE).put(val, key);
|
|
53
|
+
r.onsuccess = () => resolve();
|
|
54
|
+
r.onerror = () => reject(r.error);
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
function guestKeyUnavailable(cause) {
|
|
58
|
+
const detail = cause instanceof Error && cause.message ? ` Cause: ${cause.message}` : '';
|
|
59
|
+
return new Error(`${GUEST_KEY_REQUIREMENT}${detail}`);
|
|
60
|
+
}
|
|
61
|
+
async function webCryptoKey() {
|
|
62
|
+
const s = subtle();
|
|
63
|
+
if (!s)
|
|
64
|
+
throw guestKeyUnavailable(new Error('WebCrypto subtle crypto is unavailable.'));
|
|
65
|
+
if (typeof globalThis.indexedDB === 'undefined') {
|
|
66
|
+
throw guestKeyUnavailable(new Error('IndexedDB is unavailable.'));
|
|
67
|
+
}
|
|
68
|
+
try {
|
|
69
|
+
const db = await idb();
|
|
70
|
+
let pair = (await idbGet(db, IDB_KEY));
|
|
71
|
+
if (!pair) {
|
|
72
|
+
// Probe Ed25519 support by generating (throws on unsupported engines).
|
|
73
|
+
pair = (await s.generateKey({ name: 'Ed25519' }, /* extractable */ false, [
|
|
74
|
+
'sign',
|
|
75
|
+
'verify',
|
|
76
|
+
]));
|
|
77
|
+
await idbPut(db, IDB_KEY, pair); // CryptoKey is structured-cloneable; bytes never exposed
|
|
78
|
+
}
|
|
79
|
+
const raw = new Uint8Array(await s.exportKey('raw', pair.publicKey)); // public is exportable
|
|
80
|
+
const address = bs58.encode(raw);
|
|
81
|
+
return {
|
|
82
|
+
address,
|
|
83
|
+
nonExtractable: true,
|
|
84
|
+
async sign(message) {
|
|
85
|
+
const data = new TextEncoder().encode(message);
|
|
86
|
+
const sig = new Uint8Array(await s.sign({ name: 'Ed25519' }, pair.privateKey, data));
|
|
87
|
+
return toBase64(sig);
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
throw guestKeyUnavailable(error);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
let cached = null;
|
|
96
|
+
/** Get (or create) this device's durable guest key. */
|
|
97
|
+
async function getGuestKey() {
|
|
98
|
+
if (cached)
|
|
99
|
+
return cached;
|
|
100
|
+
cached = await webCryptoKey();
|
|
101
|
+
return cached;
|
|
102
|
+
}
|
|
103
|
+
/** Forget the guest key on this device (a fresh signInAnonymously mints a new one). */
|
|
104
|
+
async function clearGuestKey() {
|
|
105
|
+
cached = null;
|
|
106
|
+
try {
|
|
107
|
+
getPlatform().storage.removeItem(LEGACY_STORED_SECRET_KEY);
|
|
108
|
+
}
|
|
109
|
+
catch (_a) { }
|
|
110
|
+
try {
|
|
111
|
+
if (typeof globalThis.indexedDB !== 'undefined') {
|
|
112
|
+
const db = await idb();
|
|
113
|
+
await new Promise((res) => {
|
|
114
|
+
const r = db.transaction(IDB_STORE, 'readwrite').objectStore(IDB_STORE).delete(IDB_KEY);
|
|
115
|
+
r.onsuccess = () => res();
|
|
116
|
+
r.onerror = () => res();
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
catch (_b) { }
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const GUEST_TX_UNSUPPORTED = 'Guest (anonymous) auth is offchain-only — it cannot sign or submit Solana transactions. ' +
|
|
124
|
+
'Use a wallet provider for onchain actions.';
|
|
125
|
+
class GuestAuthProvider {
|
|
126
|
+
async login() {
|
|
127
|
+
setAuthLoading(true);
|
|
128
|
+
try {
|
|
129
|
+
const key = await getGuestKey();
|
|
130
|
+
// Reuse a still-valid session for this exact guest.
|
|
131
|
+
const existing = await getActiveSessionManager().getSession();
|
|
132
|
+
if (existing && existing.address === key.address) {
|
|
133
|
+
const user = { address: key.address, isAnonymous: true, provider: this };
|
|
134
|
+
setCurrentUser(user);
|
|
135
|
+
return user;
|
|
136
|
+
}
|
|
137
|
+
// Mint a fresh session: nonce -> canonical message -> local signature.
|
|
138
|
+
const nonce = await genAuthNonce();
|
|
139
|
+
const message = await genSolanaMessage(key.address, nonce);
|
|
140
|
+
const signature = await key.sign(message); // base64 detached ed25519
|
|
141
|
+
const result = await createSessionWithSignature(key.address, message, signature);
|
|
142
|
+
await getActiveSessionManager().storeSession(key.address, result.accessToken, result.idToken, result.refreshToken);
|
|
143
|
+
const user = { address: key.address, isAnonymous: true, provider: this };
|
|
144
|
+
setCurrentUser(user);
|
|
145
|
+
return user;
|
|
146
|
+
}
|
|
147
|
+
finally {
|
|
148
|
+
setAuthLoading(false);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
async restoreSession() {
|
|
152
|
+
const session = await getActiveSessionManager().getSession();
|
|
153
|
+
if (!session)
|
|
154
|
+
return null;
|
|
155
|
+
const user = { address: session.address, isAnonymous: true, provider: this };
|
|
156
|
+
setCurrentUser(user);
|
|
157
|
+
return user;
|
|
158
|
+
}
|
|
159
|
+
async logout() {
|
|
160
|
+
// Clears the SESSION but keeps the device key, so logging back in returns the
|
|
161
|
+
// SAME guest identity (and its data). Use forgetGuest() to wipe the key too.
|
|
162
|
+
getActiveSessionManager().clearSession();
|
|
163
|
+
setCurrentUser(null);
|
|
164
|
+
}
|
|
165
|
+
/** Wipe the device key — the next login() is a brand-new guest identity. */
|
|
166
|
+
async forgetGuest() {
|
|
167
|
+
getActiveSessionManager().clearSession();
|
|
168
|
+
await clearGuestKey();
|
|
169
|
+
setCurrentUser(null);
|
|
170
|
+
}
|
|
171
|
+
async signMessage(message) {
|
|
172
|
+
const key = await getGuestKey();
|
|
173
|
+
return key.sign(message);
|
|
174
|
+
}
|
|
175
|
+
// --- offchain guest: no Solana transaction surface -------------------------
|
|
176
|
+
async runTransaction(_evm, _sol, _options) {
|
|
177
|
+
throw new Error(GUEST_TX_UNSUPPORTED);
|
|
178
|
+
}
|
|
179
|
+
async signTransaction(_t) {
|
|
180
|
+
throw new Error(GUEST_TX_UNSUPPORTED);
|
|
181
|
+
}
|
|
182
|
+
async signAndSubmitTransaction() {
|
|
183
|
+
throw new Error(GUEST_TX_UNSUPPORTED);
|
|
184
|
+
}
|
|
185
|
+
async getNativeMethods() {
|
|
186
|
+
return {};
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export { GuestAuthProvider };
|
|
191
|
+
//# sourceMappingURL=guest-auth-provider-C4BuTNIa.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guest-auth-provider-C4BuTNIa.mjs","sources":["../.rollup-tmp/auth/providers/guest-keystore.js","../.rollup-tmp/auth/providers/guest-auth-provider.js"],"sourcesContent":["// guest-keystore.ts — durable per-device keypair for anonymous (\"guest\") auth.\n//\n// A guest is an ed25519 keypair generated on the device. Its public key is the\n// Bounded account address; it signs the auth challenge exactly like a wallet.\n//\n// Guest auth has one backend: WebCrypto Ed25519 + IndexedDB. The key is\n// NON-EXTRACTABLE: an XSS can ask it to sign but can never read the private\n// bytes. If WebCrypto or IndexedDB is unavailable, guest auth fails closed\n// instead of silently creating an extractable/stored-secret credential.\n//\n// The public output is a base58 address + a base64\n// detached signature, which is the format the session endpoint already accepts.\nimport bs58 from 'bs58';\nimport { getPlatform } from '../../platform';\nconst LEGACY_STORED_SECRET_KEY = 'bounded_guest_key_v1';\nconst IDB_NAME = 'bounded-guest';\nconst IDB_STORE = 'keys';\nconst IDB_KEY = 'ed25519';\nconst GUEST_KEY_REQUIREMENT = 'Guest auth requires non-extractable WebCrypto Ed25519 keys persisted in IndexedDB; ' +\n 'stored-secret guest credentials are disabled.';\nfunction toBase64(bytes) {\n let bin = '';\n for (let i = 0; i < bytes.length; i++)\n bin += String.fromCharCode(bytes[i]);\n // btoa exists in browsers + RN (Hermes) ; Buffer is the Node/SSR fallback.\n return typeof btoa !== 'undefined' ? btoa(bin) : Buffer.from(bytes).toString('base64');\n}\n// ----- WebCrypto (non-extractable) backend -----------------------------------\nfunction subtle() {\n const c = globalThis === null || globalThis === void 0 ? void 0 : globalThis.crypto;\n return c && c.subtle ? c.subtle : null;\n}\nfunction idb() {\n return new Promise((resolve, reject) => {\n const req = globalThis.indexedDB.open(IDB_NAME, 1);\n req.onupgradeneeded = () => req.result.createObjectStore(IDB_STORE);\n req.onsuccess = () => resolve(req.result);\n req.onerror = () => reject(req.error);\n });\n}\nfunction idbGet(db, key) {\n return new Promise((resolve, reject) => {\n const r = db.transaction(IDB_STORE, 'readonly').objectStore(IDB_STORE).get(key);\n r.onsuccess = () => resolve(r.result);\n r.onerror = () => reject(r.error);\n });\n}\nfunction idbPut(db, key, val) {\n return new Promise((resolve, reject) => {\n const r = db.transaction(IDB_STORE, 'readwrite').objectStore(IDB_STORE).put(val, key);\n r.onsuccess = () => resolve();\n r.onerror = () => reject(r.error);\n });\n}\nfunction guestKeyUnavailable(cause) {\n const detail = cause instanceof Error && cause.message ? ` Cause: ${cause.message}` : '';\n return new Error(`${GUEST_KEY_REQUIREMENT}${detail}`);\n}\nasync function webCryptoKey() {\n const s = subtle();\n if (!s)\n throw guestKeyUnavailable(new Error('WebCrypto subtle crypto is unavailable.'));\n if (typeof globalThis.indexedDB === 'undefined') {\n throw guestKeyUnavailable(new Error('IndexedDB is unavailable.'));\n }\n try {\n const db = await idb();\n let pair = (await idbGet(db, IDB_KEY));\n if (!pair) {\n // Probe Ed25519 support by generating (throws on unsupported engines).\n pair = (await s.generateKey({ name: 'Ed25519' }, /* extractable */ false, [\n 'sign',\n 'verify',\n ]));\n await idbPut(db, IDB_KEY, pair); // CryptoKey is structured-cloneable; bytes never exposed\n }\n const raw = new Uint8Array(await s.exportKey('raw', pair.publicKey)); // public is exportable\n const address = bs58.encode(raw);\n return {\n address,\n nonExtractable: true,\n async sign(message) {\n const data = new TextEncoder().encode(message);\n const sig = new Uint8Array(await s.sign({ name: 'Ed25519' }, pair.privateKey, data));\n return toBase64(sig);\n },\n };\n }\n catch (error) {\n throw guestKeyUnavailable(error);\n }\n}\nlet cached = null;\n/** Get (or create) this device's durable guest key. */\nexport async function getGuestKey() {\n if (cached)\n return cached;\n cached = await webCryptoKey();\n return cached;\n}\n/** Forget the guest key on this device (a fresh signInAnonymously mints a new one). */\nexport async function clearGuestKey() {\n cached = null;\n try {\n getPlatform().storage.removeItem(LEGACY_STORED_SECRET_KEY);\n }\n catch (_a) { }\n try {\n if (typeof globalThis.indexedDB !== 'undefined') {\n const db = await idb();\n await new Promise((res) => {\n const r = db.transaction(IDB_STORE, 'readwrite').objectStore(IDB_STORE).delete(IDB_KEY);\n r.onsuccess = () => res();\n r.onerror = () => res();\n });\n }\n }\n catch (_b) { }\n}\n","import { getActiveSessionManager as SessionManager, createSessionWithSignature, genAuthNonce, genSolanaMessage, } from '@bounded-sh/core';\nimport { setCurrentUser, setAuthLoading } from '../../global';\nimport { getGuestKey, clearGuestKey } from './guest-keystore';\nconst GUEST_TX_UNSUPPORTED = 'Guest (anonymous) auth is offchain-only — it cannot sign or submit Solana transactions. ' +\n 'Use a wallet provider for onchain actions.';\nexport class GuestAuthProvider {\n async login() {\n setAuthLoading(true);\n try {\n const key = await getGuestKey();\n // Reuse a still-valid session for this exact guest.\n const existing = await SessionManager().getSession();\n if (existing && existing.address === key.address) {\n const user = { address: key.address, isAnonymous: true, provider: this };\n setCurrentUser(user);\n return user;\n }\n // Mint a fresh session: nonce -> canonical message -> local signature.\n const nonce = await genAuthNonce();\n const message = await genSolanaMessage(key.address, nonce);\n const signature = await key.sign(message); // base64 detached ed25519\n const result = await createSessionWithSignature(key.address, message, signature);\n await SessionManager().storeSession(key.address, result.accessToken, result.idToken, result.refreshToken);\n const user = { address: key.address, isAnonymous: true, provider: this };\n setCurrentUser(user);\n return user;\n }\n finally {\n setAuthLoading(false);\n }\n }\n async restoreSession() {\n const session = await SessionManager().getSession();\n if (!session)\n return null;\n const user = { address: session.address, isAnonymous: true, provider: this };\n setCurrentUser(user);\n return user;\n }\n async logout() {\n // Clears the SESSION but keeps the device key, so logging back in returns the\n // SAME guest identity (and its data). Use forgetGuest() to wipe the key too.\n SessionManager().clearSession();\n setCurrentUser(null);\n }\n /** Wipe the device key — the next login() is a brand-new guest identity. */\n async forgetGuest() {\n SessionManager().clearSession();\n await clearGuestKey();\n setCurrentUser(null);\n }\n async signMessage(message) {\n const key = await getGuestKey();\n return key.sign(message);\n }\n // --- offchain guest: no Solana transaction surface -------------------------\n async runTransaction(_evm, _sol, _options) {\n throw new Error(GUEST_TX_UNSUPPORTED);\n }\n async signTransaction(_t) {\n throw new Error(GUEST_TX_UNSUPPORTED);\n }\n async signAndSubmitTransaction() {\n throw new Error(GUEST_TX_UNSUPPORTED);\n }\n async getNativeMethods() {\n return {};\n }\n}\n"],"names":["SessionManager"],"mappings":"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"}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var core = require('@bounded-sh/core');
|
|
4
|
+
var index = require('./index-JBeYYsGm.js');
|
|
5
|
+
require('react');
|
|
6
|
+
|
|
7
|
+
// guest-keystore.ts — durable per-device keypair for anonymous ("guest") auth.
|
|
8
|
+
//
|
|
9
|
+
// A guest is an ed25519 keypair generated on the device. Its public key is the
|
|
10
|
+
// Bounded account address; it signs the auth challenge exactly like a wallet.
|
|
11
|
+
//
|
|
12
|
+
// Guest auth has one backend: WebCrypto Ed25519 + IndexedDB. The key is
|
|
13
|
+
// NON-EXTRACTABLE: an XSS can ask it to sign but can never read the private
|
|
14
|
+
// bytes. If WebCrypto or IndexedDB is unavailable, guest auth fails closed
|
|
15
|
+
// instead of silently creating an extractable/stored-secret credential.
|
|
16
|
+
//
|
|
17
|
+
// The public output is a base58 address + a base64
|
|
18
|
+
// detached signature, which is the format the session endpoint already accepts.
|
|
19
|
+
const LEGACY_STORED_SECRET_KEY = 'bounded_guest_key_v1';
|
|
20
|
+
const IDB_NAME = 'bounded-guest';
|
|
21
|
+
const IDB_STORE = 'keys';
|
|
22
|
+
const IDB_KEY = 'ed25519';
|
|
23
|
+
const GUEST_KEY_REQUIREMENT = 'Guest auth requires non-extractable WebCrypto Ed25519 keys persisted in IndexedDB; ' +
|
|
24
|
+
'stored-secret guest credentials are disabled.';
|
|
25
|
+
function toBase64(bytes) {
|
|
26
|
+
let bin = '';
|
|
27
|
+
for (let i = 0; i < bytes.length; i++)
|
|
28
|
+
bin += String.fromCharCode(bytes[i]);
|
|
29
|
+
// btoa exists in browsers + RN (Hermes) ; Buffer is the Node/SSR fallback.
|
|
30
|
+
return typeof btoa !== 'undefined' ? btoa(bin) : Buffer.from(bytes).toString('base64');
|
|
31
|
+
}
|
|
32
|
+
// ----- WebCrypto (non-extractable) backend -----------------------------------
|
|
33
|
+
function subtle() {
|
|
34
|
+
const c = globalThis === null || globalThis === void 0 ? void 0 : globalThis.crypto;
|
|
35
|
+
return c && c.subtle ? c.subtle : null;
|
|
36
|
+
}
|
|
37
|
+
function idb() {
|
|
38
|
+
return new Promise((resolve, reject) => {
|
|
39
|
+
const req = globalThis.indexedDB.open(IDB_NAME, 1);
|
|
40
|
+
req.onupgradeneeded = () => req.result.createObjectStore(IDB_STORE);
|
|
41
|
+
req.onsuccess = () => resolve(req.result);
|
|
42
|
+
req.onerror = () => reject(req.error);
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
function idbGet(db, key) {
|
|
46
|
+
return new Promise((resolve, reject) => {
|
|
47
|
+
const r = db.transaction(IDB_STORE, 'readonly').objectStore(IDB_STORE).get(key);
|
|
48
|
+
r.onsuccess = () => resolve(r.result);
|
|
49
|
+
r.onerror = () => reject(r.error);
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
function idbPut(db, key, val) {
|
|
53
|
+
return new Promise((resolve, reject) => {
|
|
54
|
+
const r = db.transaction(IDB_STORE, 'readwrite').objectStore(IDB_STORE).put(val, key);
|
|
55
|
+
r.onsuccess = () => resolve();
|
|
56
|
+
r.onerror = () => reject(r.error);
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
function guestKeyUnavailable(cause) {
|
|
60
|
+
const detail = cause instanceof Error && cause.message ? ` Cause: ${cause.message}` : '';
|
|
61
|
+
return new Error(`${GUEST_KEY_REQUIREMENT}${detail}`);
|
|
62
|
+
}
|
|
63
|
+
async function webCryptoKey() {
|
|
64
|
+
const s = subtle();
|
|
65
|
+
if (!s)
|
|
66
|
+
throw guestKeyUnavailable(new Error('WebCrypto subtle crypto is unavailable.'));
|
|
67
|
+
if (typeof globalThis.indexedDB === 'undefined') {
|
|
68
|
+
throw guestKeyUnavailable(new Error('IndexedDB is unavailable.'));
|
|
69
|
+
}
|
|
70
|
+
try {
|
|
71
|
+
const db = await idb();
|
|
72
|
+
let pair = (await idbGet(db, IDB_KEY));
|
|
73
|
+
if (!pair) {
|
|
74
|
+
// Probe Ed25519 support by generating (throws on unsupported engines).
|
|
75
|
+
pair = (await s.generateKey({ name: 'Ed25519' }, /* extractable */ false, [
|
|
76
|
+
'sign',
|
|
77
|
+
'verify',
|
|
78
|
+
]));
|
|
79
|
+
await idbPut(db, IDB_KEY, pair); // CryptoKey is structured-cloneable; bytes never exposed
|
|
80
|
+
}
|
|
81
|
+
const raw = new Uint8Array(await s.exportKey('raw', pair.publicKey)); // public is exportable
|
|
82
|
+
const address = index.bs58.encode(raw);
|
|
83
|
+
return {
|
|
84
|
+
address,
|
|
85
|
+
nonExtractable: true,
|
|
86
|
+
async sign(message) {
|
|
87
|
+
const data = new TextEncoder().encode(message);
|
|
88
|
+
const sig = new Uint8Array(await s.sign({ name: 'Ed25519' }, pair.privateKey, data));
|
|
89
|
+
return toBase64(sig);
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
throw guestKeyUnavailable(error);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
let cached = null;
|
|
98
|
+
/** Get (or create) this device's durable guest key. */
|
|
99
|
+
async function getGuestKey() {
|
|
100
|
+
if (cached)
|
|
101
|
+
return cached;
|
|
102
|
+
cached = await webCryptoKey();
|
|
103
|
+
return cached;
|
|
104
|
+
}
|
|
105
|
+
/** Forget the guest key on this device (a fresh signInAnonymously mints a new one). */
|
|
106
|
+
async function clearGuestKey() {
|
|
107
|
+
cached = null;
|
|
108
|
+
try {
|
|
109
|
+
index.getPlatform().storage.removeItem(LEGACY_STORED_SECRET_KEY);
|
|
110
|
+
}
|
|
111
|
+
catch (_a) { }
|
|
112
|
+
try {
|
|
113
|
+
if (typeof globalThis.indexedDB !== 'undefined') {
|
|
114
|
+
const db = await idb();
|
|
115
|
+
await new Promise((res) => {
|
|
116
|
+
const r = db.transaction(IDB_STORE, 'readwrite').objectStore(IDB_STORE).delete(IDB_KEY);
|
|
117
|
+
r.onsuccess = () => res();
|
|
118
|
+
r.onerror = () => res();
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
catch (_b) { }
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const GUEST_TX_UNSUPPORTED = 'Guest (anonymous) auth is offchain-only — it cannot sign or submit Solana transactions. ' +
|
|
126
|
+
'Use a wallet provider for onchain actions.';
|
|
127
|
+
class GuestAuthProvider {
|
|
128
|
+
async login() {
|
|
129
|
+
index.setAuthLoading(true);
|
|
130
|
+
try {
|
|
131
|
+
const key = await getGuestKey();
|
|
132
|
+
// Reuse a still-valid session for this exact guest.
|
|
133
|
+
const existing = await core.getActiveSessionManager().getSession();
|
|
134
|
+
if (existing && existing.address === key.address) {
|
|
135
|
+
const user = { address: key.address, isAnonymous: true, provider: this };
|
|
136
|
+
index.setCurrentUser(user);
|
|
137
|
+
return user;
|
|
138
|
+
}
|
|
139
|
+
// Mint a fresh session: nonce -> canonical message -> local signature.
|
|
140
|
+
const nonce = await core.genAuthNonce();
|
|
141
|
+
const message = await core.genSolanaMessage(key.address, nonce);
|
|
142
|
+
const signature = await key.sign(message); // base64 detached ed25519
|
|
143
|
+
const result = await core.createSessionWithSignature(key.address, message, signature);
|
|
144
|
+
await core.getActiveSessionManager().storeSession(key.address, result.accessToken, result.idToken, result.refreshToken);
|
|
145
|
+
const user = { address: key.address, isAnonymous: true, provider: this };
|
|
146
|
+
index.setCurrentUser(user);
|
|
147
|
+
return user;
|
|
148
|
+
}
|
|
149
|
+
finally {
|
|
150
|
+
index.setAuthLoading(false);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
async restoreSession() {
|
|
154
|
+
const session = await core.getActiveSessionManager().getSession();
|
|
155
|
+
if (!session)
|
|
156
|
+
return null;
|
|
157
|
+
const user = { address: session.address, isAnonymous: true, provider: this };
|
|
158
|
+
index.setCurrentUser(user);
|
|
159
|
+
return user;
|
|
160
|
+
}
|
|
161
|
+
async logout() {
|
|
162
|
+
// Clears the SESSION but keeps the device key, so logging back in returns the
|
|
163
|
+
// SAME guest identity (and its data). Use forgetGuest() to wipe the key too.
|
|
164
|
+
core.getActiveSessionManager().clearSession();
|
|
165
|
+
index.setCurrentUser(null);
|
|
166
|
+
}
|
|
167
|
+
/** Wipe the device key — the next login() is a brand-new guest identity. */
|
|
168
|
+
async forgetGuest() {
|
|
169
|
+
core.getActiveSessionManager().clearSession();
|
|
170
|
+
await clearGuestKey();
|
|
171
|
+
index.setCurrentUser(null);
|
|
172
|
+
}
|
|
173
|
+
async signMessage(message) {
|
|
174
|
+
const key = await getGuestKey();
|
|
175
|
+
return key.sign(message);
|
|
176
|
+
}
|
|
177
|
+
// --- offchain guest: no Solana transaction surface -------------------------
|
|
178
|
+
async runTransaction(_evm, _sol, _options) {
|
|
179
|
+
throw new Error(GUEST_TX_UNSUPPORTED);
|
|
180
|
+
}
|
|
181
|
+
async signTransaction(_t) {
|
|
182
|
+
throw new Error(GUEST_TX_UNSUPPORTED);
|
|
183
|
+
}
|
|
184
|
+
async signAndSubmitTransaction() {
|
|
185
|
+
throw new Error(GUEST_TX_UNSUPPORTED);
|
|
186
|
+
}
|
|
187
|
+
async getNativeMethods() {
|
|
188
|
+
return {};
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
exports.GuestAuthProvider = GuestAuthProvider;
|
|
193
|
+
//# sourceMappingURL=guest-auth-provider-D54uHbov.js.map
|