@ai-matrx/auth 0.1.3 → 0.2.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/CHANGELOG.md +32 -0
- package/README.md +11 -0
- package/dist/claims-DXdbe3cW.d.cts +138 -0
- package/dist/claims-DXdbe3cW.d.ts +138 -0
- package/dist/index.cjs +175 -4
- package/dist/index.d.cts +5 -52
- package/dist/index.d.ts +5 -52
- package/dist/index.js +178 -4
- package/dist/matrx-oauth.cjs +458 -0
- package/dist/matrx-oauth.d.cts +181 -0
- package/dist/matrx-oauth.d.ts +181 -0
- package/dist/matrx-oauth.js +437 -0
- package/dist/next.cjs +133 -1
- package/dist/next.d.cts +77 -1
- package/dist/next.d.ts +77 -1
- package/dist/next.js +136 -1
- package/dist/node.cjs +139 -2
- package/dist/node.d.cts +117 -1
- package/dist/node.d.ts +117 -1
- package/dist/node.js +136 -1
- package/dist/organizations.cjs +132 -3
- package/dist/organizations.d.cts +108 -2
- package/dist/organizations.d.ts +108 -2
- package/dist/organizations.js +132 -3
- package/dist/{session-DhRO4euL.d.cts → session-DdQiStp6.d.cts} +12 -2
- package/dist/{session-DhRO4euL.d.ts → session-DdQiStp6.d.ts} +12 -2
- package/dist/storage-Dqnye4KF.d.cts +51 -0
- package/dist/storage-Dqnye4KF.d.ts +51 -0
- package/package.json +12 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,57 +1,10 @@
|
|
|
1
|
-
export { A as AuthClient, S as SessionSourceLike, a as SignInError, U as UserCredentialsPort, g as getPerson, b as getSession, o as onSessionChange, r as refreshSession, s as sessionCredentials, c as signInWithOAuth, d as signInWithPassword, e as signOut, u as updatePerson, f as userCredentials } from './session-
|
|
1
|
+
export { A as AuthClient, S as SessionSourceLike, a as SignInError, U as UserCredentialsPort, g as getPerson, b as getSession, i as invalidatePerson, o as onSessionChange, r as refreshSession, s as sessionCredentials, c as signInWithOAuth, d as signInWithPassword, e as signOut, u as updatePerson, f as userCredentials } from './session-DdQiStp6.js';
|
|
2
|
+
import { S as SessionStorage } from './storage-Dqnye4KF.js';
|
|
3
|
+
export { C as ChromeStorageAreaLike, a as StorageUnavailableError, b as browserStorage, e as extensionStorage, k as keyValueStorage, m as memoryStorage } from './storage-Dqnye4KF.js';
|
|
2
4
|
import { SupabaseClient } from '@supabase/supabase-js';
|
|
3
5
|
export { AuthChangeEvent, Session, User, UserAttributes } from '@supabase/supabase-js';
|
|
4
6
|
export { CredentialsPort, MatrxCredential, MatrxCredentialKind } from './credentials.js';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Session storage per platform — where a Supabase session lives between runs.
|
|
8
|
-
*
|
|
9
|
-
* supabase-js takes any `{ getItem, setItem, removeItem }` (sync or async). These are the ones
|
|
10
|
-
* every Matrx client needs, written once:
|
|
11
|
-
*
|
|
12
|
-
* browser — `browserStorage("local")` (supabase-js's own default; `"session"` = this tab only)
|
|
13
|
-
* Next.js — the cookie-backed clients in `@ai-matrx/auth/next` (the domain-wide auth cookie)
|
|
14
|
-
* extension — `extensionStorage()` over `chrome.storage.local` (shared by popup, side panel and
|
|
15
|
-
* service worker; `localStorage` does not exist in a service worker)
|
|
16
|
-
* desktop — `keyValueStorage({ get, set, remove })` over the host's store (Tauri store, keychain)
|
|
17
|
-
* Node — `fileStorage(path)` in `@ai-matrx/auth/node`, or `memoryStorage()` for a one-shot run
|
|
18
|
-
*
|
|
19
|
-
* None of these is ever silently swapped for another: a storage the platform does not have throws
|
|
20
|
-
* a named error at creation, never a quiet in-memory fallback that signs the person out on reload.
|
|
21
|
-
*/
|
|
22
|
-
/** The storage contract supabase-js reads (sync or async methods). */
|
|
23
|
-
interface SessionStorage {
|
|
24
|
-
getItem(key: string): string | null | Promise<string | null>;
|
|
25
|
-
setItem(key: string, value: string): void | Promise<void>;
|
|
26
|
-
removeItem(key: string): void | Promise<void>;
|
|
27
|
-
}
|
|
28
|
-
declare class StorageUnavailableError extends Error {
|
|
29
|
-
readonly storage: string;
|
|
30
|
-
constructor(storage: string, reason: string);
|
|
31
|
-
}
|
|
32
|
-
/** In memory only: the session ends with the process (one-shot scripts, tests, server renders). */
|
|
33
|
-
declare function memoryStorage(): SessionStorage & {
|
|
34
|
-
readonly size: () => number;
|
|
35
|
-
};
|
|
36
|
-
/** The browser's `localStorage` (shared by every tab of the origin) or `sessionStorage` (this tab). */
|
|
37
|
-
declare function browserStorage(kind?: "local" | "session"): SessionStorage;
|
|
38
|
-
/** The structural slice of `chrome.storage.<area>` read here (MV3 promise API). */
|
|
39
|
-
interface ChromeStorageAreaLike {
|
|
40
|
-
get(keys: string): Promise<Record<string, unknown>>;
|
|
41
|
-
set(items: Record<string, unknown>): Promise<void>;
|
|
42
|
-
remove(keys: string): Promise<void>;
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* A browser extension's `chrome.storage` area (default `local`): one session for the popup, side
|
|
46
|
-
* panel, content scripts' relay and the service worker. Pass `area` to inject one (tests, Firefox).
|
|
47
|
-
*/
|
|
48
|
-
declare function extensionStorage(area?: ChromeStorageAreaLike): SessionStorage;
|
|
49
|
-
/** Any async key-value store the host owns (a desktop app's Tauri store or keychain). */
|
|
50
|
-
declare function keyValueStorage(store: {
|
|
51
|
-
get(key: string): Promise<string | null | undefined>;
|
|
52
|
-
set(key: string, value: string): Promise<void>;
|
|
53
|
-
remove(key: string): Promise<void>;
|
|
54
|
-
}): SessionStorage;
|
|
7
|
+
export { A as ApiClaimsUser, C as ClaimsCacheOptions, a as ClaimsCapableClient, b as ClaimsResult, P as PROJECT_SIGNING_KEYS, c as PROJECT_SIGNING_KEY_AUTHORITIES, d as accountFlagsFor, e as confirmUnverifiableSession, f as createClaimsCache, g as getClaimsUser, i as isAuthTransportFailure, h as isSignedOutError, j as isUnverifiableTokenError, k as isUnverifiableTokenRefusal, p as pinnedSigningKeysFor, u as userFromClaims } from './claims-DXdbe3cW.js';
|
|
55
8
|
|
|
56
9
|
/**
|
|
57
10
|
* `createAuthClient` — the one Supabase client factory OUTSIDE Next.js (Next apps use
|
|
@@ -83,4 +36,4 @@ declare class SecretKeyRefusedError extends Error {
|
|
|
83
36
|
}
|
|
84
37
|
declare function createAuthClient<Database = any>(options: CreateAuthClientOptions): SupabaseClient<Database>;
|
|
85
38
|
|
|
86
|
-
export { type
|
|
39
|
+
export { type CreateAuthClientOptions, SecretKeyRefusedError, SessionStorage, createAuthClient };
|
package/dist/index.js
CHANGED
|
@@ -19,10 +19,43 @@ async function getSession(client) {
|
|
|
19
19
|
const { data, error } = await client.auth.getSession();
|
|
20
20
|
return check("getSession", data, error).session;
|
|
21
21
|
}
|
|
22
|
+
var personCache = /* @__PURE__ */ new WeakMap();
|
|
23
|
+
function personEntry(client) {
|
|
24
|
+
let entry = personCache.get(client);
|
|
25
|
+
if (!entry) {
|
|
26
|
+
const fresh = { read: null, epoch: 0 };
|
|
27
|
+
entry = fresh;
|
|
28
|
+
personCache.set(client, fresh);
|
|
29
|
+
const subscribe = client.auth?.onAuthStateChange;
|
|
30
|
+
if (typeof subscribe === "function") {
|
|
31
|
+
subscribe.call(client.auth, async (event) => {
|
|
32
|
+
if (event === "SIGNED_IN" || event === "SIGNED_OUT" || event === "USER_UPDATED") invalidatePerson(client);
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return entry;
|
|
37
|
+
}
|
|
38
|
+
function invalidatePerson(client) {
|
|
39
|
+
const entry = personCache.get(client);
|
|
40
|
+
if (!entry) return;
|
|
41
|
+
entry.epoch += 1;
|
|
42
|
+
entry.read = null;
|
|
43
|
+
}
|
|
22
44
|
async function getPerson(client) {
|
|
23
|
-
const
|
|
24
|
-
if (
|
|
25
|
-
|
|
45
|
+
const entry = personEntry(client);
|
|
46
|
+
if (entry.read) return entry.read;
|
|
47
|
+
const epoch = entry.epoch;
|
|
48
|
+
const read = (async () => {
|
|
49
|
+
const { data, error } = await client.auth.getUser();
|
|
50
|
+
if (error && (error.name === "AuthSessionMissingError" || error.status === 401)) return null;
|
|
51
|
+
return check("getPerson", data, error).user;
|
|
52
|
+
})();
|
|
53
|
+
entry.read = read;
|
|
54
|
+
const settle = (keep) => {
|
|
55
|
+
if (entry.epoch === epoch && entry.read === read && !keep) entry.read = null;
|
|
56
|
+
};
|
|
57
|
+
read.then((user) => settle(user !== null), () => settle(false));
|
|
58
|
+
return read;
|
|
26
59
|
}
|
|
27
60
|
function onSessionChange(client, cb) {
|
|
28
61
|
const { data } = client.auth.onAuthStateChange(cb);
|
|
@@ -30,6 +63,7 @@ function onSessionChange(client, cb) {
|
|
|
30
63
|
}
|
|
31
64
|
async function signInWithPassword(client, credentials) {
|
|
32
65
|
const { data, error } = await client.auth.signInWithPassword(credentials);
|
|
66
|
+
invalidatePerson(client);
|
|
33
67
|
const ok = check("signInWithPassword", data, error);
|
|
34
68
|
return { session: ok.session, person: ok.user };
|
|
35
69
|
}
|
|
@@ -41,10 +75,12 @@ async function signInWithOAuth(client, options) {
|
|
|
41
75
|
}
|
|
42
76
|
async function signOut(client, scope = "global") {
|
|
43
77
|
const { error } = await client.auth.signOut({ scope });
|
|
78
|
+
invalidatePerson(client);
|
|
44
79
|
check("signOut", null, error);
|
|
45
80
|
}
|
|
46
81
|
async function updatePerson(client, attributes) {
|
|
47
82
|
const { data, error } = await client.auth.updateUser(attributes);
|
|
83
|
+
invalidatePerson(client);
|
|
48
84
|
return check("updatePerson", data, error).user;
|
|
49
85
|
}
|
|
50
86
|
function sessionCredentials(client) {
|
|
@@ -144,23 +180,161 @@ function createAuthClient(options) {
|
|
|
144
180
|
if (options.detectSessionInUrl !== void 0) auth["detectSessionInUrl"] = options.detectSessionInUrl;
|
|
145
181
|
return createClient(options.url, options.key, { auth });
|
|
146
182
|
}
|
|
183
|
+
|
|
184
|
+
// src/claims.ts
|
|
185
|
+
import {
|
|
186
|
+
AuthError,
|
|
187
|
+
AuthSessionMissingError
|
|
188
|
+
} from "@supabase/supabase-js";
|
|
189
|
+
async function getClaimsUser(client, jwt, options) {
|
|
190
|
+
const { data, error } = options ? await client.auth.getClaims(jwt, options) : await client.auth.getClaims(jwt);
|
|
191
|
+
if (error) {
|
|
192
|
+
if (isSignedOutError(error)) return { data: { user: null }, error: null };
|
|
193
|
+
return { data: { user: null }, error };
|
|
194
|
+
}
|
|
195
|
+
if (!data?.claims) return { data: { user: null }, error: null };
|
|
196
|
+
const user = userFromClaims(data.claims);
|
|
197
|
+
if (!user) {
|
|
198
|
+
return {
|
|
199
|
+
data: { user: null },
|
|
200
|
+
error: new AuthError("Verified token carries no `sub` claim", 401, "bad_jwt")
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
return { data: { user }, error: null };
|
|
204
|
+
}
|
|
205
|
+
var BUDGET_EXHAUSTED_STATUS = 408;
|
|
206
|
+
var BUDGET_EXHAUSTED_CODE = "auth_budget_exhausted";
|
|
207
|
+
function isAuthTransportFailure(error) {
|
|
208
|
+
if (!error || typeof error !== "object") return false;
|
|
209
|
+
const name = String(error.name ?? "");
|
|
210
|
+
if (name === "AuthRetryableFetchError" || name === "TimeoutError" || name === "AbortError") return true;
|
|
211
|
+
const status = error.status;
|
|
212
|
+
if (status === 0 || status === 504 || status === 503) return true;
|
|
213
|
+
return status === BUDGET_EXHAUSTED_STATUS || error.code === BUDGET_EXHAUSTED_CODE;
|
|
214
|
+
}
|
|
215
|
+
function isSignedOutError(error) {
|
|
216
|
+
return error instanceof AuthSessionMissingError || error.name === "AuthSessionMissingError" || error.code === "session_not_found";
|
|
217
|
+
}
|
|
218
|
+
function userFromClaims(claims) {
|
|
219
|
+
if (!claims || typeof claims.sub !== "string") return null;
|
|
220
|
+
return {
|
|
221
|
+
...claims,
|
|
222
|
+
id: claims.sub,
|
|
223
|
+
app_metadata: claims.app_metadata ?? {},
|
|
224
|
+
user_metadata: claims.user_metadata ?? {}
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
function accountFlagsFor(claims) {
|
|
228
|
+
if (!claims) return { isGuest: false, hasAccount: false };
|
|
229
|
+
const isGuest = claims.is_anonymous === true;
|
|
230
|
+
return { isGuest, hasAccount: !isGuest };
|
|
231
|
+
}
|
|
232
|
+
var PROJECT_SIGNING_KEY_AUTHORITIES = [
|
|
233
|
+
"db.matrxserver.com",
|
|
234
|
+
"brsgrqvjdzwihsvnfqkf.supabase.co"
|
|
235
|
+
];
|
|
236
|
+
var PROJECT_SIGNING_KEYS = [
|
|
237
|
+
{
|
|
238
|
+
alg: "ES256",
|
|
239
|
+
crv: "P-256",
|
|
240
|
+
ext: true,
|
|
241
|
+
key_ops: ["verify"],
|
|
242
|
+
kid: "a00aed0b-202c-483a-91ff-fc74719a3bb2",
|
|
243
|
+
kty: "EC",
|
|
244
|
+
use: "sig",
|
|
245
|
+
x: "guqOsgjJqGVU6g_K0iIXKrzixlkeXKOqkPUQCconp6U",
|
|
246
|
+
y: "39zQtkzlfQuPf_MtIhYmMdxNQpIR2Pkl11AToywaslY"
|
|
247
|
+
}
|
|
248
|
+
];
|
|
249
|
+
function pinnedSigningKeysFor(supabaseUrl) {
|
|
250
|
+
if (!supabaseUrl) return [];
|
|
251
|
+
let host;
|
|
252
|
+
try {
|
|
253
|
+
host = new URL(supabaseUrl).host.toLowerCase();
|
|
254
|
+
} catch {
|
|
255
|
+
return [];
|
|
256
|
+
}
|
|
257
|
+
return PROJECT_SIGNING_KEY_AUTHORITIES.includes(host) ? PROJECT_SIGNING_KEYS : [];
|
|
258
|
+
}
|
|
259
|
+
function isUnverifiableTokenRefusal(result) {
|
|
260
|
+
if (result.status !== 401) return false;
|
|
261
|
+
if (result.error?.code !== "PGRST301") return false;
|
|
262
|
+
return !/expired/i.test(result.error?.message ?? "");
|
|
263
|
+
}
|
|
264
|
+
function isUnverifiableTokenError(error) {
|
|
265
|
+
if (!error) return false;
|
|
266
|
+
const name = String(error.name ?? "");
|
|
267
|
+
if (name === "AuthRetryableFetchError" || name === "TimeoutError" || name === "AbortError") return false;
|
|
268
|
+
if (name === "AuthSessionMissingError" || error.code === "session_not_found") return false;
|
|
269
|
+
const status = error.status;
|
|
270
|
+
if (status === 0 || status === 408 || status === 503 || status === 504) return false;
|
|
271
|
+
if (/expired/i.test(error.message ?? "")) return false;
|
|
272
|
+
return error.code === "bad_jwt" || name === "AuthInvalidJwtError";
|
|
273
|
+
}
|
|
274
|
+
async function confirmUnverifiableSession(auth, supabaseUrl) {
|
|
275
|
+
try {
|
|
276
|
+
const { error } = await auth.getClaims(void 0, { jwks: { keys: pinnedSigningKeysFor(supabaseUrl) } });
|
|
277
|
+
return isUnverifiableTokenError(error);
|
|
278
|
+
} catch {
|
|
279
|
+
return false;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
function createClaimsCache(client, options) {
|
|
283
|
+
const ttl = options.ttlMs ?? 2e3;
|
|
284
|
+
let cached = null;
|
|
285
|
+
let inflight = null;
|
|
286
|
+
let listening = false;
|
|
287
|
+
return () => {
|
|
288
|
+
if (!listening) {
|
|
289
|
+
listening = true;
|
|
290
|
+
client.auth.onAuthStateChange((event) => {
|
|
291
|
+
if (event === "SIGNED_OUT" || event === "SIGNED_IN" || event === "TOKEN_REFRESHED" || event === "USER_UPDATED") {
|
|
292
|
+
cached = null;
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
if (cached && cached.expiresAt > Date.now()) return Promise.resolve(cached.value);
|
|
297
|
+
if (inflight) return inflight;
|
|
298
|
+
inflight = getClaimsUser(client, void 0, { jwks: { keys: pinnedSigningKeysFor(options.supabaseUrl) } }).then((result) => {
|
|
299
|
+
if (isUnverifiableTokenError(result.error)) options.onUnverifiable?.();
|
|
300
|
+
cached = { value: result, expiresAt: Date.now() + ttl };
|
|
301
|
+
return result;
|
|
302
|
+
}).finally(() => {
|
|
303
|
+
inflight = null;
|
|
304
|
+
});
|
|
305
|
+
return inflight;
|
|
306
|
+
};
|
|
307
|
+
}
|
|
147
308
|
export {
|
|
309
|
+
PROJECT_SIGNING_KEYS,
|
|
310
|
+
PROJECT_SIGNING_KEY_AUTHORITIES,
|
|
148
311
|
SecretKeyRefusedError,
|
|
149
312
|
SignInError,
|
|
150
313
|
StorageUnavailableError,
|
|
314
|
+
accountFlagsFor,
|
|
151
315
|
browserStorage,
|
|
316
|
+
confirmUnverifiableSession,
|
|
152
317
|
createAuthClient,
|
|
318
|
+
createClaimsCache,
|
|
153
319
|
extensionStorage,
|
|
320
|
+
getClaimsUser,
|
|
154
321
|
getPerson,
|
|
155
322
|
getSession,
|
|
323
|
+
invalidatePerson,
|
|
324
|
+
isAuthTransportFailure,
|
|
325
|
+
isSignedOutError,
|
|
326
|
+
isUnverifiableTokenError,
|
|
327
|
+
isUnverifiableTokenRefusal,
|
|
156
328
|
keyValueStorage,
|
|
157
329
|
memoryStorage,
|
|
158
330
|
onSessionChange,
|
|
331
|
+
pinnedSigningKeysFor,
|
|
159
332
|
refreshSession,
|
|
160
333
|
sessionCredentials,
|
|
161
334
|
signInWithOAuth,
|
|
162
335
|
signInWithPassword,
|
|
163
336
|
signOut,
|
|
164
337
|
updatePerson,
|
|
165
|
-
userCredentials
|
|
338
|
+
userCredentials,
|
|
339
|
+
userFromClaims
|
|
166
340
|
};
|