@ttt-productions/auth-core 0.0.0 → 0.0.2
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.d.ts +10 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +13 -0
- package/dist/auth.js.map +1 -0
- package/dist/claims.d.ts +27 -0
- package/dist/claims.d.ts.map +1 -0
- package/dist/claims.js +40 -0
- package/dist/claims.js.map +1 -0
- package/dist/errors.d.ts +9 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +96 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/{src/index.ts → dist/index.js} +1 -0
- package/dist/index.js.map +1 -0
- package/dist/react/index.d.ts +2 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +2 -0
- package/dist/react/index.js.map +1 -0
- package/dist/react/useAuthState.d.ts +8 -0
- package/dist/react/useAuthState.d.ts.map +1 -0
- package/dist/react/useAuthState.js +21 -0
- package/dist/react/useAuthState.js.map +1 -0
- package/package.json +54 -28
- package/src/auth.ts +0 -21
- package/src/claims.ts +0 -54
- package/src/errors.ts +0 -126
- package/src/react/index.ts +0 -1
- package/src/react/useAuthState.ts +0 -34
- package/tsconfig.json +0 -8
package/dist/auth.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Auth, User } from "firebase/auth";
|
|
2
|
+
export type Unsubscribe = () => void;
|
|
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
|
+
export declare function onAuthStateChanged(auth: Auth, cb: (user: User | null) => void, onError?: (error: unknown) => void): Unsubscribe;
|
|
10
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/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;;;;GAIG;AACH,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/auth.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { onAuthStateChanged as fbOnAuthStateChanged } from "firebase/auth";
|
|
2
|
+
export function getAuthUser(auth) {
|
|
3
|
+
return auth.currentUser;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Thin wrapper around Firebase onAuthStateChanged.
|
|
7
|
+
* - always returns the unsubscribe function
|
|
8
|
+
* - preserves Firebase callback signature
|
|
9
|
+
*/
|
|
10
|
+
export function onAuthStateChanged(auth, cb, onError) {
|
|
11
|
+
return fbOnAuthStateChanged(auth, cb, onError);
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=auth.js.map
|
package/dist/auth.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/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;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAChC,IAAU,EACV,EAA+B,EAC/B,OAAkC;IAElC,OAAO,oBAAoB,CAAC,IAAI,EAAE,EAAE,EAAE,OAAO,CAAC,CAAC;AACjD,CAAC"}
|
package/dist/claims.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Auth, User, IdTokenResult } from "firebase/auth";
|
|
2
|
+
export type IdTokenClaims = IdTokenResult["claims"];
|
|
3
|
+
export type ParsedClaims = {
|
|
4
|
+
roles: string[];
|
|
5
|
+
raw: IdTokenClaims;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Get the current user's ID token claims.
|
|
9
|
+
* Returns null if no current user.
|
|
10
|
+
*/
|
|
11
|
+
export declare function getIdTokenClaims(user: User | null): Promise<IdTokenClaims | null>;
|
|
12
|
+
/**
|
|
13
|
+
* Forces refresh of ID token + claims.
|
|
14
|
+
* Returns null if no current user.
|
|
15
|
+
*/
|
|
16
|
+
export declare function refreshClaims(user: User | null): Promise<IdTokenClaims | null>;
|
|
17
|
+
/**
|
|
18
|
+
* Convenience helper when you only have Auth.
|
|
19
|
+
*/
|
|
20
|
+
export declare function refreshClaimsFromAuth(auth: Auth): Promise<IdTokenClaims | null>;
|
|
21
|
+
/**
|
|
22
|
+
* Keep parsing intentionally minimal and generic:
|
|
23
|
+
* - normalize "roles" into string[]
|
|
24
|
+
* - keep raw claims attached
|
|
25
|
+
*/
|
|
26
|
+
export declare function parseClaims(claims: IdTokenClaims | null | undefined): ParsedClaims;
|
|
27
|
+
//# sourceMappingURL=claims.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claims.d.ts","sourceRoot":"","sources":["../src/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;IAEzB,KAAK,EAAE,MAAM,EAAE,CAAC;IAEhB,GAAG,EAAE,aAAa,CAAC;CACpB,CAAC;AAEF;;;GAGG;AACH,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAIvF;AAED;;;GAGG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAIpF;AAED;;GAEG;AACH,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAErF;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,GAAG,SAAS,GAAG,YAAY,CAUlF"}
|
package/dist/claims.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get the current user's ID token claims.
|
|
3
|
+
* Returns null if no current user.
|
|
4
|
+
*/
|
|
5
|
+
export async function getIdTokenClaims(user) {
|
|
6
|
+
if (!user)
|
|
7
|
+
return null;
|
|
8
|
+
const res = await user.getIdTokenResult(false);
|
|
9
|
+
return res.claims;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Forces refresh of ID token + claims.
|
|
13
|
+
* Returns null if no current user.
|
|
14
|
+
*/
|
|
15
|
+
export async function refreshClaims(user) {
|
|
16
|
+
if (!user)
|
|
17
|
+
return null;
|
|
18
|
+
const res = await user.getIdTokenResult(true);
|
|
19
|
+
return res.claims;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Convenience helper when you only have Auth.
|
|
23
|
+
*/
|
|
24
|
+
export async function refreshClaimsFromAuth(auth) {
|
|
25
|
+
return refreshClaims(auth.currentUser);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Keep parsing intentionally minimal and generic:
|
|
29
|
+
* - normalize "roles" into string[]
|
|
30
|
+
* - keep raw claims attached
|
|
31
|
+
*/
|
|
32
|
+
export function parseClaims(claims) {
|
|
33
|
+
const raw = (claims ?? {});
|
|
34
|
+
const rolesValue = raw.roles;
|
|
35
|
+
const roles = Array.isArray(rolesValue) ? rolesValue.filter((x) => typeof x === "string") :
|
|
36
|
+
typeof rolesValue === "string" ? [rolesValue] :
|
|
37
|
+
[];
|
|
38
|
+
return { roles, raw };
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=claims.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claims.js","sourceRoot":"","sources":["../src/claims.ts"],"names":[],"mappings":"AAWA;;;GAGG;AACH,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,OAAO,GAAG,CAAC,MAAM,CAAC;AACpB,CAAC;AAED;;;GAGG;AACH,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;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,IAAU;IACpD,OAAO,aAAa,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;AACzC,CAAC;AAED;;;;GAIG;AACH,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,GACT,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC;QAC7E,OAAO,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC;YAC/C,EAAE,CAAC;IAEL,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;AACxB,CAAC"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export type AuthErrorCode = "AUTH_UNKNOWN" | "AUTH_NETWORK_ERROR" | "AUTH_TOO_MANY_REQUESTS" | "AUTH_POPUP_CLOSED" | "AUTH_POPUP_BLOCKED" | "AUTH_CANCELLED" | "AUTH_USER_DISABLED" | "AUTH_USER_NOT_FOUND" | "AUTH_WRONG_PASSWORD" | "AUTH_INVALID_EMAIL" | "AUTH_EMAIL_ALREADY_IN_USE" | "AUTH_WEAK_PASSWORD" | "AUTH_REQUIRES_RECENT_LOGIN" | "AUTH_INVALID_CREDENTIAL" | "AUTH_ACCOUNT_EXISTS_WITH_DIFFERENT_CREDENTIAL" | "AUTH_PROVIDER_ALREADY_LINKED" | "AUTH_CREDENTIAL_ALREADY_IN_USE";
|
|
2
|
+
export type NormalizedAuthError = {
|
|
3
|
+
code: AuthErrorCode;
|
|
4
|
+
message: string;
|
|
5
|
+
firebaseCode?: string;
|
|
6
|
+
details?: Record<string, unknown>;
|
|
7
|
+
};
|
|
8
|
+
export declare function normalizeAuthError(err: unknown, details?: Record<string, unknown>): NormalizedAuthError;
|
|
9
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +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,CAAC;AAErC,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;AA4EF,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,mBAAmB,CAyBvG"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
function pickFirebaseCode(err) {
|
|
2
|
+
if (!err || typeof err !== "object")
|
|
3
|
+
return undefined;
|
|
4
|
+
const anyErr = err;
|
|
5
|
+
const c = anyErr.code;
|
|
6
|
+
return typeof c === "string" ? c : undefined;
|
|
7
|
+
}
|
|
8
|
+
const MAP = {
|
|
9
|
+
"auth/network-request-failed": {
|
|
10
|
+
code: "AUTH_NETWORK_ERROR",
|
|
11
|
+
message: "Network error. Please check your connection and try again."
|
|
12
|
+
},
|
|
13
|
+
"auth/too-many-requests": {
|
|
14
|
+
code: "AUTH_TOO_MANY_REQUESTS",
|
|
15
|
+
message: "Too many attempts. Please wait a moment and try again."
|
|
16
|
+
},
|
|
17
|
+
"auth/popup-closed-by-user": {
|
|
18
|
+
code: "AUTH_POPUP_CLOSED",
|
|
19
|
+
message: "Sign-in popup was closed."
|
|
20
|
+
},
|
|
21
|
+
"auth/popup-blocked": {
|
|
22
|
+
code: "AUTH_POPUP_BLOCKED",
|
|
23
|
+
message: "Sign-in popup was blocked by the browser."
|
|
24
|
+
},
|
|
25
|
+
"auth/cancelled-popup-request": {
|
|
26
|
+
code: "AUTH_CANCELLED",
|
|
27
|
+
message: "Sign-in was cancelled."
|
|
28
|
+
},
|
|
29
|
+
"auth/user-disabled": {
|
|
30
|
+
code: "AUTH_USER_DISABLED",
|
|
31
|
+
message: "This account has been disabled."
|
|
32
|
+
},
|
|
33
|
+
"auth/user-not-found": {
|
|
34
|
+
code: "AUTH_USER_NOT_FOUND",
|
|
35
|
+
message: "No account found with that email."
|
|
36
|
+
},
|
|
37
|
+
"auth/wrong-password": {
|
|
38
|
+
code: "AUTH_WRONG_PASSWORD",
|
|
39
|
+
message: "Incorrect password."
|
|
40
|
+
},
|
|
41
|
+
"auth/invalid-email": {
|
|
42
|
+
code: "AUTH_INVALID_EMAIL",
|
|
43
|
+
message: "Invalid email address."
|
|
44
|
+
},
|
|
45
|
+
"auth/email-already-in-use": {
|
|
46
|
+
code: "AUTH_EMAIL_ALREADY_IN_USE",
|
|
47
|
+
message: "That email is already in use."
|
|
48
|
+
},
|
|
49
|
+
"auth/weak-password": {
|
|
50
|
+
code: "AUTH_WEAK_PASSWORD",
|
|
51
|
+
message: "Password is too weak."
|
|
52
|
+
},
|
|
53
|
+
"auth/requires-recent-login": {
|
|
54
|
+
code: "AUTH_REQUIRES_RECENT_LOGIN",
|
|
55
|
+
message: "Please re-authenticate and try again."
|
|
56
|
+
},
|
|
57
|
+
"auth/invalid-credential": {
|
|
58
|
+
code: "AUTH_INVALID_CREDENTIAL",
|
|
59
|
+
message: "Invalid credentials. Please try again."
|
|
60
|
+
},
|
|
61
|
+
"auth/account-exists-with-different-credential": {
|
|
62
|
+
code: "AUTH_ACCOUNT_EXISTS_WITH_DIFFERENT_CREDENTIAL",
|
|
63
|
+
message: "An account already exists with a different sign-in method."
|
|
64
|
+
},
|
|
65
|
+
"auth/provider-already-linked": {
|
|
66
|
+
code: "AUTH_PROVIDER_ALREADY_LINKED",
|
|
67
|
+
message: "That provider is already linked to this account."
|
|
68
|
+
},
|
|
69
|
+
"auth/credential-already-in-use": {
|
|
70
|
+
code: "AUTH_CREDENTIAL_ALREADY_IN_USE",
|
|
71
|
+
message: "Those credentials are already associated with another account."
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
export function normalizeAuthError(err, details) {
|
|
75
|
+
const firebaseCode = pickFirebaseCode(err);
|
|
76
|
+
const mapped = firebaseCode ? MAP[firebaseCode] : undefined;
|
|
77
|
+
if (mapped) {
|
|
78
|
+
return {
|
|
79
|
+
code: mapped.code,
|
|
80
|
+
message: mapped.message,
|
|
81
|
+
firebaseCode,
|
|
82
|
+
details
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
// best-effort message (don’t leak weird objects)
|
|
86
|
+
const msg = typeof err?.message === "string"
|
|
87
|
+
? err.message
|
|
88
|
+
: "Authentication error. Please try again.";
|
|
89
|
+
return {
|
|
90
|
+
code: "AUTH_UNKNOWN",
|
|
91
|
+
message: msg,
|
|
92
|
+
firebaseCode,
|
|
93
|
+
details
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AA0BA,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;CACF,CAAC;AAEF,MAAM,UAAU,kBAAkB,CAAC,GAAY,EAAE,OAAiC;IAChF,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,iDAAiD;IACjD,MAAM,GAAG,GACP,OAAQ,GAAW,EAAE,OAAO,KAAK,QAAQ;QACvC,CAAC,CAAE,GAAW,CAAC,OAAO;QACtB,CAAC,CAAC,yCAAyC,CAAC;IAEhD,OAAO;QACL,IAAI,EAAE,cAAc;QACpB,OAAO,EAAE,GAAG;QACZ,YAAY;QACZ,OAAO;KACR,CAAC;AACJ,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,QAAQ,CAAC;AACvB,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,QAAQ,CAAC;AACvB,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useAuthState.d.ts","sourceRoot":"","sources":["../../src/react/useAuthState.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAGhD,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;CACvB,CAAC;AAEF,wBAAgB,YAAY,CAAC,IAAI,EAAE,IAAI,GAAG,SAAS,CAuBlD"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
import { onAuthStateChanged } from "../auth";
|
|
3
|
+
export function useAuthState(auth) {
|
|
4
|
+
const [user, setUser] = useState(() => auth.currentUser);
|
|
5
|
+
const [loading, setLoading] = useState(true);
|
|
6
|
+
const [error, setError] = useState(null);
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
setLoading(true);
|
|
9
|
+
const unsub = onAuthStateChanged(auth, (u) => {
|
|
10
|
+
setUser(u);
|
|
11
|
+
setError(null);
|
|
12
|
+
setLoading(false);
|
|
13
|
+
}, (e) => {
|
|
14
|
+
setError(e);
|
|
15
|
+
setLoading(false);
|
|
16
|
+
});
|
|
17
|
+
return unsub;
|
|
18
|
+
}, [auth]);
|
|
19
|
+
return { user, loading, error };
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=useAuthState.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useAuthState.js","sourceRoot":"","sources":["../../src/react/useAuthState.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAE5C,OAAO,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAQ7C,MAAM,UAAU,YAAY,CAAC,IAAU;IACrC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAc,GAAG,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACtE,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAU,IAAI,CAAC,CAAC;IACtD,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAiB,IAAI,CAAC,CAAC;IAEzD,SAAS,CAAC,GAAG,EAAE;QACb,UAAU,CAAC,IAAI,CAAC,CAAC;QACjB,MAAM,KAAK,GAAG,kBAAkB,CAC9B,IAAI,EACJ,CAAC,CAAC,EAAE,EAAE;YACJ,OAAO,CAAC,CAAC,CAAC,CAAC;YACX,QAAQ,CAAC,IAAI,CAAC,CAAC;YACf,UAAU,CAAC,KAAK,CAAC,CAAC;QACpB,CAAC,EACD,CAAC,CAAC,EAAE,EAAE;YACJ,QAAQ,CAAC,CAAC,CAAC,CAAC;YACZ,UAAU,CAAC,KAAK,CAAC,CAAC;QACpB,CAAC,CACF,CAAC;QACF,OAAO,KAAK,CAAC;IACf,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IAEX,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AAClC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,31 +1,57 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
"
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
"sideEffects": false,
|
|
10
|
-
"main": "dist/index.cjs",
|
|
11
|
-
"module": "dist/index.js",
|
|
12
|
-
"types": "dist/index.d.ts",
|
|
13
|
-
"exports": {
|
|
14
|
-
".": {
|
|
15
|
-
"types": "./dist/index.d.ts",
|
|
16
|
-
"import": "./dist/index.js",
|
|
17
|
-
"require": "./dist/index.cjs"
|
|
2
|
+
"name": "@ttt-productions/auth-core",
|
|
3
|
+
"version": "0.0.2",
|
|
4
|
+
"description": "Shared Firebase authentication utilities and React hooks for TTT Productions apps",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/ttt-productions/ttt-packages.git",
|
|
8
|
+
"directory": "packages/auth-core"
|
|
18
9
|
},
|
|
19
|
-
"
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
10
|
+
"type": "module",
|
|
11
|
+
"main": "dist/index.js",
|
|
12
|
+
"module": "dist/index.js",
|
|
13
|
+
"types": "dist/index.d.ts",
|
|
14
|
+
"sideEffects": false,
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"exports": {
|
|
19
|
+
".": {
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"default": "./dist/index.js"
|
|
22
|
+
},
|
|
23
|
+
"./react": {
|
|
24
|
+
"types": "./dist/react/index.d.ts",
|
|
25
|
+
"default": "./dist/react/index.js"
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"scripts": {
|
|
29
|
+
"build": "tsc",
|
|
30
|
+
"clean": "rm -rf dist *.tsbuildinfo",
|
|
31
|
+
"typecheck": "tsc --noEmit",
|
|
32
|
+
"prepublishOnly": "npm run clean && npm run build"
|
|
33
|
+
},
|
|
34
|
+
"peerDependencies": {
|
|
35
|
+
"firebase": ">=10.0.0",
|
|
36
|
+
"react": ">=19.0.0",
|
|
37
|
+
"react-dom": ">=19.0.0"
|
|
38
|
+
},
|
|
39
|
+
"peerDependenciesMeta": {
|
|
40
|
+
"react": {
|
|
41
|
+
"optional": true
|
|
42
|
+
},
|
|
43
|
+
"react-dom": {
|
|
44
|
+
"optional": true
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@types/react": "^19.0.0",
|
|
49
|
+
"@types/react-dom": "^19.0.0",
|
|
50
|
+
"firebase": "^10.0.0",
|
|
51
|
+
"react": "^19.2.0",
|
|
52
|
+
"react-dom": "^19.2.0",
|
|
53
|
+
"typescript": "^5.8.3"
|
|
54
|
+
},
|
|
55
|
+
"author": "DJ (TTT Productions)",
|
|
56
|
+
"license": "MIT"
|
|
31
57
|
}
|
package/src/auth.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import type { Auth, User } from "firebase/auth";
|
|
2
|
-
import { onAuthStateChanged as fbOnAuthStateChanged } from "firebase/auth";
|
|
3
|
-
|
|
4
|
-
export type Unsubscribe = () => void;
|
|
5
|
-
|
|
6
|
-
export function getAuthUser(auth: Auth): User | null {
|
|
7
|
-
return auth.currentUser;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Thin wrapper around Firebase onAuthStateChanged.
|
|
12
|
-
* - always returns the unsubscribe function
|
|
13
|
-
* - preserves Firebase callback signature
|
|
14
|
-
*/
|
|
15
|
-
export function onAuthStateChanged(
|
|
16
|
-
auth: Auth,
|
|
17
|
-
cb: (user: User | null) => void,
|
|
18
|
-
onError?: (error: unknown) => void
|
|
19
|
-
): Unsubscribe {
|
|
20
|
-
return fbOnAuthStateChanged(auth, cb, onError);
|
|
21
|
-
}
|
package/src/claims.ts
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import type { Auth, User, IdTokenResult } from "firebase/auth";
|
|
2
|
-
|
|
3
|
-
export type IdTokenClaims = IdTokenResult["claims"];
|
|
4
|
-
|
|
5
|
-
export type ParsedClaims = {
|
|
6
|
-
// common defaults (kept generic)
|
|
7
|
-
roles: string[];
|
|
8
|
-
// raw claims for app-level decisions
|
|
9
|
-
raw: IdTokenClaims;
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Get the current user's ID token claims.
|
|
14
|
-
* Returns null if no current user.
|
|
15
|
-
*/
|
|
16
|
-
export async function getIdTokenClaims(user: User | null): Promise<IdTokenClaims | null> {
|
|
17
|
-
if (!user) return null;
|
|
18
|
-
const res = await user.getIdTokenResult(false);
|
|
19
|
-
return res.claims;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Forces refresh of ID token + claims.
|
|
24
|
-
* Returns null if no current user.
|
|
25
|
-
*/
|
|
26
|
-
export async function refreshClaims(user: User | null): Promise<IdTokenClaims | null> {
|
|
27
|
-
if (!user) return null;
|
|
28
|
-
const res = await user.getIdTokenResult(true);
|
|
29
|
-
return res.claims;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Convenience helper when you only have Auth.
|
|
34
|
-
*/
|
|
35
|
-
export async function refreshClaimsFromAuth(auth: Auth): Promise<IdTokenClaims | null> {
|
|
36
|
-
return refreshClaims(auth.currentUser);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Keep parsing intentionally minimal and generic:
|
|
41
|
-
* - normalize "roles" into string[]
|
|
42
|
-
* - keep raw claims attached
|
|
43
|
-
*/
|
|
44
|
-
export function parseClaims(claims: IdTokenClaims | null | undefined): ParsedClaims {
|
|
45
|
-
const raw = (claims ?? {}) as IdTokenClaims;
|
|
46
|
-
|
|
47
|
-
const rolesValue = (raw as any).roles;
|
|
48
|
-
const roles =
|
|
49
|
-
Array.isArray(rolesValue) ? rolesValue.filter((x) => typeof x === "string") :
|
|
50
|
-
typeof rolesValue === "string" ? [rolesValue] :
|
|
51
|
-
[];
|
|
52
|
-
|
|
53
|
-
return { roles, raw };
|
|
54
|
-
}
|
package/src/errors.ts
DELETED
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
export type AuthErrorCode =
|
|
2
|
-
| "AUTH_UNKNOWN"
|
|
3
|
-
| "AUTH_NETWORK_ERROR"
|
|
4
|
-
| "AUTH_TOO_MANY_REQUESTS"
|
|
5
|
-
| "AUTH_POPUP_CLOSED"
|
|
6
|
-
| "AUTH_POPUP_BLOCKED"
|
|
7
|
-
| "AUTH_CANCELLED"
|
|
8
|
-
| "AUTH_USER_DISABLED"
|
|
9
|
-
| "AUTH_USER_NOT_FOUND"
|
|
10
|
-
| "AUTH_WRONG_PASSWORD"
|
|
11
|
-
| "AUTH_INVALID_EMAIL"
|
|
12
|
-
| "AUTH_EMAIL_ALREADY_IN_USE"
|
|
13
|
-
| "AUTH_WEAK_PASSWORD"
|
|
14
|
-
| "AUTH_REQUIRES_RECENT_LOGIN"
|
|
15
|
-
| "AUTH_INVALID_CREDENTIAL"
|
|
16
|
-
| "AUTH_ACCOUNT_EXISTS_WITH_DIFFERENT_CREDENTIAL"
|
|
17
|
-
| "AUTH_PROVIDER_ALREADY_LINKED"
|
|
18
|
-
| "AUTH_CREDENTIAL_ALREADY_IN_USE";
|
|
19
|
-
|
|
20
|
-
export type NormalizedAuthError = {
|
|
21
|
-
code: AuthErrorCode;
|
|
22
|
-
message: string;
|
|
23
|
-
firebaseCode?: string;
|
|
24
|
-
details?: Record<string, unknown>;
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
function pickFirebaseCode(err: unknown): string | undefined {
|
|
28
|
-
if (!err || typeof err !== "object") return undefined;
|
|
29
|
-
const anyErr = err as any;
|
|
30
|
-
const c = anyErr.code;
|
|
31
|
-
return typeof c === "string" ? c : undefined;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const MAP: Record<string, { code: AuthErrorCode; message: string }> = {
|
|
35
|
-
"auth/network-request-failed": {
|
|
36
|
-
code: "AUTH_NETWORK_ERROR",
|
|
37
|
-
message: "Network error. Please check your connection and try again."
|
|
38
|
-
},
|
|
39
|
-
"auth/too-many-requests": {
|
|
40
|
-
code: "AUTH_TOO_MANY_REQUESTS",
|
|
41
|
-
message: "Too many attempts. Please wait a moment and try again."
|
|
42
|
-
},
|
|
43
|
-
"auth/popup-closed-by-user": {
|
|
44
|
-
code: "AUTH_POPUP_CLOSED",
|
|
45
|
-
message: "Sign-in popup was closed."
|
|
46
|
-
},
|
|
47
|
-
"auth/popup-blocked": {
|
|
48
|
-
code: "AUTH_POPUP_BLOCKED",
|
|
49
|
-
message: "Sign-in popup was blocked by the browser."
|
|
50
|
-
},
|
|
51
|
-
"auth/cancelled-popup-request": {
|
|
52
|
-
code: "AUTH_CANCELLED",
|
|
53
|
-
message: "Sign-in was cancelled."
|
|
54
|
-
},
|
|
55
|
-
"auth/user-disabled": {
|
|
56
|
-
code: "AUTH_USER_DISABLED",
|
|
57
|
-
message: "This account has been disabled."
|
|
58
|
-
},
|
|
59
|
-
"auth/user-not-found": {
|
|
60
|
-
code: "AUTH_USER_NOT_FOUND",
|
|
61
|
-
message: "No account found with that email."
|
|
62
|
-
},
|
|
63
|
-
"auth/wrong-password": {
|
|
64
|
-
code: "AUTH_WRONG_PASSWORD",
|
|
65
|
-
message: "Incorrect password."
|
|
66
|
-
},
|
|
67
|
-
"auth/invalid-email": {
|
|
68
|
-
code: "AUTH_INVALID_EMAIL",
|
|
69
|
-
message: "Invalid email address."
|
|
70
|
-
},
|
|
71
|
-
"auth/email-already-in-use": {
|
|
72
|
-
code: "AUTH_EMAIL_ALREADY_IN_USE",
|
|
73
|
-
message: "That email is already in use."
|
|
74
|
-
},
|
|
75
|
-
"auth/weak-password": {
|
|
76
|
-
code: "AUTH_WEAK_PASSWORD",
|
|
77
|
-
message: "Password is too weak."
|
|
78
|
-
},
|
|
79
|
-
"auth/requires-recent-login": {
|
|
80
|
-
code: "AUTH_REQUIRES_RECENT_LOGIN",
|
|
81
|
-
message: "Please re-authenticate and try again."
|
|
82
|
-
},
|
|
83
|
-
"auth/invalid-credential": {
|
|
84
|
-
code: "AUTH_INVALID_CREDENTIAL",
|
|
85
|
-
message: "Invalid credentials. Please try again."
|
|
86
|
-
},
|
|
87
|
-
"auth/account-exists-with-different-credential": {
|
|
88
|
-
code: "AUTH_ACCOUNT_EXISTS_WITH_DIFFERENT_CREDENTIAL",
|
|
89
|
-
message: "An account already exists with a different sign-in method."
|
|
90
|
-
},
|
|
91
|
-
"auth/provider-already-linked": {
|
|
92
|
-
code: "AUTH_PROVIDER_ALREADY_LINKED",
|
|
93
|
-
message: "That provider is already linked to this account."
|
|
94
|
-
},
|
|
95
|
-
"auth/credential-already-in-use": {
|
|
96
|
-
code: "AUTH_CREDENTIAL_ALREADY_IN_USE",
|
|
97
|
-
message: "Those credentials are already associated with another account."
|
|
98
|
-
}
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
export function normalizeAuthError(err: unknown, details?: Record<string, unknown>): NormalizedAuthError {
|
|
102
|
-
const firebaseCode = pickFirebaseCode(err);
|
|
103
|
-
|
|
104
|
-
const mapped = firebaseCode ? MAP[firebaseCode] : undefined;
|
|
105
|
-
if (mapped) {
|
|
106
|
-
return {
|
|
107
|
-
code: mapped.code,
|
|
108
|
-
message: mapped.message,
|
|
109
|
-
firebaseCode,
|
|
110
|
-
details
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// best-effort message (don’t leak weird objects)
|
|
115
|
-
const msg =
|
|
116
|
-
typeof (err as any)?.message === "string"
|
|
117
|
-
? (err as any).message
|
|
118
|
-
: "Authentication error. Please try again.";
|
|
119
|
-
|
|
120
|
-
return {
|
|
121
|
-
code: "AUTH_UNKNOWN",
|
|
122
|
-
message: msg,
|
|
123
|
-
firebaseCode,
|
|
124
|
-
details
|
|
125
|
-
};
|
|
126
|
-
}
|
package/src/react/index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./useAuthState";
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { useEffect, useState } from "react";
|
|
2
|
-
import type { Auth, User } from "firebase/auth";
|
|
3
|
-
import { onAuthStateChanged } from "../auth";
|
|
4
|
-
|
|
5
|
-
export type AuthState = {
|
|
6
|
-
user: User | null;
|
|
7
|
-
loading: boolean;
|
|
8
|
-
error: unknown | null;
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
export function useAuthState(auth: Auth): AuthState {
|
|
12
|
-
const [user, setUser] = useState<User | null>(() => auth.currentUser);
|
|
13
|
-
const [loading, setLoading] = useState<boolean>(true);
|
|
14
|
-
const [error, setError] = useState<unknown | null>(null);
|
|
15
|
-
|
|
16
|
-
useEffect(() => {
|
|
17
|
-
setLoading(true);
|
|
18
|
-
const unsub = onAuthStateChanged(
|
|
19
|
-
auth,
|
|
20
|
-
(u) => {
|
|
21
|
-
setUser(u);
|
|
22
|
-
setError(null);
|
|
23
|
-
setLoading(false);
|
|
24
|
-
},
|
|
25
|
-
(e) => {
|
|
26
|
-
setError(e);
|
|
27
|
-
setLoading(false);
|
|
28
|
-
}
|
|
29
|
-
);
|
|
30
|
-
return unsub;
|
|
31
|
-
}, [auth]);
|
|
32
|
-
|
|
33
|
-
return { user, loading, error };
|
|
34
|
-
}
|