@ttt-productions/auth-core 1.6.16 → 1.6.17
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/acceptance.d.ts +0 -20
- package/dist/acceptance.d.ts.map +1 -1
- package/dist/acceptance.js +0 -20
- package/dist/acceptance.js.map +1 -1
- package/dist/client/auth.d.ts +0 -5
- package/dist/client/auth.d.ts.map +1 -1
- package/dist/client/auth.js +0 -5
- package/dist/client/auth.js.map +1 -1
- package/dist/client/claims.d.ts +0 -27
- package/dist/client/claims.d.ts.map +1 -1
- package/dist/client/claims.js +0 -27
- package/dist/client/claims.js.map +1 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +0 -3
- package/dist/client/index.js.map +1 -1
- package/dist/email-verification.d.ts +0 -13
- package/dist/email-verification.d.ts.map +1 -1
- package/dist/email-verification.js +0 -16
- package/dist/email-verification.js.map +1 -1
- package/dist/errors.d.ts +0 -2
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +1 -8
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -7
- package/dist/index.js.map +1 -1
- package/dist/react/AuthProvider.d.ts.map +1 -1
- package/dist/react/AuthProvider.js +1 -25
- package/dist/react/AuthProvider.js.map +1 -1
- package/dist/react/redirect-storage.d.ts.map +1 -1
- package/dist/react/redirect-storage.js +4 -13
- package/dist/react/redirect-storage.js.map +1 -1
- package/dist/react/types.d.ts +0 -21
- package/dist/react/types.d.ts.map +1 -1
- package/dist/react/types.js.map +1 -1
- package/dist/react/useAuth.d.ts +0 -4
- package/dist/react/useAuth.d.ts.map +1 -1
- package/dist/react/useAuth.js +0 -4
- package/dist/react/useAuth.js.map +1 -1
- package/dist/react/useAuthGuard.d.ts +0 -33
- package/dist/react/useAuthGuard.d.ts.map +1 -1
- package/dist/react/useAuthGuard.js +2 -32
- package/dist/react/useAuthGuard.js.map +1 -1
- package/dist/react/useAuthState.d.ts.map +1 -1
- package/dist/react/useAuthState.js +0 -4
- package/dist/react/useAuthState.js.map +1 -1
- package/dist/refusal.d.ts +0 -6
- package/dist/refusal.d.ts.map +1 -1
- package/dist/refusal.js +0 -12
- package/dist/refusal.js.map +1 -1
- package/dist/server/assertAuth.d.ts +1 -1
- package/dist/server/assertAuth.d.ts.map +1 -1
- package/dist/server/assertAuth.js +0 -22
- package/dist/server/assertAuth.js.map +1 -1
- package/dist/server/authError.d.ts +0 -48
- package/dist/server/authError.d.ts.map +1 -1
- package/dist/server/authError.js.map +1 -1
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +0 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/types.d.ts +5 -105
- package/dist/server/types.d.ts.map +1 -1
- package/dist/server/types.js +0 -2
- package/dist/server/types.js.map +1 -1
- package/package.json +1 -1
package/dist/acceptance.d.ts
CHANGED
|
@@ -1,30 +1,10 @@
|
|
|
1
|
-
/** The `details.reason` of the refusal a caller below the required level receives. */
|
|
2
1
|
export declare const ACCEPTANCE_REQUIRED_REASON: "acceptance-required";
|
|
3
|
-
/**
|
|
4
|
-
* Structured details carried by the acceptance refusal. Server side it rides
|
|
5
|
-
* `AuthAssertionError.details`; the consuming app forwards it as the `HttpsError` details, so
|
|
6
|
-
* the client receives it on the callable error's `details`.
|
|
7
|
-
*/
|
|
8
2
|
export interface AcceptanceRequiredDetails {
|
|
9
3
|
readonly reason: typeof ACCEPTANCE_REQUIRED_REASON;
|
|
10
|
-
/** The level the backend currently requires. */
|
|
11
4
|
readonly requiredLevel: number;
|
|
12
|
-
/** The level read from the caller's token (0 when absent or unreadable). */
|
|
13
5
|
readonly acceptedLevel: number;
|
|
14
6
|
}
|
|
15
|
-
/**
|
|
16
|
-
* Normalize an acceptance level: a finite number ≥ 0, floored to a whole number. Anything
|
|
17
|
-
* else — absent, a string, NaN, a negative — is 0. Applied to BOTH sides of the comparison:
|
|
18
|
-
* an unreadable accepted level can never satisfy a positive requirement, and an unreadable
|
|
19
|
-
* required level requires nothing.
|
|
20
|
-
*/
|
|
21
7
|
export declare function normalizeAcceptanceLevel(value: unknown): number;
|
|
22
|
-
/** True when `details` is the acceptance refusal's structured details. */
|
|
23
8
|
export declare function isAcceptanceRequiredDetails(details: unknown): details is AcceptanceRequiredDetails;
|
|
24
|
-
/**
|
|
25
|
-
* True when `err` is the acceptance refusal — a `failed-precondition` whose details carry
|
|
26
|
-
* `reason: 'acceptance-required'`. Works on the server-side error and on the client-side
|
|
27
|
-
* callable error alike, so the app has one recognizer for "show the acceptance prompt".
|
|
28
|
-
*/
|
|
29
9
|
export declare function isAcceptanceRequiredError(err: unknown): boolean;
|
|
30
10
|
//# sourceMappingURL=acceptance.d.ts.map
|
package/dist/acceptance.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"acceptance.d.ts","sourceRoot":"","sources":["../src/acceptance.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"acceptance.d.ts","sourceRoot":"","sources":["../src/acceptance.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,0BAA0B,EAAG,qBAA8B,CAAC;AAEzE,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,MAAM,EAAE,OAAO,0BAA0B,CAAC;IACnD,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAG/D;AAED,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,OAAO,GACf,OAAO,IAAI,yBAAyB,CAQtC;AAED,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAE/D"}
|
package/dist/acceptance.js
CHANGED
|
@@ -1,25 +1,10 @@
|
|
|
1
|
-
// The acceptance-level contract shared by the server gate (`createAssertAuth`'s optional
|
|
2
|
-
// `acceptance` config on ./server) and the client that answers it. Pure: no Firebase runtime.
|
|
3
|
-
//
|
|
4
|
-
// A consuming app publishes documents its users must accept, and tracks acceptance as a
|
|
5
|
-
// monotonically increasing whole-number LEVEL: the app decides what raises the required level
|
|
6
|
-
// and where the caller's accepted level is stored (a custom claim named by the app). The package
|
|
7
|
-
// only compares the two numbers and reports a refusal in one recognisable shape.
|
|
8
1
|
import { failedPreconditionDetails } from './refusal.js';
|
|
9
|
-
/** The `details.reason` of the refusal a caller below the required level receives. */
|
|
10
2
|
export const ACCEPTANCE_REQUIRED_REASON = 'acceptance-required';
|
|
11
|
-
/**
|
|
12
|
-
* Normalize an acceptance level: a finite number ≥ 0, floored to a whole number. Anything
|
|
13
|
-
* else — absent, a string, NaN, a negative — is 0. Applied to BOTH sides of the comparison:
|
|
14
|
-
* an unreadable accepted level can never satisfy a positive requirement, and an unreadable
|
|
15
|
-
* required level requires nothing.
|
|
16
|
-
*/
|
|
17
3
|
export function normalizeAcceptanceLevel(value) {
|
|
18
4
|
if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0)
|
|
19
5
|
return 0;
|
|
20
6
|
return Math.floor(value);
|
|
21
7
|
}
|
|
22
|
-
/** True when `details` is the acceptance refusal's structured details. */
|
|
23
8
|
export function isAcceptanceRequiredDetails(details) {
|
|
24
9
|
if (!details || typeof details !== 'object')
|
|
25
10
|
return false;
|
|
@@ -28,11 +13,6 @@ export function isAcceptanceRequiredDetails(details) {
|
|
|
28
13
|
typeof d.requiredLevel === 'number' &&
|
|
29
14
|
typeof d.acceptedLevel === 'number');
|
|
30
15
|
}
|
|
31
|
-
/**
|
|
32
|
-
* True when `err` is the acceptance refusal — a `failed-precondition` whose details carry
|
|
33
|
-
* `reason: 'acceptance-required'`. Works on the server-side error and on the client-side
|
|
34
|
-
* callable error alike, so the app has one recognizer for "show the acceptance prompt".
|
|
35
|
-
*/
|
|
36
16
|
export function isAcceptanceRequiredError(err) {
|
|
37
17
|
return isAcceptanceRequiredDetails(failedPreconditionDetails(err));
|
|
38
18
|
}
|
package/dist/acceptance.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"acceptance.js","sourceRoot":"","sources":["../src/acceptance.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"acceptance.js","sourceRoot":"","sources":["../src/acceptance.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAC;AAEzD,MAAM,CAAC,MAAM,0BAA0B,GAAG,qBAA8B,CAAC;AAQzE,MAAM,UAAU,wBAAwB,CAAC,KAAc;IACrD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC;QAAE,OAAO,CAAC,CAAC;IACjF,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;AAC3B,CAAC;AAED,MAAM,UAAU,2BAA2B,CACzC,OAAgB;IAEhB,IAAI,CAAC,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC1D,MAAM,CAAC,GAAG,OAAkC,CAAC;IAC7C,OAAO,CACL,CAAC,CAAC,MAAM,KAAK,0BAA0B;QACvC,OAAO,CAAC,CAAC,aAAa,KAAK,QAAQ;QACnC,OAAO,CAAC,CAAC,aAAa,KAAK,QAAQ,CACpC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,yBAAyB,CAAC,GAAY;IACpD,OAAO,2BAA2B,CAAC,yBAAyB,CAAC,GAAG,CAAC,CAAC,CAAC;AACrE,CAAC","sourcesContent":["import { failedPreconditionDetails } from './refusal.js';\n\nexport const ACCEPTANCE_REQUIRED_REASON = 'acceptance-required' as const;\n\nexport interface AcceptanceRequiredDetails {\n readonly reason: typeof ACCEPTANCE_REQUIRED_REASON;\n readonly requiredLevel: number;\n readonly acceptedLevel: number;\n}\n\nexport function normalizeAcceptanceLevel(value: unknown): number {\n if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0) return 0;\n return Math.floor(value);\n}\n\nexport function isAcceptanceRequiredDetails(\n details: unknown,\n): details is AcceptanceRequiredDetails {\n if (!details || typeof details !== 'object') return false;\n const d = details as Record<string, unknown>;\n return (\n d.reason === ACCEPTANCE_REQUIRED_REASON &&\n typeof d.requiredLevel === 'number' &&\n typeof d.acceptedLevel === 'number'\n );\n}\n\nexport function isAcceptanceRequiredError(err: unknown): boolean {\n return isAcceptanceRequiredDetails(failedPreconditionDetails(err));\n}\n"]}
|
package/dist/client/auth.d.ts
CHANGED
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
import type { Auth, User } from 'firebase/auth';
|
|
2
2
|
export type Unsubscribe = () => void;
|
|
3
3
|
export declare function getAuthUser(auth: Auth): User | null;
|
|
4
|
-
/**
|
|
5
|
-
* Thin wrapper around Firebase onAuthStateChanged.
|
|
6
|
-
* - always returns the unsubscribe function
|
|
7
|
-
* - preserves Firebase callback signature
|
|
8
|
-
*/
|
|
9
4
|
export declare function onAuthStateChanged(auth: Auth, cb: (user: User | null) => void, onError?: (error: unknown) => void): Unsubscribe;
|
|
10
5
|
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/client/auth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAGhD,MAAM,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC;AAErC,wBAAgB,WAAW,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAEnD;AAED
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/client/auth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAGhD,MAAM,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC;AAErC,wBAAgB,WAAW,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAEnD;AAED,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,IAAI,EACV,EAAE,EAAE,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,KAAK,IAAI,EAC/B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,GACjC,WAAW,CAEb"}
|
package/dist/client/auth.js
CHANGED
|
@@ -2,11 +2,6 @@ import { onAuthStateChanged as fbOnAuthStateChanged } from 'firebase/auth';
|
|
|
2
2
|
export function getAuthUser(auth) {
|
|
3
3
|
return auth.currentUser;
|
|
4
4
|
}
|
|
5
|
-
/**
|
|
6
|
-
* Thin wrapper around Firebase onAuthStateChanged.
|
|
7
|
-
* - always returns the unsubscribe function
|
|
8
|
-
* - preserves Firebase callback signature
|
|
9
|
-
*/
|
|
10
5
|
export function onAuthStateChanged(auth, cb, onError) {
|
|
11
6
|
return fbOnAuthStateChanged(auth, cb, onError);
|
|
12
7
|
}
|
package/dist/client/auth.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/client/auth.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,IAAI,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAI3E,MAAM,UAAU,WAAW,CAAC,IAAU;IACpC,OAAO,IAAI,CAAC,WAAW,CAAC;AAC1B,CAAC;AAED
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/client/auth.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,IAAI,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAI3E,MAAM,UAAU,WAAW,CAAC,IAAU;IACpC,OAAO,IAAI,CAAC,WAAW,CAAC;AAC1B,CAAC;AAED,MAAM,UAAU,kBAAkB,CAChC,IAAU,EACV,EAA+B,EAC/B,OAAkC;IAElC,OAAO,oBAAoB,CAAC,IAAI,EAAE,EAAE,EAAE,OAAO,CAAC,CAAC;AACjD,CAAC","sourcesContent":["import type { Auth, User } from 'firebase/auth';\nimport { onAuthStateChanged as fbOnAuthStateChanged } from 'firebase/auth';\n\nexport type Unsubscribe = () => void;\n\nexport function getAuthUser(auth: Auth): User | null {\n return auth.currentUser;\n}\n\nexport function onAuthStateChanged(\n auth: Auth,\n cb: (user: User | null) => void,\n onError?: (error: unknown) => void,\n): Unsubscribe {\n return fbOnAuthStateChanged(auth, cb, onError);\n}\n"]}
|
package/dist/client/claims.d.ts
CHANGED
|
@@ -4,35 +4,8 @@ export type ParsedClaims = {
|
|
|
4
4
|
roles: string[];
|
|
5
5
|
raw: IdTokenClaims;
|
|
6
6
|
};
|
|
7
|
-
/**
|
|
8
|
-
* Get the current user's ID token claims.
|
|
9
|
-
* Returns null if no current user.
|
|
10
|
-
*
|
|
11
|
-
* STALE-VERIFICATION RECONCILIATION: when the ACCOUNT says the email is
|
|
12
|
-
* verified but the cached token claim still says it is not, force-mint a fresh
|
|
13
|
-
* token and return ITS claims. Firebase ID tokens live for up to an hour and a
|
|
14
|
-
* backend-side `emailVerified` flip (verification link applied elsewhere, or an
|
|
15
|
-
* Admin SDK update) never invalidates them — without this, the UI (account
|
|
16
|
-
* state) believes the user is verified while every backend call (token claim)
|
|
17
|
-
* rejects with "Email must be verified" until the token happens to rotate.
|
|
18
|
-
* One forced mint converges the two (the fresh token carries
|
|
19
|
-
* `email_verified: true`); the check is directional, so an already-consistent
|
|
20
|
-
* user never pays a forced refresh.
|
|
21
|
-
*/
|
|
22
7
|
export declare function getIdTokenClaims(user: User | null): Promise<IdTokenClaims | null>;
|
|
23
|
-
/**
|
|
24
|
-
* Forces refresh of ID token + claims.
|
|
25
|
-
* Returns null if no current user.
|
|
26
|
-
*/
|
|
27
8
|
export declare function refreshClaims(user: User | null): Promise<IdTokenClaims | null>;
|
|
28
|
-
/**
|
|
29
|
-
* Convenience helper when you only have Auth.
|
|
30
|
-
*/
|
|
31
9
|
export declare function refreshClaimsFromAuth(auth: Auth): Promise<IdTokenClaims | null>;
|
|
32
|
-
/**
|
|
33
|
-
* Keep parsing intentionally minimal and generic:
|
|
34
|
-
* - normalize "roles" into string[]
|
|
35
|
-
* - keep raw claims attached
|
|
36
|
-
*/
|
|
37
10
|
export declare function parseClaims(claims: IdTokenClaims | null | undefined): ParsedClaims;
|
|
38
11
|
//# sourceMappingURL=claims.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claims.d.ts","sourceRoot":"","sources":["../../src/client/claims.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE/D,MAAM,MAAM,aAAa,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;AAEpD,MAAM,MAAM,YAAY,GAAG;
|
|
1
|
+
{"version":3,"file":"claims.d.ts","sourceRoot":"","sources":["../../src/client/claims.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE/D,MAAM,MAAM,aAAa,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;AAEpD,MAAM,MAAM,YAAY,GAAG;IACzB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,GAAG,EAAE,aAAa,CAAC;CACpB,CAAC;AAEF,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAQvF;AAED,wBAAsB,aAAa,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAIpF;AAED,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAErF;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,GAAG,SAAS,GAAG,YAAY,CAWlF"}
|
package/dist/client/claims.js
CHANGED
|
@@ -1,18 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Get the current user's ID token claims.
|
|
3
|
-
* Returns null if no current user.
|
|
4
|
-
*
|
|
5
|
-
* STALE-VERIFICATION RECONCILIATION: when the ACCOUNT says the email is
|
|
6
|
-
* verified but the cached token claim still says it is not, force-mint a fresh
|
|
7
|
-
* token and return ITS claims. Firebase ID tokens live for up to an hour and a
|
|
8
|
-
* backend-side `emailVerified` flip (verification link applied elsewhere, or an
|
|
9
|
-
* Admin SDK update) never invalidates them — without this, the UI (account
|
|
10
|
-
* state) believes the user is verified while every backend call (token claim)
|
|
11
|
-
* rejects with "Email must be verified" until the token happens to rotate.
|
|
12
|
-
* One forced mint converges the two (the fresh token carries
|
|
13
|
-
* `email_verified: true`); the check is directional, so an already-consistent
|
|
14
|
-
* user never pays a forced refresh.
|
|
15
|
-
*/
|
|
16
1
|
export async function getIdTokenClaims(user) {
|
|
17
2
|
if (!user)
|
|
18
3
|
return null;
|
|
@@ -23,27 +8,15 @@ export async function getIdTokenClaims(user) {
|
|
|
23
8
|
}
|
|
24
9
|
return res.claims;
|
|
25
10
|
}
|
|
26
|
-
/**
|
|
27
|
-
* Forces refresh of ID token + claims.
|
|
28
|
-
* Returns null if no current user.
|
|
29
|
-
*/
|
|
30
11
|
export async function refreshClaims(user) {
|
|
31
12
|
if (!user)
|
|
32
13
|
return null;
|
|
33
14
|
const res = await user.getIdTokenResult(true);
|
|
34
15
|
return res.claims;
|
|
35
16
|
}
|
|
36
|
-
/**
|
|
37
|
-
* Convenience helper when you only have Auth.
|
|
38
|
-
*/
|
|
39
17
|
export async function refreshClaimsFromAuth(auth) {
|
|
40
18
|
return refreshClaims(auth.currentUser);
|
|
41
19
|
}
|
|
42
|
-
/**
|
|
43
|
-
* Keep parsing intentionally minimal and generic:
|
|
44
|
-
* - normalize "roles" into string[]
|
|
45
|
-
* - keep raw claims attached
|
|
46
|
-
*/
|
|
47
20
|
export function parseClaims(claims) {
|
|
48
21
|
const raw = (claims ?? {});
|
|
49
22
|
const rolesValue = raw.roles;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claims.js","sourceRoot":"","sources":["../../src/client/claims.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"claims.js","sourceRoot":"","sources":["../../src/client/claims.ts"],"names":[],"mappings":"AASA,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,IAAiB;IACtD,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IACvB,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC;IAC/C,IAAI,IAAI,CAAC,aAAa,KAAK,IAAI,IAAI,GAAG,CAAC,MAAM,CAAC,cAAc,KAAK,IAAI,EAAE,CAAC;QACtE,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC;QAChD,OAAO,KAAK,CAAC,MAAM,CAAC;IACtB,CAAC;IACD,OAAO,GAAG,CAAC,MAAM,CAAC;AACpB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,IAAiB;IACnD,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IACvB,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC;IAC9C,OAAO,GAAG,CAAC,MAAM,CAAC;AACpB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,IAAU;IACpD,OAAO,aAAa,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;AACzC,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,MAAwC;IAClE,MAAM,GAAG,GAAG,CAAC,MAAM,IAAI,EAAE,CAAkB,CAAC;IAE5C,MAAM,UAAU,GAAI,GAAW,CAAC,KAAK,CAAC;IACtC,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC;QACrC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC;QACjD,CAAC,CAAC,OAAO,UAAU,KAAK,QAAQ;YAC9B,CAAC,CAAC,CAAC,UAAU,CAAC;YACd,CAAC,CAAC,EAAE,CAAC;IAET,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;AACxB,CAAC","sourcesContent":["import type { Auth, User, IdTokenResult } from 'firebase/auth';\n\nexport type IdTokenClaims = IdTokenResult['claims'];\n\nexport type ParsedClaims = {\n roles: string[];\n raw: IdTokenClaims;\n};\n\nexport async function getIdTokenClaims(user: User | null): Promise<IdTokenClaims | null> {\n if (!user) return null;\n const res = await user.getIdTokenResult(false);\n if (user.emailVerified === true && res.claims.email_verified !== true) {\n const fresh = await user.getIdTokenResult(true);\n return fresh.claims;\n }\n return res.claims;\n}\n\nexport async function refreshClaims(user: User | null): Promise<IdTokenClaims | null> {\n if (!user) return null;\n const res = await user.getIdTokenResult(true);\n return res.claims;\n}\n\nexport async function refreshClaimsFromAuth(auth: Auth): Promise<IdTokenClaims | null> {\n return refreshClaims(auth.currentUser);\n}\n\nexport function parseClaims(claims: IdTokenClaims | null | undefined): ParsedClaims {\n const raw = (claims ?? {}) as IdTokenClaims;\n\n const rolesValue = (raw as any).roles;\n const roles = Array.isArray(rolesValue)\n ? rolesValue.filter((x) => typeof x === 'string')\n : typeof rolesValue === 'string'\n ? [rolesValue]\n : [];\n\n return { roles, raw };\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC"}
|
package/dist/client/index.js
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
// Firebase *client* auth runtime. Importing this subpath pulls firebase/auth
|
|
2
|
-
// at runtime (e.g. onAuthStateChanged), so it is intentionally kept off the
|
|
3
|
-
// pure root. The pure root exposes only contracts/config/errors.
|
|
4
1
|
export * from './auth.js';
|
|
5
2
|
export * from './claims.js';
|
|
6
3
|
//# sourceMappingURL=index.js.map
|
package/dist/client/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC","sourcesContent":["export * from './auth.js';\nexport * from './claims.js';\n"]}
|
|
@@ -1,20 +1,7 @@
|
|
|
1
|
-
/** The `details.reason` of the refusal a caller without a verified email receives. */
|
|
2
1
|
export declare const EMAIL_VERIFICATION_REQUIRED_REASON: "email-verification-required";
|
|
3
|
-
/**
|
|
4
|
-
* Structured details carried by the email-verification refusal. Server side it rides
|
|
5
|
-
* `AuthAssertionError.details`; the consuming app forwards it as the `HttpsError` details, so
|
|
6
|
-
* the client receives it on the callable error's `details`.
|
|
7
|
-
*/
|
|
8
2
|
export interface EmailVerificationRequiredDetails {
|
|
9
3
|
readonly reason: typeof EMAIL_VERIFICATION_REQUIRED_REASON;
|
|
10
4
|
}
|
|
11
|
-
/** True when `details` is the email-verification refusal's structured details. */
|
|
12
5
|
export declare function isEmailVerificationRequiredDetails(details: unknown): details is EmailVerificationRequiredDetails;
|
|
13
|
-
/**
|
|
14
|
-
* True when `err` is the email-verification refusal — a `failed-precondition` whose details
|
|
15
|
-
* carry `reason: 'email-verification-required'`. Works on the server-side error and on the
|
|
16
|
-
* client-side callable error alike, so the app has one recognizer for "show the verify-email
|
|
17
|
-
* surface".
|
|
18
|
-
*/
|
|
19
6
|
export declare function isEmailVerificationRequiredError(err: unknown): boolean;
|
|
20
7
|
//# sourceMappingURL=email-verification.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"email-verification.d.ts","sourceRoot":"","sources":["../src/email-verification.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"email-verification.d.ts","sourceRoot":"","sources":["../src/email-verification.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,kCAAkC,EAAG,6BAAsC,CAAC;AAEzF,MAAM,WAAW,gCAAgC;IAC/C,QAAQ,CAAC,MAAM,EAAE,OAAO,kCAAkC,CAAC;CAC5D;AAED,wBAAgB,kCAAkC,CAChD,OAAO,EAAE,OAAO,GACf,OAAO,IAAI,gCAAgC,CAG7C;AAED,wBAAgB,gCAAgC,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAEtE"}
|
|
@@ -1,26 +1,10 @@
|
|
|
1
|
-
// The email-verification refusal contract shared by the server gate (`createAssertAuth`'s
|
|
2
|
-
// `emailVerified` requirement on ./server) and the client that answers it. Pure: no Firebase
|
|
3
|
-
// runtime.
|
|
4
|
-
//
|
|
5
|
-
// A callable that requires a verified email refuses a caller whose ID token is not
|
|
6
|
-
// email-verified. The package owns that refusal, so it owns its one recognisable shape: a
|
|
7
|
-
// `failed-precondition` whose details name the reason. The client tells it apart from every
|
|
8
|
-
// other failed-precondition by that reason — never by the message text.
|
|
9
1
|
import { failedPreconditionDetails } from './refusal.js';
|
|
10
|
-
/** The `details.reason` of the refusal a caller without a verified email receives. */
|
|
11
2
|
export const EMAIL_VERIFICATION_REQUIRED_REASON = 'email-verification-required';
|
|
12
|
-
/** True when `details` is the email-verification refusal's structured details. */
|
|
13
3
|
export function isEmailVerificationRequiredDetails(details) {
|
|
14
4
|
if (!details || typeof details !== 'object')
|
|
15
5
|
return false;
|
|
16
6
|
return details.reason === EMAIL_VERIFICATION_REQUIRED_REASON;
|
|
17
7
|
}
|
|
18
|
-
/**
|
|
19
|
-
* True when `err` is the email-verification refusal — a `failed-precondition` whose details
|
|
20
|
-
* carry `reason: 'email-verification-required'`. Works on the server-side error and on the
|
|
21
|
-
* client-side callable error alike, so the app has one recognizer for "show the verify-email
|
|
22
|
-
* surface".
|
|
23
|
-
*/
|
|
24
8
|
export function isEmailVerificationRequiredError(err) {
|
|
25
9
|
return isEmailVerificationRequiredDetails(failedPreconditionDetails(err));
|
|
26
10
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"email-verification.js","sourceRoot":"","sources":["../src/email-verification.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"email-verification.js","sourceRoot":"","sources":["../src/email-verification.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAC;AAEzD,MAAM,CAAC,MAAM,kCAAkC,GAAG,6BAAsC,CAAC;AAMzF,MAAM,UAAU,kCAAkC,CAChD,OAAgB;IAEhB,IAAI,CAAC,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC1D,OAAQ,OAAmC,CAAC,MAAM,KAAK,kCAAkC,CAAC;AAC5F,CAAC;AAED,MAAM,UAAU,gCAAgC,CAAC,GAAY;IAC3D,OAAO,kCAAkC,CAAC,yBAAyB,CAAC,GAAG,CAAC,CAAC,CAAC;AAC5E,CAAC","sourcesContent":["import { failedPreconditionDetails } from './refusal.js';\n\nexport const EMAIL_VERIFICATION_REQUIRED_REASON = 'email-verification-required' as const;\n\nexport interface EmailVerificationRequiredDetails {\n readonly reason: typeof EMAIL_VERIFICATION_REQUIRED_REASON;\n}\n\nexport function isEmailVerificationRequiredDetails(\n details: unknown,\n): details is EmailVerificationRequiredDetails {\n if (!details || typeof details !== 'object') return false;\n return (details as Record<string, unknown>).reason === EMAIL_VERIFICATION_REQUIRED_REASON;\n}\n\nexport function isEmailVerificationRequiredError(err: unknown): boolean {\n return isEmailVerificationRequiredDetails(failedPreconditionDetails(err));\n}\n"]}
|
package/dist/errors.d.ts
CHANGED
|
@@ -5,9 +5,7 @@ export type NormalizedAuthError = {
|
|
|
5
5
|
firebaseCode?: string;
|
|
6
6
|
details?: Record<string, unknown>;
|
|
7
7
|
};
|
|
8
|
-
/** The copy for every auth failure no mapped code covers. */
|
|
9
8
|
export declare const UNKNOWN_AUTH_ERROR_MESSAGE = "Authentication error. Please try again.";
|
|
10
9
|
export declare function normalizeAuthError(err: unknown, details?: Record<string, unknown>): NormalizedAuthError;
|
|
11
|
-
/** Convenience: returns just the user-facing message string */
|
|
12
10
|
export declare function getErrorMessage(error: unknown): string;
|
|
13
11
|
//# sourceMappingURL=errors.d.ts.map
|
package/dist/errors.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,aAAa,GACrB,cAAc,GACd,oBAAoB,GACpB,wBAAwB,GACxB,mBAAmB,GACnB,oBAAoB,GACpB,gBAAgB,GAChB,oBAAoB,GACpB,qBAAqB,GACrB,qBAAqB,GACrB,oBAAoB,GACpB,2BAA2B,GAC3B,oBAAoB,GACpB,4BAA4B,GAC5B,yBAAyB,GACzB,+CAA+C,GAC/C,8BAA8B,GAC9B,gCAAgC,GAChC,0BAA0B,GAC1B,0BAA0B,CAAC;AAE/B,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,EAAE,aAAa,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC,CAAC;
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,aAAa,GACrB,cAAc,GACd,oBAAoB,GACpB,wBAAwB,GACxB,mBAAmB,GACnB,oBAAoB,GACpB,gBAAgB,GAChB,oBAAoB,GACpB,qBAAqB,GACrB,qBAAqB,GACrB,oBAAoB,GACpB,2BAA2B,GAC3B,oBAAoB,GACpB,4BAA4B,GAC5B,yBAAyB,GACzB,+CAA+C,GAC/C,8BAA8B,GAC9B,gCAAgC,GAChC,0BAA0B,GAC1B,0BAA0B,CAAC;AAE/B,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,EAAE,aAAa,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC,CAAC;AAqFF,eAAO,MAAM,0BAA0B,4CAA4C,CAAC;AAEpF,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,OAAO,EACZ,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAChC,mBAAmB,CAoBrB;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAEtD"}
|
package/dist/errors.js
CHANGED
|
@@ -70,9 +70,6 @@ const MAP = {
|
|
|
70
70
|
code: 'AUTH_CREDENTIAL_ALREADY_IN_USE',
|
|
71
71
|
message: 'Those credentials are already associated with another account.',
|
|
72
72
|
},
|
|
73
|
-
// Email-action (oobCode) links — password reset, email verification, email recovery.
|
|
74
|
-
// The copy is deliberately generic: it never reveals whether an account exists, and it
|
|
75
|
-
// always points at the one recovery affordance (request a fresh link).
|
|
76
73
|
'auth/expired-action-code': {
|
|
77
74
|
code: 'AUTH_EXPIRED_ACTION_CODE',
|
|
78
75
|
message: 'This email action link has expired. Request a new link and try again.',
|
|
@@ -82,7 +79,6 @@ const MAP = {
|
|
|
82
79
|
message: 'This email action link is invalid or has already been used. Request a new link and try again.',
|
|
83
80
|
},
|
|
84
81
|
};
|
|
85
|
-
/** The copy for every auth failure no mapped code covers. */
|
|
86
82
|
export const UNKNOWN_AUTH_ERROR_MESSAGE = 'Authentication error. Please try again.';
|
|
87
83
|
export function normalizeAuthError(err, details) {
|
|
88
84
|
const firebaseCode = pickFirebaseCode(err);
|
|
@@ -95,9 +91,7 @@ export function normalizeAuthError(err, details) {
|
|
|
95
91
|
details,
|
|
96
92
|
};
|
|
97
93
|
}
|
|
98
|
-
// Never the error's own message: for an unclassified failure it is the SDK's internal diagnostic
|
|
99
|
-
// ("Firebase: Cloud function deadline exceeded. (auth/internal-error)."), not an answer the person
|
|
100
|
-
// can act on. The consumer reports the error itself to its monitoring.
|
|
94
|
+
// Never the error's own message: for an unclassified failure it is the SDK's internal diagnostic.
|
|
101
95
|
return {
|
|
102
96
|
code: 'AUTH_UNKNOWN',
|
|
103
97
|
message: UNKNOWN_AUTH_ERROR_MESSAGE,
|
|
@@ -105,7 +99,6 @@ export function normalizeAuthError(err, details) {
|
|
|
105
99
|
details,
|
|
106
100
|
};
|
|
107
101
|
}
|
|
108
|
-
/** Convenience: returns just the user-facing message string */
|
|
109
102
|
export function getErrorMessage(error) {
|
|
110
103
|
return normalizeAuthError(error).message;
|
|
111
104
|
}
|
package/dist/errors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AA4BA,SAAS,gBAAgB,CAAC,GAAY;IACpC,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IACtD,MAAM,MAAM,GAAG,GAAU,CAAC;IAC1B,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC;IACtB,OAAO,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AAC/C,CAAC;AAED,MAAM,GAAG,GAA6D;IACpE,6BAA6B,EAAE;QAC7B,IAAI,EAAE,oBAAoB;QAC1B,OAAO,EAAE,4DAA4D;KACtE;IACD,wBAAwB,EAAE;QACxB,IAAI,EAAE,wBAAwB;QAC9B,OAAO,EAAE,wDAAwD;KAClE;IACD,2BAA2B,EAAE;QAC3B,IAAI,EAAE,mBAAmB;QACzB,OAAO,EAAE,2BAA2B;KACrC;IACD,oBAAoB,EAAE;QACpB,IAAI,EAAE,oBAAoB;QAC1B,OAAO,EAAE,2CAA2C;KACrD;IACD,8BAA8B,EAAE;QAC9B,IAAI,EAAE,gBAAgB;QACtB,OAAO,EAAE,wBAAwB;KAClC;IACD,oBAAoB,EAAE;QACpB,IAAI,EAAE,oBAAoB;QAC1B,OAAO,EAAE,iCAAiC;KAC3C;IACD,qBAAqB,EAAE;QACrB,IAAI,EAAE,qBAAqB;QAC3B,OAAO,EAAE,mCAAmC;KAC7C;IACD,qBAAqB,EAAE;QACrB,IAAI,EAAE,qBAAqB;QAC3B,OAAO,EAAE,qBAAqB;KAC/B;IACD,oBAAoB,EAAE;QACpB,IAAI,EAAE,oBAAoB;QAC1B,OAAO,EAAE,wBAAwB;KAClC;IACD,2BAA2B,EAAE;QAC3B,IAAI,EAAE,2BAA2B;QACjC,OAAO,EAAE,+BAA+B;KACzC;IACD,oBAAoB,EAAE;QACpB,IAAI,EAAE,oBAAoB;QAC1B,OAAO,EAAE,uBAAuB;KACjC;IACD,4BAA4B,EAAE;QAC5B,IAAI,EAAE,4BAA4B;QAClC,OAAO,EAAE,uCAAuC;KACjD;IACD,yBAAyB,EAAE;QACzB,IAAI,EAAE,yBAAyB;QAC/B,OAAO,EAAE,wCAAwC;KAClD;IACD,+CAA+C,EAAE;QAC/C,IAAI,EAAE,+CAA+C;QACrD,OAAO,EAAE,4DAA4D;KACtE;IACD,8BAA8B,EAAE;QAC9B,IAAI,EAAE,8BAA8B;QACpC,OAAO,EAAE,kDAAkD;KAC5D;IACD,gCAAgC,EAAE;QAChC,IAAI,EAAE,gCAAgC;QACtC,OAAO,EAAE,gEAAgE;KAC1E;IACD,
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AA4BA,SAAS,gBAAgB,CAAC,GAAY;IACpC,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IACtD,MAAM,MAAM,GAAG,GAAU,CAAC;IAC1B,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC;IACtB,OAAO,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AAC/C,CAAC;AAED,MAAM,GAAG,GAA6D;IACpE,6BAA6B,EAAE;QAC7B,IAAI,EAAE,oBAAoB;QAC1B,OAAO,EAAE,4DAA4D;KACtE;IACD,wBAAwB,EAAE;QACxB,IAAI,EAAE,wBAAwB;QAC9B,OAAO,EAAE,wDAAwD;KAClE;IACD,2BAA2B,EAAE;QAC3B,IAAI,EAAE,mBAAmB;QACzB,OAAO,EAAE,2BAA2B;KACrC;IACD,oBAAoB,EAAE;QACpB,IAAI,EAAE,oBAAoB;QAC1B,OAAO,EAAE,2CAA2C;KACrD;IACD,8BAA8B,EAAE;QAC9B,IAAI,EAAE,gBAAgB;QACtB,OAAO,EAAE,wBAAwB;KAClC;IACD,oBAAoB,EAAE;QACpB,IAAI,EAAE,oBAAoB;QAC1B,OAAO,EAAE,iCAAiC;KAC3C;IACD,qBAAqB,EAAE;QACrB,IAAI,EAAE,qBAAqB;QAC3B,OAAO,EAAE,mCAAmC;KAC7C;IACD,qBAAqB,EAAE;QACrB,IAAI,EAAE,qBAAqB;QAC3B,OAAO,EAAE,qBAAqB;KAC/B;IACD,oBAAoB,EAAE;QACpB,IAAI,EAAE,oBAAoB;QAC1B,OAAO,EAAE,wBAAwB;KAClC;IACD,2BAA2B,EAAE;QAC3B,IAAI,EAAE,2BAA2B;QACjC,OAAO,EAAE,+BAA+B;KACzC;IACD,oBAAoB,EAAE;QACpB,IAAI,EAAE,oBAAoB;QAC1B,OAAO,EAAE,uBAAuB;KACjC;IACD,4BAA4B,EAAE;QAC5B,IAAI,EAAE,4BAA4B;QAClC,OAAO,EAAE,uCAAuC;KACjD;IACD,yBAAyB,EAAE;QACzB,IAAI,EAAE,yBAAyB;QAC/B,OAAO,EAAE,wCAAwC;KAClD;IACD,+CAA+C,EAAE;QAC/C,IAAI,EAAE,+CAA+C;QACrD,OAAO,EAAE,4DAA4D;KACtE;IACD,8BAA8B,EAAE;QAC9B,IAAI,EAAE,8BAA8B;QACpC,OAAO,EAAE,kDAAkD;KAC5D;IACD,gCAAgC,EAAE;QAChC,IAAI,EAAE,gCAAgC;QACtC,OAAO,EAAE,gEAAgE;KAC1E;IACD,0BAA0B,EAAE;QAC1B,IAAI,EAAE,0BAA0B;QAChC,OAAO,EAAE,uEAAuE;KACjF;IACD,0BAA0B,EAAE;QAC1B,IAAI,EAAE,0BAA0B;QAChC,OAAO,EACL,+FAA+F;KAClG;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,0BAA0B,GAAG,yCAAyC,CAAC;AAEpF,MAAM,UAAU,kBAAkB,CAChC,GAAY,EACZ,OAAiC;IAEjC,MAAM,YAAY,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;IAE3C,MAAM,MAAM,GAAG,YAAY,CAAC,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC5D,IAAI,MAAM,EAAE,CAAC;QACX,OAAO;YACL,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,YAAY;YACZ,OAAO;SACR,CAAC;IACJ,CAAC;IAED,kGAAkG;IAClG,OAAO;QACL,IAAI,EAAE,cAAc;QACpB,OAAO,EAAE,0BAA0B;QACnC,YAAY;QACZ,OAAO;KACR,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,KAAc;IAC5C,OAAO,kBAAkB,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC;AAC3C,CAAC","sourcesContent":["export type AuthErrorCode =\n | 'AUTH_UNKNOWN'\n | 'AUTH_NETWORK_ERROR'\n | 'AUTH_TOO_MANY_REQUESTS'\n | 'AUTH_POPUP_CLOSED'\n | 'AUTH_POPUP_BLOCKED'\n | 'AUTH_CANCELLED'\n | 'AUTH_USER_DISABLED'\n | 'AUTH_USER_NOT_FOUND'\n | 'AUTH_WRONG_PASSWORD'\n | 'AUTH_INVALID_EMAIL'\n | 'AUTH_EMAIL_ALREADY_IN_USE'\n | 'AUTH_WEAK_PASSWORD'\n | 'AUTH_REQUIRES_RECENT_LOGIN'\n | 'AUTH_INVALID_CREDENTIAL'\n | 'AUTH_ACCOUNT_EXISTS_WITH_DIFFERENT_CREDENTIAL'\n | 'AUTH_PROVIDER_ALREADY_LINKED'\n | 'AUTH_CREDENTIAL_ALREADY_IN_USE'\n | 'AUTH_EXPIRED_ACTION_CODE'\n | 'AUTH_INVALID_ACTION_CODE';\n\nexport type NormalizedAuthError = {\n code: AuthErrorCode;\n message: string;\n firebaseCode?: string;\n details?: Record<string, unknown>;\n};\n\nfunction pickFirebaseCode(err: unknown): string | undefined {\n if (!err || typeof err !== 'object') return undefined;\n const anyErr = err as any;\n const c = anyErr.code;\n return typeof c === 'string' ? c : undefined;\n}\n\nconst MAP: Record<string, { code: AuthErrorCode; message: string }> = {\n 'auth/network-request-failed': {\n code: 'AUTH_NETWORK_ERROR',\n message: 'Network error. Please check your connection and try again.',\n },\n 'auth/too-many-requests': {\n code: 'AUTH_TOO_MANY_REQUESTS',\n message: 'Too many attempts. Please wait a moment and try again.',\n },\n 'auth/popup-closed-by-user': {\n code: 'AUTH_POPUP_CLOSED',\n message: 'Sign-in popup was closed.',\n },\n 'auth/popup-blocked': {\n code: 'AUTH_POPUP_BLOCKED',\n message: 'Sign-in popup was blocked by the browser.',\n },\n 'auth/cancelled-popup-request': {\n code: 'AUTH_CANCELLED',\n message: 'Sign-in was cancelled.',\n },\n 'auth/user-disabled': {\n code: 'AUTH_USER_DISABLED',\n message: 'This account has been disabled.',\n },\n 'auth/user-not-found': {\n code: 'AUTH_USER_NOT_FOUND',\n message: 'No account found with that email.',\n },\n 'auth/wrong-password': {\n code: 'AUTH_WRONG_PASSWORD',\n message: 'Incorrect password.',\n },\n 'auth/invalid-email': {\n code: 'AUTH_INVALID_EMAIL',\n message: 'Invalid email address.',\n },\n 'auth/email-already-in-use': {\n code: 'AUTH_EMAIL_ALREADY_IN_USE',\n message: 'That email is already in use.',\n },\n 'auth/weak-password': {\n code: 'AUTH_WEAK_PASSWORD',\n message: 'Password is too weak.',\n },\n 'auth/requires-recent-login': {\n code: 'AUTH_REQUIRES_RECENT_LOGIN',\n message: 'Please re-authenticate and try again.',\n },\n 'auth/invalid-credential': {\n code: 'AUTH_INVALID_CREDENTIAL',\n message: 'Invalid credentials. Please try again.',\n },\n 'auth/account-exists-with-different-credential': {\n code: 'AUTH_ACCOUNT_EXISTS_WITH_DIFFERENT_CREDENTIAL',\n message: 'An account already exists with a different sign-in method.',\n },\n 'auth/provider-already-linked': {\n code: 'AUTH_PROVIDER_ALREADY_LINKED',\n message: 'That provider is already linked to this account.',\n },\n 'auth/credential-already-in-use': {\n code: 'AUTH_CREDENTIAL_ALREADY_IN_USE',\n message: 'Those credentials are already associated with another account.',\n },\n 'auth/expired-action-code': {\n code: 'AUTH_EXPIRED_ACTION_CODE',\n message: 'This email action link has expired. Request a new link and try again.',\n },\n 'auth/invalid-action-code': {\n code: 'AUTH_INVALID_ACTION_CODE',\n message:\n 'This email action link is invalid or has already been used. Request a new link and try again.',\n },\n};\n\nexport const UNKNOWN_AUTH_ERROR_MESSAGE = 'Authentication error. Please try again.';\n\nexport function normalizeAuthError(\n err: unknown,\n details?: Record<string, unknown>,\n): NormalizedAuthError {\n const firebaseCode = pickFirebaseCode(err);\n\n const mapped = firebaseCode ? MAP[firebaseCode] : undefined;\n if (mapped) {\n return {\n code: mapped.code,\n message: mapped.message,\n firebaseCode,\n details,\n };\n }\n\n // Never the error's own message: for an unclassified failure it is the SDK's internal diagnostic.\n return {\n code: 'AUTH_UNKNOWN',\n message: UNKNOWN_AUTH_ERROR_MESSAGE,\n firebaseCode,\n details,\n };\n}\n\nexport function getErrorMessage(error: unknown): string {\n return normalizeAuthError(error).message;\n}\n"]}
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC;AACzB,cAAc,iBAAiB,CAAC;AAChC,cAAc,yBAAyB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
// Pure root: contracts, normalized errors, environment helpers.
|
|
2
|
-
// Nothing here imports firebase/auth at runtime.
|
|
3
|
-
//
|
|
4
|
-
// Client auth runtime (onAuthStateChanged wrapper, getAuthUser, ID-token claims) lives behind
|
|
5
|
-
// "@ttt-productions/auth-core/client". Admin/Functions helpers live behind
|
|
6
|
-
// "@ttt-productions/auth-core/server". React hooks live behind
|
|
7
|
-
// "@ttt-productions/auth-core/react".
|
|
8
1
|
export * from './errors.js';
|
|
9
2
|
export * from './env.js';
|
|
10
3
|
export * from './acceptance.js';
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC;AACzB,cAAc,iBAAiB,CAAC;AAChC,cAAc,yBAAyB,CAAC","sourcesContent":["export * from './errors.js';\nexport * from './env.js';\nexport * from './acceptance.js';\nexport * from './email-verification.js';\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AuthProvider.d.ts","sourceRoot":"","sources":["../../src/react/AuthProvider.tsx"],"names":[],"mappings":"AAEA,OAAO,EAOL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAIf,OAAO,KAAK,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"AuthProvider.d.ts","sourceRoot":"","sources":["../../src/react/AuthProvider.tsx"],"names":[],"mappings":"AAEA,OAAO,EAOL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAIf,OAAO,KAAK,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAQvE,eAAO,MAAM,WAAW,uDAAoD,CAAC;AAE7E,UAAU,iBAAiB,CAAC,OAAO;IACjC,MAAM,EAAE,kBAAkB,CAAC,OAAO,CAAC,CAAC;IACpC,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,wBAAgB,YAAY,CAAC,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAC9D,MAAM,EACN,QAAQ,GACT,EAAE,iBAAiB,CAAC,OAAO,CAAC,+BAwG5B"}
|
|
@@ -6,22 +6,13 @@ import { getIdTokenClaims } from '../client/claims.js';
|
|
|
6
6
|
export const AuthContext = createContext(null);
|
|
7
7
|
export function AuthProvider({ config, children, }) {
|
|
8
8
|
const { user, loading: authLoading } = useAuthState(config.auth);
|
|
9
|
-
// Claims are held with the session they were read for and derived at render (below), so a
|
|
10
|
-
// render can never pair one account's `user` with another account's claims, and a read that
|
|
11
|
-
// settles after the session changed (a late refresh, a slow initial read) is dropped.
|
|
12
9
|
const [resolved, setResolved] = useState(null);
|
|
13
10
|
const generationRef = useRef(0);
|
|
14
11
|
const appliedGenerationRef = useRef(0);
|
|
15
12
|
const currentUserRef = useRef(user);
|
|
16
13
|
currentUserRef.current = user;
|
|
17
|
-
// Stable config ref so effects don't re-fire on config object identity changes
|
|
18
14
|
const configRef = useRef(config);
|
|
19
15
|
configRef.current = config;
|
|
20
|
-
/**
|
|
21
|
-
* Read `forUser`'s claims under a new generation and apply them only if `forUser` is still
|
|
22
|
-
* the signed-in session and no newer read has applied. A failed read leaves the session's
|
|
23
|
-
* claims as they are, or settles an unread session on the default claims.
|
|
24
|
-
*/
|
|
25
16
|
const readClaims = useCallback(async (forUser, read, context) => {
|
|
26
17
|
const generation = ++generationRef.current;
|
|
27
18
|
try {
|
|
@@ -44,9 +35,6 @@ export function AuthProvider({ config, children, }) {
|
|
|
44
35
|
: { user: forUser, generation, claims: configRef.current.defaultClaims });
|
|
45
36
|
}
|
|
46
37
|
}, []);
|
|
47
|
-
// --- Optional readiness gate (see AuthProviderConfig.readyGate) ---
|
|
48
|
-
// Open immediately when no gate is configured; otherwise held closed until the
|
|
49
|
-
// gate settles. Fail-open: a rejected gate is reported and then treated as open.
|
|
50
38
|
const [gateOpen, setGateOpen] = useState(() => !config.readyGate);
|
|
51
39
|
useEffect(() => {
|
|
52
40
|
const gate = configRef.current.readyGate;
|
|
@@ -63,10 +51,7 @@ export function AuthProvider({ config, children, }) {
|
|
|
63
51
|
return () => {
|
|
64
52
|
cancelled = true;
|
|
65
53
|
};
|
|
66
|
-
// Mount-only by design (reads only the stable configRef): the gate is a
|
|
67
|
-
// one-time client readiness signal.
|
|
68
54
|
}, []);
|
|
69
|
-
// --- Claims fetching ---
|
|
70
55
|
useEffect(() => {
|
|
71
56
|
if (authLoading)
|
|
72
57
|
return;
|
|
@@ -78,26 +63,17 @@ export function AuthProvider({ config, children, }) {
|
|
|
78
63
|
configRef.current.onAuthStateChange?.(user);
|
|
79
64
|
void readClaims(user, async () => ((await getIdTokenClaims(user)) ?? {}), 'getIdTokenClaims');
|
|
80
65
|
}, [user, authLoading, readClaims]);
|
|
81
|
-
// --- refreshClaims ---
|
|
82
66
|
const refreshClaims = useCallback(async () => {
|
|
83
|
-
//
|
|
84
|
-
// before its own sign-in (a registration's submit handler creates the account and then asks for
|
|
85
|
-
// the claims the server just set), and a refresh of that render's `null` user would do nothing —
|
|
86
|
-
// leaving the new account on its pre-claim token until the hourly refresh.
|
|
67
|
+
// `auth.currentUser`, not this render's user: a caller can hold this callback from before its own sign-in.
|
|
87
68
|
const liveUser = configRef.current.auth.currentUser;
|
|
88
69
|
if (!liveUser)
|
|
89
70
|
return;
|
|
90
71
|
await readClaims(liveUser, async () => ((await liveUser.getIdTokenResult(true)).claims ?? {}), 'refreshClaims');
|
|
91
72
|
}, [readClaims]);
|
|
92
|
-
// --- Derived at render: only the current session's claims are ever exposed ---
|
|
93
73
|
const sessionClaims = user && resolved?.user === user ? resolved : null;
|
|
94
74
|
const claimsLoading = authLoading || (user !== null && sessionClaims === null);
|
|
95
|
-
// While the gate is closed a restored session is held back from consumers: a uid-keyed
|
|
96
|
-
// subscription opened before the gate's readiness signal is rejected once and dies, so no
|
|
97
|
-
// consumer may see a uid (or that session's claims) until the gate opens or fails open.
|
|
98
75
|
const exposedUser = gateOpen ? user : null;
|
|
99
76
|
const claims = exposedUser && sessionClaims ? sessionClaims.claims : config.defaultClaims;
|
|
100
|
-
// --- Context value ---
|
|
101
77
|
const loading = authLoading || claimsLoading || !gateOpen;
|
|
102
78
|
const value = useMemo(() => ({
|
|
103
79
|
user: exposedUser,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AuthProvider.js","sourceRoot":"","sources":["../../src/react/AuthProvider.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAO,EACL,aAAa,EACb,SAAS,EACT,MAAM,EACN,QAAQ,EACR,WAAW,EACX,OAAO,GAER,MAAM,OAAO,CAAC;AAEf,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAavD,MAAM,CAAC,MAAM,WAAW,GAAG,aAAa,CAA+B,IAAI,CAAC,CAAC;AAO7E,MAAM,UAAU,YAAY,CAAoC,EAC9D,MAAM,EACN,QAAQ,GACmB;IAC3B,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAEjE,0FAA0F;IAC1F,4FAA4F;IAC5F,sFAAsF;IACtF,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAiC,IAAI,CAAC,CAAC;IAC/E,MAAM,aAAa,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IAChC,MAAM,oBAAoB,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IACvC,MAAM,cAAc,GAAG,MAAM,CAAc,IAAI,CAAC,CAAC;IACjD,cAAc,CAAC,OAAO,GAAG,IAAI,CAAC;IAE9B,+EAA+E;IAC/E,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;IACjC,SAAS,CAAC,OAAO,GAAG,MAAM,CAAC;IAE3B;;;;OAIG;IACH,MAAM,UAAU,GAAG,WAAW,CAC5B,KAAK,EAAE,OAAa,EAAE,IAAmD,EAAE,OAAe,EAAE,EAAE;QAC5F,MAAM,UAAU,GAAG,EAAE,aAAa,CAAC,OAAO,CAAC;QAC3C,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,IAAI,EAAE,CAAC;YACzB,IAAI,cAAc,CAAC,OAAO,KAAK,OAAO,IAAI,UAAU,IAAI,oBAAoB,CAAC,OAAO;gBAClF,OAAO;YACT,oBAAoB,CAAC,OAAO,GAAG,UAAU,CAAC;YAC1C,WAAW,CAAC;gBACV,IAAI,EAAE,OAAO;gBACb,UAAU;gBACV,MAAM,EAAE,SAAS,CAAC,OAAO,CAAC,WAAW,CAAC,GAAG,IAAI,EAAE,CAAC;aACjD,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,cAAc,CAAC,OAAO,KAAK,OAAO;gBAAE,OAAO;YAC/C,SAAS,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;YAC1C,WAAW,CAAC,CAAC,IAAI,EAAE,EAAE,CACnB,IAAI,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO;gBAC3B,CAAC,CAAC,IAAI;gBACN,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,CAAC,OAAO,CAAC,aAAa,EAAE,CAC3E,CAAC;QACJ,CAAC;IACH,CAAC,EACD,EAAE,CACH,CAAC;IAEF,qEAAqE;IACrE,+EAA+E;IAC/E,iFAAiF;IACjF,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAClE,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,IAAI,GAAG,SAAS,CAAC,OAAO,CAAC,SAAS,CAAC;QACzC,IAAI,CAAC,IAAI;YAAE,OAAO;QAClB,IAAI,SAAS,GAAG,KAAK,CAAC;QACtB,OAAO,CAAC,OAAO,EAAE;aACd,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,CAAC;aAClB,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;aAC7D,OAAO,CAAC,GAAG,EAAE;YACZ,IAAI,CAAC,SAAS;gBAAE,WAAW,CAAC,IAAI,CAAC,CAAC;QACpC,CAAC,CAAC,CAAC;QACL,OAAO,GAAG,EAAE;YACV,SAAS,GAAG,IAAI,CAAC;QACnB,CAAC,CAAC;QACF,wEAAwE;QACxE,oCAAoC;IACtC,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,0BAA0B;IAC1B,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,WAAW;YAAE,OAAO;QAExB,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,WAAW,CAAC,IAAI,CAAC,CAAC;YAClB,SAAS,CAAC,OAAO,CAAC,iBAAiB,EAAE,CAAC,IAAI,CAAC,CAAC;YAC5C,OAAO;QACT,CAAC;QAED,SAAS,CAAC,OAAO,CAAC,iBAAiB,EAAE,CAAC,IAAI,CAAC,CAAC;QAC5C,KAAK,UAAU,CACb,IAAI,EACJ,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC,MAAM,gBAAgB,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAA4B,EAC7E,kBAAkB,CACnB,CAAC;IACJ,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,EAAE,UAAU,CAAC,CAAC,CAAC;IAEpC,wBAAwB;IACxB,MAAM,aAAa,GAAG,WAAW,CAAC,KAAK,IAAI,EAAE;QAC3C,+FAA+F;QAC/F,gGAAgG;QAChG,iGAAiG;QACjG,2EAA2E;QAC3E,MAAM,QAAQ,GAAG,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC;QACpD,IAAI,CAAC,QAAQ;YAAE,OAAO;QACtB,MAAM,UAAU,CACd,QAAQ,EACR,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC,MAAM,QAAQ,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,IAAI,EAAE,CAA4B,EAC7F,eAAe,CAChB,CAAC;IACJ,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC;IAEjB,gFAAgF;IAChF,MAAM,aAAa,GAAG,IAAI,IAAI,QAAQ,EAAE,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC;IACxE,MAAM,aAAa,GAAG,WAAW,IAAI,CAAC,IAAI,KAAK,IAAI,IAAI,aAAa,KAAK,IAAI,CAAC,CAAC;IAE/E,uFAAuF;IACvF,0FAA0F;IAC1F,wFAAwF;IACxF,MAAM,WAAW,GAAG,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;IAC3C,MAAM,MAAM,GAAG,WAAW,IAAI,aAAa,CAAC,CAAC,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC;IAE1F,wBAAwB;IACxB,MAAM,OAAO,GAAG,WAAW,IAAI,aAAa,IAAI,CAAC,QAAQ,CAAC;IAE1D,MAAM,KAAK,GAAG,OAAO,CACnB,GAAG,EAAE,CAAC,CAAC;QACL,IAAI,EAAE,WAAW;QACjB,MAAM;QACN,OAAO;QACP,WAAW;QACX,aAAa;QACb,eAAe,EAAE,CAAC,CAAC,WAAW;QAC9B,aAAa;KACd,CAAC,EACF,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,aAAa,CAAC,CAC1E,CAAC;IAEF,OAAO,KAAC,WAAW,CAAC,QAAQ,IAAC,KAAK,EAAE,KAAK,YAAG,QAAQ,GAAwB,CAAC;AAC/E,CAAC","sourcesContent":["'use client';\n\nimport {\n createContext,\n useEffect,\n useRef,\n useState,\n useCallback,\n useMemo,\n type ReactNode,\n} from 'react';\nimport type { User } from 'firebase/auth';\nimport { useAuthState } from './useAuthState.js';\nimport { getIdTokenClaims } from '../client/claims.js';\nimport type { AuthProviderConfig, AuthContextValue } from './types.js';\n\n/**\n * Claims resolved for one signed-in session. `user` is the Firebase `User` instance (it carries\n * the uid and is replaced on every sign-in), and `generation` orders the reads that produced it.\n */\ninterface ResolvedClaims<TClaims> {\n user: User;\n generation: number;\n claims: TClaims;\n}\n\nexport const AuthContext = createContext<AuthContextValue<any> | null>(null);\n\ninterface AuthProviderProps<TClaims> {\n config: AuthProviderConfig<TClaims>;\n children: ReactNode;\n}\n\nexport function AuthProvider<TClaims = Record<string, unknown>>({\n config,\n children,\n}: AuthProviderProps<TClaims>) {\n const { user, loading: authLoading } = useAuthState(config.auth);\n\n // Claims are held with the session they were read for and derived at render (below), so a\n // render can never pair one account's `user` with another account's claims, and a read that\n // settles after the session changed (a late refresh, a slow initial read) is dropped.\n const [resolved, setResolved] = useState<ResolvedClaims<TClaims> | null>(null);\n const generationRef = useRef(0);\n const appliedGenerationRef = useRef(0);\n const currentUserRef = useRef<User | null>(user);\n currentUserRef.current = user;\n\n // Stable config ref so effects don't re-fire on config object identity changes\n const configRef = useRef(config);\n configRef.current = config;\n\n /**\n * Read `forUser`'s claims under a new generation and apply them only if `forUser` is still\n * the signed-in session and no newer read has applied. A failed read leaves the session's\n * claims as they are, or settles an unread session on the default claims.\n */\n const readClaims = useCallback(\n async (forUser: User, read: () => Promise<Record<string, unknown> | null>, context: string) => {\n const generation = ++generationRef.current;\n try {\n const raw = await read();\n if (currentUserRef.current !== forUser || generation <= appliedGenerationRef.current)\n return;\n appliedGenerationRef.current = generation;\n setResolved({\n user: forUser,\n generation,\n claims: configRef.current.parseClaims(raw ?? {}),\n });\n } catch (err) {\n if (currentUserRef.current !== forUser) return;\n configRef.current.onError?.(err, context);\n setResolved((prev) =>\n prev && prev.user === forUser\n ? prev\n : { user: forUser, generation, claims: configRef.current.defaultClaims },\n );\n }\n },\n [],\n );\n\n // --- Optional readiness gate (see AuthProviderConfig.readyGate) ---\n // Open immediately when no gate is configured; otherwise held closed until the\n // gate settles. Fail-open: a rejected gate is reported and then treated as open.\n const [gateOpen, setGateOpen] = useState(() => !config.readyGate);\n useEffect(() => {\n const gate = configRef.current.readyGate;\n if (!gate) return;\n let cancelled = false;\n Promise.resolve()\n .then(() => gate())\n .catch((err) => configRef.current.onError?.(err, 'readyGate'))\n .finally(() => {\n if (!cancelled) setGateOpen(true);\n });\n return () => {\n cancelled = true;\n };\n // Mount-only by design (reads only the stable configRef): the gate is a\n // one-time client readiness signal.\n }, []);\n\n // --- Claims fetching ---\n useEffect(() => {\n if (authLoading) return;\n\n if (!user) {\n setResolved(null);\n configRef.current.onAuthStateChange?.(null);\n return;\n }\n\n configRef.current.onAuthStateChange?.(user);\n void readClaims(\n user,\n async () => ((await getIdTokenClaims(user)) ?? {}) as Record<string, unknown>,\n 'getIdTokenClaims',\n );\n }, [user, authLoading, readClaims]);\n\n // --- refreshClaims ---\n const refreshClaims = useCallback(async () => {\n // The LIVE signed-in user, never the one this render saw: a caller can hold this callback from\n // before its own sign-in (a registration's submit handler creates the account and then asks for\n // the claims the server just set), and a refresh of that render's `null` user would do nothing —\n // leaving the new account on its pre-claim token until the hourly refresh.\n const liveUser = configRef.current.auth.currentUser;\n if (!liveUser) return;\n await readClaims(\n liveUser,\n async () => ((await liveUser.getIdTokenResult(true)).claims ?? {}) as Record<string, unknown>,\n 'refreshClaims',\n );\n }, [readClaims]);\n\n // --- Derived at render: only the current session's claims are ever exposed ---\n const sessionClaims = user && resolved?.user === user ? resolved : null;\n const claimsLoading = authLoading || (user !== null && sessionClaims === null);\n\n // While the gate is closed a restored session is held back from consumers: a uid-keyed\n // subscription opened before the gate's readiness signal is rejected once and dies, so no\n // consumer may see a uid (or that session's claims) until the gate opens or fails open.\n const exposedUser = gateOpen ? user : null;\n const claims = exposedUser && sessionClaims ? sessionClaims.claims : config.defaultClaims;\n\n // --- Context value ---\n const loading = authLoading || claimsLoading || !gateOpen;\n\n const value = useMemo<AuthContextValue<TClaims>>(\n () => ({\n user: exposedUser,\n claims,\n loading,\n authLoading,\n claimsLoading,\n isAuthenticated: !!exposedUser,\n refreshClaims,\n }),\n [exposedUser, claims, loading, authLoading, claimsLoading, refreshClaims],\n );\n\n return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"AuthProvider.js","sourceRoot":"","sources":["../../src/react/AuthProvider.tsx"],"names":[],"mappings":"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","sourcesContent":["'use client';\n\nimport {\n createContext,\n useEffect,\n useRef,\n useState,\n useCallback,\n useMemo,\n type ReactNode,\n} from 'react';\nimport type { User } from 'firebase/auth';\nimport { useAuthState } from './useAuthState.js';\nimport { getIdTokenClaims } from '../client/claims.js';\nimport type { AuthProviderConfig, AuthContextValue } from './types.js';\n\ninterface ResolvedClaims<TClaims> {\n user: User;\n generation: number;\n claims: TClaims;\n}\n\nexport const AuthContext = createContext<AuthContextValue<any> | null>(null);\n\ninterface AuthProviderProps<TClaims> {\n config: AuthProviderConfig<TClaims>;\n children: ReactNode;\n}\n\nexport function AuthProvider<TClaims = Record<string, unknown>>({\n config,\n children,\n}: AuthProviderProps<TClaims>) {\n const { user, loading: authLoading } = useAuthState(config.auth);\n\n const [resolved, setResolved] = useState<ResolvedClaims<TClaims> | null>(null);\n const generationRef = useRef(0);\n const appliedGenerationRef = useRef(0);\n const currentUserRef = useRef<User | null>(user);\n currentUserRef.current = user;\n\n const configRef = useRef(config);\n configRef.current = config;\n\n const readClaims = useCallback(\n async (forUser: User, read: () => Promise<Record<string, unknown> | null>, context: string) => {\n const generation = ++generationRef.current;\n try {\n const raw = await read();\n if (currentUserRef.current !== forUser || generation <= appliedGenerationRef.current)\n return;\n appliedGenerationRef.current = generation;\n setResolved({\n user: forUser,\n generation,\n claims: configRef.current.parseClaims(raw ?? {}),\n });\n } catch (err) {\n if (currentUserRef.current !== forUser) return;\n configRef.current.onError?.(err, context);\n setResolved((prev) =>\n prev && prev.user === forUser\n ? prev\n : { user: forUser, generation, claims: configRef.current.defaultClaims },\n );\n }\n },\n [],\n );\n\n const [gateOpen, setGateOpen] = useState(() => !config.readyGate);\n useEffect(() => {\n const gate = configRef.current.readyGate;\n if (!gate) return;\n let cancelled = false;\n Promise.resolve()\n .then(() => gate())\n .catch((err) => configRef.current.onError?.(err, 'readyGate'))\n .finally(() => {\n if (!cancelled) setGateOpen(true);\n });\n return () => {\n cancelled = true;\n };\n }, []);\n\n useEffect(() => {\n if (authLoading) return;\n\n if (!user) {\n setResolved(null);\n configRef.current.onAuthStateChange?.(null);\n return;\n }\n\n configRef.current.onAuthStateChange?.(user);\n void readClaims(\n user,\n async () => ((await getIdTokenClaims(user)) ?? {}) as Record<string, unknown>,\n 'getIdTokenClaims',\n );\n }, [user, authLoading, readClaims]);\n\n const refreshClaims = useCallback(async () => {\n // `auth.currentUser`, not this render's user: a caller can hold this callback from before its own sign-in.\n const liveUser = configRef.current.auth.currentUser;\n if (!liveUser) return;\n await readClaims(\n liveUser,\n async () => ((await liveUser.getIdTokenResult(true)).claims ?? {}) as Record<string, unknown>,\n 'refreshClaims',\n );\n }, [readClaims]);\n\n const sessionClaims = user && resolved?.user === user ? resolved : null;\n const claimsLoading = authLoading || (user !== null && sessionClaims === null);\n\n const exposedUser = gateOpen ? user : null;\n const claims = exposedUser && sessionClaims ? sessionClaims.claims : config.defaultClaims;\n\n const loading = authLoading || claimsLoading || !gateOpen;\n\n const value = useMemo<AuthContextValue<TClaims>>(\n () => ({\n user: exposedUser,\n claims,\n loading,\n authLoading,\n claimsLoading,\n isAuthenticated: !!exposedUser,\n refreshClaims,\n }),\n [exposedUser, claims, loading, authLoading, claimsLoading, refreshClaims],\n );\n\n return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"redirect-storage.d.ts","sourceRoot":"","sources":["../../src/react/redirect-storage.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"redirect-storage.d.ts","sourceRoot":"","sources":["../../src/react/redirect-storage.ts"],"names":[],"mappings":"AAgBA,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAO3D;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAWhE;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAMnD"}
|