@kaiz11/stack-client 0.0.14
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/LICENSE +32 -0
- package/README.md +586 -0
- package/dist/accounts/accounts-client.d.ts +188 -0
- package/dist/accounts/accounts-client.d.ts.map +1 -0
- package/dist/accounts/accounts-client.js +264 -0
- package/dist/accounts/accounts-client.js.map +1 -0
- package/dist/accounts/index.d.ts +8 -0
- package/dist/accounts/index.d.ts.map +1 -0
- package/dist/accounts/index.js +8 -0
- package/dist/accounts/index.js.map +1 -0
- package/dist/accounts/mock-accounts.d.ts +90 -0
- package/dist/accounts/mock-accounts.d.ts.map +1 -0
- package/dist/accounts/mock-accounts.js +434 -0
- package/dist/accounts/mock-accounts.js.map +1 -0
- package/dist/accounts/types.d.ts +180 -0
- package/dist/accounts/types.d.ts.map +1 -0
- package/dist/accounts/types.js +59 -0
- package/dist/accounts/types.js.map +1 -0
- package/dist/auth/auth-client.d.ts +224 -0
- package/dist/auth/auth-client.d.ts.map +1 -0
- package/dist/auth/auth-client.js +230 -0
- package/dist/auth/auth-client.js.map +1 -0
- package/dist/auth/base-auth.d.ts +44 -0
- package/dist/auth/base-auth.d.ts.map +1 -0
- package/dist/auth/base-auth.js +55 -0
- package/dist/auth/base-auth.js.map +1 -0
- package/dist/auth/index.d.ts +11 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +11 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/methods/admin.d.ts +59 -0
- package/dist/auth/methods/admin.d.ts.map +1 -0
- package/dist/auth/methods/admin.js +55 -0
- package/dist/auth/methods/admin.js.map +1 -0
- package/dist/auth/methods/index.d.ts +9 -0
- package/dist/auth/methods/index.d.ts.map +1 -0
- package/dist/auth/methods/index.js +8 -0
- package/dist/auth/methods/index.js.map +1 -0
- package/dist/auth/methods/magic-link.d.ts +27 -0
- package/dist/auth/methods/magic-link.d.ts.map +1 -0
- package/dist/auth/methods/magic-link.js +37 -0
- package/dist/auth/methods/magic-link.js.map +1 -0
- package/dist/auth/methods/mfa.d.ts +92 -0
- package/dist/auth/methods/mfa.d.ts.map +1 -0
- package/dist/auth/methods/mfa.js +153 -0
- package/dist/auth/methods/mfa.js.map +1 -0
- package/dist/auth/methods/oauth.d.ts +62 -0
- package/dist/auth/methods/oauth.d.ts.map +1 -0
- package/dist/auth/methods/oauth.js +165 -0
- package/dist/auth/methods/oauth.js.map +1 -0
- package/dist/auth/methods/otp.d.ts +43 -0
- package/dist/auth/methods/otp.d.ts.map +1 -0
- package/dist/auth/methods/otp.js +66 -0
- package/dist/auth/methods/otp.js.map +1 -0
- package/dist/auth/methods/password.d.ts +64 -0
- package/dist/auth/methods/password.d.ts.map +1 -0
- package/dist/auth/methods/password.js +116 -0
- package/dist/auth/methods/password.js.map +1 -0
- package/dist/auth/methods/recovery.d.ts +62 -0
- package/dist/auth/methods/recovery.d.ts.map +1 -0
- package/dist/auth/methods/recovery.js +100 -0
- package/dist/auth/methods/recovery.js.map +1 -0
- package/dist/auth/mock-auth.d.ts +135 -0
- package/dist/auth/mock-auth.d.ts.map +1 -0
- package/dist/auth/mock-auth.js +417 -0
- package/dist/auth/mock-auth.js.map +1 -0
- package/dist/auth/server/helpers.d.ts +215 -0
- package/dist/auth/server/helpers.d.ts.map +1 -0
- package/dist/auth/server/helpers.js +241 -0
- package/dist/auth/server/helpers.js.map +1 -0
- package/dist/auth/server/index.d.ts +24 -0
- package/dist/auth/server/index.d.ts.map +1 -0
- package/dist/auth/server/index.js +40 -0
- package/dist/auth/server/index.js.map +1 -0
- package/dist/auth/server/middleware.d.ts +305 -0
- package/dist/auth/server/middleware.d.ts.map +1 -0
- package/dist/auth/server/middleware.js +405 -0
- package/dist/auth/server/middleware.js.map +1 -0
- package/dist/auth/server/verify.d.ts +184 -0
- package/dist/auth/server/verify.d.ts.map +1 -0
- package/dist/auth/server/verify.js +222 -0
- package/dist/auth/server/verify.js.map +1 -0
- package/dist/auth/token-manager.d.ts +94 -0
- package/dist/auth/token-manager.d.ts.map +1 -0
- package/dist/auth/token-manager.js +231 -0
- package/dist/auth/token-manager.js.map +1 -0
- package/dist/auth/types.d.ts +412 -0
- package/dist/auth/types.d.ts.map +1 -0
- package/dist/auth/types.js +66 -0
- package/dist/auth/types.js.map +1 -0
- package/dist/auth/user/identities.d.ts +62 -0
- package/dist/auth/user/identities.d.ts.map +1 -0
- package/dist/auth/user/identities.js +88 -0
- package/dist/auth/user/identities.js.map +1 -0
- package/dist/auth/user/index.d.ts +4 -0
- package/dist/auth/user/index.d.ts.map +1 -0
- package/dist/auth/user/index.js +4 -0
- package/dist/auth/user/index.js.map +1 -0
- package/dist/auth/user/user.d.ts +64 -0
- package/dist/auth/user/user.d.ts.map +1 -0
- package/dist/auth/user/user.js +105 -0
- package/dist/auth/user/user.js.map +1 -0
- package/dist/auth/user/verification.d.ts +49 -0
- package/dist/auth/user/verification.d.ts.map +1 -0
- package/dist/auth/user/verification.js +71 -0
- package/dist/auth/user/verification.js.map +1 -0
- package/dist/cli/browser.d.ts +11 -0
- package/dist/cli/browser.d.ts.map +1 -0
- package/dist/cli/browser.js +35 -0
- package/dist/cli/browser.js.map +1 -0
- package/dist/cli/callback-server.d.ts +30 -0
- package/dist/cli/callback-server.d.ts.map +1 -0
- package/dist/cli/callback-server.js +100 -0
- package/dist/cli/callback-server.js.map +1 -0
- package/dist/cli/file-token-store.d.ts +79 -0
- package/dist/cli/file-token-store.d.ts.map +1 -0
- package/dist/cli/file-token-store.js +138 -0
- package/dist/cli/file-token-store.js.map +1 -0
- package/dist/cli/index.d.ts +33 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +38 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/oauth.d.ts +67 -0
- package/dist/cli/oauth.d.ts.map +1 -0
- package/dist/cli/oauth.js +101 -0
- package/dist/cli/oauth.js.map +1 -0
- package/dist/cli/pkce.d.ts +35 -0
- package/dist/cli/pkce.d.ts.map +1 -0
- package/dist/cli/pkce.js +43 -0
- package/dist/cli/pkce.js.map +1 -0
- package/dist/client.d.ts +22 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +99 -0
- package/dist/client.js.map +1 -0
- package/dist/db/client.d.ts +9 -0
- package/dist/db/client.d.ts.map +1 -0
- package/dist/db/client.js +19 -0
- package/dist/db/client.js.map +1 -0
- package/dist/db/errors.d.ts +19 -0
- package/dist/db/errors.d.ts.map +1 -0
- package/dist/db/errors.js +57 -0
- package/dist/db/errors.js.map +1 -0
- package/dist/db/index.d.ts +7 -0
- package/dist/db/index.d.ts.map +1 -0
- package/dist/db/index.js +5 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/mock.d.ts +28 -0
- package/dist/db/mock.d.ts.map +1 -0
- package/dist/db/mock.js +459 -0
- package/dist/db/mock.js.map +1 -0
- package/dist/db/types.d.ts +73 -0
- package/dist/db/types.d.ts.map +1 -0
- package/dist/db/types.js +2 -0
- package/dist/db/types.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/errors.d.ts +33 -0
- package/dist/lib/errors.d.ts.map +1 -0
- package/dist/lib/errors.js +76 -0
- package/dist/lib/errors.js.map +1 -0
- package/dist/lib/http.d.ts +81 -0
- package/dist/lib/http.d.ts.map +1 -0
- package/dist/lib/http.js +163 -0
- package/dist/lib/http.js.map +1 -0
- package/dist/lib/keys.d.ts +87 -0
- package/dist/lib/keys.d.ts.map +1 -0
- package/dist/lib/keys.js +147 -0
- package/dist/lib/keys.js.map +1 -0
- package/dist/lib/paths.d.ts +37 -0
- package/dist/lib/paths.d.ts.map +1 -0
- package/dist/lib/paths.js +49 -0
- package/dist/lib/paths.js.map +1 -0
- package/dist/lib/token-store.d.ts +42 -0
- package/dist/lib/token-store.d.ts.map +1 -0
- package/dist/lib/token-store.js +75 -0
- package/dist/lib/token-store.js.map +1 -0
- package/dist/mocks/handlers.d.ts +29 -0
- package/dist/mocks/handlers.d.ts.map +1 -0
- package/dist/mocks/handlers.js +79 -0
- package/dist/mocks/handlers.js.map +1 -0
- package/dist/mocks/index.d.ts +5 -0
- package/dist/mocks/index.d.ts.map +1 -0
- package/dist/mocks/index.js +9 -0
- package/dist/mocks/index.js.map +1 -0
- package/dist/mocks/responses.d.ts +76 -0
- package/dist/mocks/responses.d.ts.map +1 -0
- package/dist/mocks/responses.js +91 -0
- package/dist/mocks/responses.js.map +1 -0
- package/dist/mocks/server.d.ts +7 -0
- package/dist/mocks/server.d.ts.map +1 -0
- package/dist/mocks/server.js +9 -0
- package/dist/mocks/server.js.map +1 -0
- package/dist/mocks/state.d.ts +86 -0
- package/dist/mocks/state.d.ts.map +1 -0
- package/dist/mocks/state.js +77 -0
- package/dist/mocks/state.js.map +1 -0
- package/dist/storage/bucket-ref.d.ts +183 -0
- package/dist/storage/bucket-ref.d.ts.map +1 -0
- package/dist/storage/bucket-ref.js +529 -0
- package/dist/storage/bucket-ref.js.map +1 -0
- package/dist/storage/errors.d.ts +27 -0
- package/dist/storage/errors.d.ts.map +1 -0
- package/dist/storage/errors.js +89 -0
- package/dist/storage/errors.js.map +1 -0
- package/dist/storage/index.d.ts +13 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +11 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/interface.d.ts +245 -0
- package/dist/storage/interface.d.ts.map +1 -0
- package/dist/storage/interface.js +2 -0
- package/dist/storage/interface.js.map +1 -0
- package/dist/storage/mock-storage.d.ts +67 -0
- package/dist/storage/mock-storage.d.ts.map +1 -0
- package/dist/storage/mock-storage.js +478 -0
- package/dist/storage/mock-storage.js.map +1 -0
- package/dist/storage/policies-client.d.ts +77 -0
- package/dist/storage/policies-client.d.ts.map +1 -0
- package/dist/storage/policies-client.js +115 -0
- package/dist/storage/policies-client.js.map +1 -0
- package/dist/storage/policy-templates.d.ts +6 -0
- package/dist/storage/policy-templates.d.ts.map +1 -0
- package/dist/storage/policy-templates.js +290 -0
- package/dist/storage/policy-templates.js.map +1 -0
- package/dist/storage/policy-types.d.ts +98 -0
- package/dist/storage/policy-types.d.ts.map +1 -0
- package/dist/storage/policy-types.js +20 -0
- package/dist/storage/policy-types.js.map +1 -0
- package/dist/storage/storage-client.d.ts +32 -0
- package/dist/storage/storage-client.d.ts.map +1 -0
- package/dist/storage/storage-client.js +94 -0
- package/dist/storage/storage-client.js.map +1 -0
- package/dist/storage/tus-upload.d.ts +56 -0
- package/dist/storage/tus-upload.d.ts.map +1 -0
- package/dist/storage/tus-upload.js +236 -0
- package/dist/storage/tus-upload.js.map +1 -0
- package/dist/storage/types.d.ts +335 -0
- package/dist/storage/types.d.ts.map +1 -0
- package/dist/storage/types.js +39 -0
- package/dist/storage/types.js.map +1 -0
- package/dist/test/auth/helpers.d.ts +33 -0
- package/dist/test/auth/helpers.d.ts.map +1 -0
- package/dist/test/auth/helpers.js +80 -0
- package/dist/test/auth/helpers.js.map +1 -0
- package/dist/test/helpers/jwt.d.ts +61 -0
- package/dist/test/helpers/jwt.d.ts.map +1 -0
- package/dist/test/helpers/jwt.js +132 -0
- package/dist/test/helpers/jwt.js.map +1 -0
- package/dist/test/helpers/mailpit.d.ts +61 -0
- package/dist/test/helpers/mailpit.d.ts.map +1 -0
- package/dist/test/helpers/mailpit.js +107 -0
- package/dist/test/helpers/mailpit.js.map +1 -0
- package/dist/test/setup.d.ts +2 -0
- package/dist/test/setup.d.ts.map +1 -0
- package/dist/test/setup.js +17 -0
- package/dist/test/setup.js.map +1 -0
- package/dist/types.d.ts +96 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/package.json +78 -0
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import * as jose from "jose";
|
|
2
|
+
import { PLATFORM_TENANT_ID } from "../../types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Verification error
|
|
5
|
+
*/
|
|
6
|
+
export class VerifyError extends Error {
|
|
7
|
+
code;
|
|
8
|
+
statusCode;
|
|
9
|
+
constructor(message, code, statusCode = 401) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.code = code;
|
|
12
|
+
this.statusCode = statusCode;
|
|
13
|
+
this.name = "VerifyError";
|
|
14
|
+
}
|
|
15
|
+
static invalidToken(reason) {
|
|
16
|
+
return new VerifyError(reason ? `Invalid token: ${reason}` : "Invalid token", "invalid_token", 401);
|
|
17
|
+
}
|
|
18
|
+
static tokenExpired() {
|
|
19
|
+
return new VerifyError("Token has expired", "token_expired", 401);
|
|
20
|
+
}
|
|
21
|
+
static insufficientRole(required, actual) {
|
|
22
|
+
return new VerifyError(`Insufficient role: required ${required}, got ${actual}`, "insufficient_role", 403);
|
|
23
|
+
}
|
|
24
|
+
static insufficientAal(required, actual) {
|
|
25
|
+
return new VerifyError(`Insufficient AAL: required ${required}, got ${actual}`, "insufficient_aal", 403);
|
|
26
|
+
}
|
|
27
|
+
static missingToken() {
|
|
28
|
+
return new VerifyError("Missing authorization token", "missing_token", 401);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
// Cache for remote JWKS to avoid creating multiple instances
|
|
32
|
+
const jwksCache = new Map();
|
|
33
|
+
/**
|
|
34
|
+
* Get the JWKS URL for a tenant
|
|
35
|
+
*
|
|
36
|
+
* @param baseUrl - The stack base URL (e.g., "https://stack.zenku.app")
|
|
37
|
+
* @param tenantId - The tenant identifier (default: "_platform")
|
|
38
|
+
* @returns The JWKS URL
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```typescript
|
|
42
|
+
* getJWKSUrl("https://stack.zenku.app")
|
|
43
|
+
* // → "https://stack.zenku.app/auth/_platform/.well-known/jwks.json"
|
|
44
|
+
*
|
|
45
|
+
* getJWKSUrl("https://stack.zenku.app", "acme-corp")
|
|
46
|
+
* // → "https://stack.zenku.app/auth/acme-corp/.well-known/jwks.json"
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export function getJWKSUrl(baseUrl, tenantId = PLATFORM_TENANT_ID) {
|
|
50
|
+
return `${baseUrl}/auth/${tenantId}/.well-known/jwks.json`;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Create a JWKS key getter
|
|
54
|
+
*
|
|
55
|
+
* Automatically constructs the JWKS URL from the base URL and optional tenant ID.
|
|
56
|
+
*
|
|
57
|
+
* @param baseUrl - The stack base URL (e.g., "https://stack.zenku.app")
|
|
58
|
+
* @param tenantId - The tenant identifier (default: "_platform")
|
|
59
|
+
* @returns A key getter function for use with verifyToken
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```typescript
|
|
63
|
+
* import { createJWKS, verifyToken } from "@kaiz11/stack-client/auth/server";
|
|
64
|
+
*
|
|
65
|
+
* // Platform (default)
|
|
66
|
+
* const getKey = createJWKS("https://stack.zenku.app");
|
|
67
|
+
*
|
|
68
|
+
* // Tenant
|
|
69
|
+
* const getKey = createJWKS("https://stack.zenku.app", "acme-corp");
|
|
70
|
+
*
|
|
71
|
+
* const payload = await verifyToken(token, getKey);
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
export function createJWKS(baseUrl, tenantId = PLATFORM_TENANT_ID) {
|
|
75
|
+
return createRemoteJWKS(getJWKSUrl(baseUrl, tenantId));
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Create a JWKS key getter from a URL
|
|
79
|
+
*
|
|
80
|
+
* Fetches and caches the JWKS from the GoTrue /.well-known/jwks.json endpoint.
|
|
81
|
+
* Uses jose's built-in caching and refresh logic.
|
|
82
|
+
*
|
|
83
|
+
* For most use cases, prefer `createPlatformJWKS` or `createTenantJWKS` which
|
|
84
|
+
* automatically construct the URL.
|
|
85
|
+
*
|
|
86
|
+
* @param jwksUrl - The JWKS endpoint URL
|
|
87
|
+
* @returns A key getter function for use with verifyToken
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* ```typescript
|
|
91
|
+
* import { createRemoteJWKS, verifyToken } from "@kaiz11/stack-client/auth/server";
|
|
92
|
+
*
|
|
93
|
+
* const getKey = createRemoteJWKS("https://stack.zenku.app/auth/_platform/.well-known/jwks.json");
|
|
94
|
+
* const payload = await verifyToken(token, getKey);
|
|
95
|
+
* ```
|
|
96
|
+
*/
|
|
97
|
+
export function createRemoteJWKS(jwksUrl) {
|
|
98
|
+
// Return cached instance if available
|
|
99
|
+
const cached = jwksCache.get(jwksUrl);
|
|
100
|
+
if (cached) {
|
|
101
|
+
return cached;
|
|
102
|
+
}
|
|
103
|
+
// Create new remote JWKS with caching
|
|
104
|
+
const keyGetter = jose.createRemoteJWKSet(new URL(jwksUrl), {
|
|
105
|
+
// Cache JWKS for 10 minutes, refresh in background
|
|
106
|
+
cacheMaxAge: 600_000,
|
|
107
|
+
cooldownDuration: 30_000,
|
|
108
|
+
});
|
|
109
|
+
jwksCache.set(jwksUrl, keyGetter);
|
|
110
|
+
return keyGetter;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Create a JWKS key getter from a local JWKS object
|
|
114
|
+
*
|
|
115
|
+
* Useful for testing without making network requests.
|
|
116
|
+
*
|
|
117
|
+
* @param jwks - The JWKS object containing keys
|
|
118
|
+
* @returns A key getter function for use with verifyToken
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* ```typescript
|
|
122
|
+
* import { createLocalJWKS, verifyToken } from "@kaiz11/stack-client/auth/server";
|
|
123
|
+
*
|
|
124
|
+
* const jwks = { keys: [{ kty: "EC", crv: "P-256", ... }] };
|
|
125
|
+
* const getKey = createLocalJWKS(jwks);
|
|
126
|
+
* const payload = await verifyToken(token, getKey);
|
|
127
|
+
* ```
|
|
128
|
+
*/
|
|
129
|
+
export function createLocalJWKS(jwks) {
|
|
130
|
+
return jose.createLocalJWKSet(jwks);
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Clear the JWKS cache (useful for testing or key rotation)
|
|
134
|
+
*/
|
|
135
|
+
export function clearJWKSCache() {
|
|
136
|
+
jwksCache.clear();
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Verify a JWT and extract the payload using ES256 (ECDSA P-256)
|
|
140
|
+
*
|
|
141
|
+
* @param token - The JWT access token to verify
|
|
142
|
+
* @param keyGetter - JWKS key getter (from createRemoteJWKS or createLocalJWKS)
|
|
143
|
+
* @param options - Optional verification options
|
|
144
|
+
* @returns The token payload if valid
|
|
145
|
+
* @throws VerifyError if the token is invalid or expired
|
|
146
|
+
*
|
|
147
|
+
* @example
|
|
148
|
+
* ```typescript
|
|
149
|
+
* import { createRemoteJWKS, verifyToken } from "@kaiz11/stack-client/auth/server";
|
|
150
|
+
*
|
|
151
|
+
* // Create key getter once (cached)
|
|
152
|
+
* const getKey = createRemoteJWKS(process.env.JWKS_URL!);
|
|
153
|
+
*
|
|
154
|
+
* // Verify tokens
|
|
155
|
+
* const payload = await verifyToken(token, getKey);
|
|
156
|
+
* console.log("User ID:", payload.sub);
|
|
157
|
+
* console.log("Email:", payload.email);
|
|
158
|
+
* console.log("Role:", payload.role);
|
|
159
|
+
* ```
|
|
160
|
+
*/
|
|
161
|
+
export async function verifyToken(token, keyGetter, options = {}) {
|
|
162
|
+
try {
|
|
163
|
+
const { payload } = await jose.jwtVerify(token, keyGetter, {
|
|
164
|
+
audience: options.audience,
|
|
165
|
+
clockTolerance: options.clockTolerance,
|
|
166
|
+
algorithms: ["ES256"], // Only accept ES256
|
|
167
|
+
});
|
|
168
|
+
const tokenPayload = payload;
|
|
169
|
+
// sub is required for authenticated users, but anon tokens don't have it
|
|
170
|
+
if (!tokenPayload.sub && tokenPayload.role !== "anon") {
|
|
171
|
+
throw VerifyError.invalidToken("missing sub claim");
|
|
172
|
+
}
|
|
173
|
+
// Check role if required
|
|
174
|
+
if (options.requiredRole && tokenPayload.role !== options.requiredRole) {
|
|
175
|
+
throw VerifyError.insufficientRole(options.requiredRole, tokenPayload.role);
|
|
176
|
+
}
|
|
177
|
+
// Check AAL if required
|
|
178
|
+
if (options.requiredAal) {
|
|
179
|
+
const actualAal = tokenPayload.aal || "aal1";
|
|
180
|
+
const aalOrder = { aal1: 1, aal2: 2 };
|
|
181
|
+
if (aalOrder[actualAal] < aalOrder[options.requiredAal]) {
|
|
182
|
+
throw VerifyError.insufficientAal(options.requiredAal, actualAal);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return tokenPayload;
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
if (error instanceof VerifyError) {
|
|
189
|
+
throw error;
|
|
190
|
+
}
|
|
191
|
+
if (error instanceof jose.errors.JWTExpired) {
|
|
192
|
+
throw VerifyError.tokenExpired();
|
|
193
|
+
}
|
|
194
|
+
if (error instanceof jose.errors.JWTClaimValidationFailed) {
|
|
195
|
+
throw VerifyError.invalidToken(error.message);
|
|
196
|
+
}
|
|
197
|
+
throw VerifyError.invalidToken();
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Extract bearer token from Authorization header
|
|
202
|
+
*
|
|
203
|
+
* @param authHeader - The Authorization header value
|
|
204
|
+
* @returns The token string or null if not a valid Bearer token
|
|
205
|
+
*
|
|
206
|
+
* @example
|
|
207
|
+
* ```typescript
|
|
208
|
+
* import { extractBearerToken } from "@kaiz11/stack-client/auth/server";
|
|
209
|
+
*
|
|
210
|
+
* const token = extractBearerToken(request.headers.get("Authorization"));
|
|
211
|
+
* if (token) {
|
|
212
|
+
* const payload = await verifyToken(token, secret);
|
|
213
|
+
* }
|
|
214
|
+
* ```
|
|
215
|
+
*/
|
|
216
|
+
export function extractBearerToken(authHeader) {
|
|
217
|
+
if (!authHeader?.startsWith("Bearer ")) {
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
220
|
+
return authHeader.slice(7);
|
|
221
|
+
}
|
|
222
|
+
//# sourceMappingURL=verify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify.js","sourceRoot":"","sources":["../../../src/auth/server/verify.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import type { HttpClient } from "../lib/http.js";
|
|
2
|
+
import type { TokenStore } from "../lib/token-store.js";
|
|
3
|
+
import type { Session, AuthChangeEvent, AuthChangeCallback, Unsubscribe } from "./types.js";
|
|
4
|
+
/**
|
|
5
|
+
* Custom refresh function type (for mock mode)
|
|
6
|
+
*/
|
|
7
|
+
export type RefreshFn = () => Promise<Session>;
|
|
8
|
+
/**
|
|
9
|
+
* Token manager configuration
|
|
10
|
+
*/
|
|
11
|
+
export interface TokenManagerConfig {
|
|
12
|
+
/** HTTP client for token refresh (optional if refreshFn provided) */
|
|
13
|
+
http?: HttpClient;
|
|
14
|
+
tokenStore: TokenStore;
|
|
15
|
+
tenantId: string;
|
|
16
|
+
/** Auto-refresh buffer in seconds (default: 60) */
|
|
17
|
+
autoRefreshBuffer?: number;
|
|
18
|
+
/** Custom refresh function (for mock mode) */
|
|
19
|
+
refreshFn?: RefreshFn;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Manages token storage, refresh, and auth state
|
|
23
|
+
*/
|
|
24
|
+
export declare class TokenManager {
|
|
25
|
+
private http;
|
|
26
|
+
private tokenStore;
|
|
27
|
+
private tenantId;
|
|
28
|
+
private autoRefreshBuffer;
|
|
29
|
+
private refreshFn;
|
|
30
|
+
private refreshTimer;
|
|
31
|
+
private listeners;
|
|
32
|
+
private currentSession;
|
|
33
|
+
constructor(config: TokenManagerConfig);
|
|
34
|
+
/**
|
|
35
|
+
* Initialize session from stored tokens
|
|
36
|
+
*/
|
|
37
|
+
private initializeFromStorage;
|
|
38
|
+
/**
|
|
39
|
+
* JWT payload structure (for decoding)
|
|
40
|
+
*/
|
|
41
|
+
private decodeToken;
|
|
42
|
+
/**
|
|
43
|
+
* Set the refresh function (for mock mode)
|
|
44
|
+
*
|
|
45
|
+
* This allows MockAuthClient to provide its refresh implementation
|
|
46
|
+
* after TokenManager is constructed.
|
|
47
|
+
*/
|
|
48
|
+
setRefreshFn(fn: RefreshFn): void;
|
|
49
|
+
/**
|
|
50
|
+
* Set session from auth response
|
|
51
|
+
*/
|
|
52
|
+
setSession(session: Session, event?: AuthChangeEvent): void;
|
|
53
|
+
/**
|
|
54
|
+
* Clear current session
|
|
55
|
+
*/
|
|
56
|
+
clearSession(): void;
|
|
57
|
+
/**
|
|
58
|
+
* Get current session
|
|
59
|
+
*/
|
|
60
|
+
getSession(): Session | null;
|
|
61
|
+
/**
|
|
62
|
+
* Get access token (null if not authenticated)
|
|
63
|
+
*/
|
|
64
|
+
getAccessToken(): string | null;
|
|
65
|
+
/**
|
|
66
|
+
* Check if current token is expired
|
|
67
|
+
*/
|
|
68
|
+
isTokenExpired(): boolean;
|
|
69
|
+
/**
|
|
70
|
+
* Refresh the current session
|
|
71
|
+
*/
|
|
72
|
+
refreshSession(): Promise<Session>;
|
|
73
|
+
/**
|
|
74
|
+
* Schedule automatic token refresh
|
|
75
|
+
*/
|
|
76
|
+
private scheduleRefresh;
|
|
77
|
+
/**
|
|
78
|
+
* Cancel scheduled refresh
|
|
79
|
+
*/
|
|
80
|
+
private cancelRefresh;
|
|
81
|
+
/**
|
|
82
|
+
* Subscribe to auth state changes
|
|
83
|
+
*/
|
|
84
|
+
onAuthStateChange(callback: AuthChangeCallback): Unsubscribe;
|
|
85
|
+
/**
|
|
86
|
+
* Notify all listeners of auth state change
|
|
87
|
+
*/
|
|
88
|
+
private notifyListeners;
|
|
89
|
+
/**
|
|
90
|
+
* Cleanup resources
|
|
91
|
+
*/
|
|
92
|
+
destroy(): void;
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=token-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-manager.d.ts","sourceRoot":"","sources":["../../src/auth/token-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAGxD,OAAO,KAAK,EAEV,OAAO,EACP,eAAe,EACf,kBAAkB,EAClB,WAAW,EACZ,MAAM,YAAY,CAAC;AAgBpB;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;AAE/C;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,qEAAqE;IACrE,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,UAAU,EAAE,UAAU,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,mDAAmD;IACnD,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,8CAA8C;IAC9C,SAAS,CAAC,EAAE,SAAS,CAAC;CACvB;AAED;;GAEG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,IAAI,CAAoB;IAChC,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,SAAS,CAAmB;IACpC,OAAO,CAAC,YAAY,CAA8C;IAClE,OAAO,CAAC,SAAS,CAAsC;IACvD,OAAO,CAAC,cAAc,CAAwB;gBAElC,MAAM,EAAE,kBAAkB;IAWtC;;OAEG;IACH,OAAO,CAAC,qBAAqB;IA8C7B;;OAEG;IACH,OAAO,CAAC,WAAW;IAWnB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,EAAE,SAAS,GAAG,IAAI;IAIjC;;OAEG;IACH,UAAU,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,GAAE,eAA6B,GAAG,IAAI;IAOxE;;OAEG;IACH,YAAY,IAAI,IAAI;IAOpB;;OAEG;IACH,UAAU,IAAI,OAAO,GAAG,IAAI;IAI5B;;OAEG;IACH,cAAc,IAAI,MAAM,GAAG,IAAI;IAI/B;;OAEG;IACH,cAAc,IAAI,OAAO;IAczB;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC;IAuCxC;;OAEG;IACH,OAAO,CAAC,eAAe;IAiBvB;;OAEG;IACH,OAAO,CAAC,aAAa;IAOrB;;OAEG;IACH,iBAAiB,CAAC,QAAQ,EAAE,kBAAkB,GAAG,WAAW;IAa5D;;OAEG;IACH,OAAO,CAAC,eAAe;IAavB;;OAEG;IACH,OAAO,IAAI,IAAI;CAIhB"}
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import { authPath } from "../lib/paths.js";
|
|
2
|
+
import { AuthError } from "../lib/errors.js";
|
|
3
|
+
import { normalizeSession } from "./types.js";
|
|
4
|
+
/**
|
|
5
|
+
* Manages token storage, refresh, and auth state
|
|
6
|
+
*/
|
|
7
|
+
export class TokenManager {
|
|
8
|
+
http;
|
|
9
|
+
tokenStore;
|
|
10
|
+
tenantId;
|
|
11
|
+
autoRefreshBuffer;
|
|
12
|
+
refreshFn;
|
|
13
|
+
refreshTimer = null;
|
|
14
|
+
listeners = new Set();
|
|
15
|
+
currentSession = null;
|
|
16
|
+
constructor(config) {
|
|
17
|
+
this.http = config.http ?? null;
|
|
18
|
+
this.tokenStore = config.tokenStore;
|
|
19
|
+
this.tenantId = config.tenantId;
|
|
20
|
+
this.autoRefreshBuffer = config.autoRefreshBuffer ?? 60;
|
|
21
|
+
this.refreshFn = config.refreshFn ?? null;
|
|
22
|
+
// Initialize session from stored tokens
|
|
23
|
+
this.initializeFromStorage();
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Initialize session from stored tokens
|
|
27
|
+
*/
|
|
28
|
+
initializeFromStorage() {
|
|
29
|
+
const accessToken = this.tokenStore.getAccessToken();
|
|
30
|
+
const refreshToken = this.tokenStore.getRefreshToken();
|
|
31
|
+
if (accessToken && refreshToken) {
|
|
32
|
+
// Decode token to check expiry and get user info
|
|
33
|
+
try {
|
|
34
|
+
const payload = this.decodeToken(accessToken);
|
|
35
|
+
const now = Math.floor(Date.now() / 1000);
|
|
36
|
+
if (payload.exp > now) {
|
|
37
|
+
// Token still valid, reconstruct session
|
|
38
|
+
this.currentSession = {
|
|
39
|
+
accessToken,
|
|
40
|
+
refreshToken,
|
|
41
|
+
expiresIn: payload.exp - now,
|
|
42
|
+
expiresAt: payload.exp,
|
|
43
|
+
tokenType: "bearer",
|
|
44
|
+
user: {
|
|
45
|
+
id: payload.sub,
|
|
46
|
+
email: payload.email ?? null,
|
|
47
|
+
phone: payload.phone ?? null,
|
|
48
|
+
emailConfirmedAt: null,
|
|
49
|
+
phoneConfirmedAt: null,
|
|
50
|
+
createdAt: new Date(payload.iat * 1000).toISOString(),
|
|
51
|
+
updatedAt: new Date(payload.iat * 1000).toISOString(),
|
|
52
|
+
appMetadata: payload.app_metadata ?? {},
|
|
53
|
+
userMetadata: payload.user_metadata ?? {},
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
// Schedule auto-refresh
|
|
57
|
+
this.scheduleRefresh();
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
// Token expired, try to refresh
|
|
61
|
+
this.refreshSession().catch(() => {
|
|
62
|
+
this.clearSession();
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
// Invalid token, clear storage
|
|
68
|
+
this.clearSession();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* JWT payload structure (for decoding)
|
|
74
|
+
*/
|
|
75
|
+
decodeToken(token) {
|
|
76
|
+
const parts = token.split(".");
|
|
77
|
+
if (parts.length !== 3) {
|
|
78
|
+
throw new Error("Invalid JWT format");
|
|
79
|
+
}
|
|
80
|
+
const payload = parts[1];
|
|
81
|
+
const decoded = atob(payload.replace(/-/g, "+").replace(/_/g, "/"));
|
|
82
|
+
return JSON.parse(decoded);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Set the refresh function (for mock mode)
|
|
86
|
+
*
|
|
87
|
+
* This allows MockAuthClient to provide its refresh implementation
|
|
88
|
+
* after TokenManager is constructed.
|
|
89
|
+
*/
|
|
90
|
+
setRefreshFn(fn) {
|
|
91
|
+
this.refreshFn = fn;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Set session from auth response
|
|
95
|
+
*/
|
|
96
|
+
setSession(session, event = "SIGNED_IN") {
|
|
97
|
+
this.currentSession = session;
|
|
98
|
+
this.tokenStore.setTokens(session.accessToken, session.refreshToken);
|
|
99
|
+
this.scheduleRefresh();
|
|
100
|
+
this.notifyListeners(event, session);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Clear current session
|
|
104
|
+
*/
|
|
105
|
+
clearSession() {
|
|
106
|
+
this.currentSession = null;
|
|
107
|
+
this.tokenStore.clearTokens();
|
|
108
|
+
this.cancelRefresh();
|
|
109
|
+
this.notifyListeners("SIGNED_OUT", null);
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Get current session
|
|
113
|
+
*/
|
|
114
|
+
getSession() {
|
|
115
|
+
return this.currentSession;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Get access token (null if not authenticated)
|
|
119
|
+
*/
|
|
120
|
+
getAccessToken() {
|
|
121
|
+
return this.tokenStore.getAccessToken();
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Check if current token is expired
|
|
125
|
+
*/
|
|
126
|
+
isTokenExpired() {
|
|
127
|
+
const accessToken = this.tokenStore.getAccessToken();
|
|
128
|
+
if (!accessToken)
|
|
129
|
+
return true;
|
|
130
|
+
try {
|
|
131
|
+
const payload = this.decodeToken(accessToken);
|
|
132
|
+
const exp = payload.exp;
|
|
133
|
+
const now = Math.floor(Date.now() / 1000);
|
|
134
|
+
return exp <= now;
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Refresh the current session
|
|
142
|
+
*/
|
|
143
|
+
async refreshSession() {
|
|
144
|
+
// Use custom refresh function if provided (mock mode)
|
|
145
|
+
if (this.refreshFn) {
|
|
146
|
+
return this.refreshFn();
|
|
147
|
+
}
|
|
148
|
+
// HTTP-based refresh
|
|
149
|
+
const refreshToken = this.tokenStore.getRefreshToken();
|
|
150
|
+
if (!refreshToken) {
|
|
151
|
+
throw AuthError.notAuthenticated();
|
|
152
|
+
}
|
|
153
|
+
if (!this.http) {
|
|
154
|
+
throw new Error("TokenManager requires http client or refreshFn for token refresh");
|
|
155
|
+
}
|
|
156
|
+
try {
|
|
157
|
+
const path = authPath(this.tenantId, "/token?grant_type=refresh_token");
|
|
158
|
+
const response = await this.http.post(path, { refresh_token: refreshToken }, { noAuth: true });
|
|
159
|
+
const session = normalizeSession(response);
|
|
160
|
+
this.currentSession = session;
|
|
161
|
+
this.tokenStore.setTokens(session.accessToken, session.refreshToken);
|
|
162
|
+
this.scheduleRefresh();
|
|
163
|
+
this.notifyListeners("TOKEN_REFRESHED", session);
|
|
164
|
+
return session;
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
this.clearSession();
|
|
168
|
+
throw AuthError.refreshFailed();
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Schedule automatic token refresh
|
|
173
|
+
*/
|
|
174
|
+
scheduleRefresh() {
|
|
175
|
+
this.cancelRefresh();
|
|
176
|
+
if (!this.currentSession)
|
|
177
|
+
return;
|
|
178
|
+
// Calculate refresh time (expiresAt - buffer)
|
|
179
|
+
const now = Math.floor(Date.now() / 1000);
|
|
180
|
+
const refreshAt = this.currentSession.expiresAt - this.autoRefreshBuffer;
|
|
181
|
+
const delay = Math.max(0, (refreshAt - now) * 1000);
|
|
182
|
+
this.refreshTimer = setTimeout(() => {
|
|
183
|
+
this.refreshSession().catch(() => {
|
|
184
|
+
// Refresh failed, session cleared
|
|
185
|
+
});
|
|
186
|
+
}, delay);
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Cancel scheduled refresh
|
|
190
|
+
*/
|
|
191
|
+
cancelRefresh() {
|
|
192
|
+
if (this.refreshTimer) {
|
|
193
|
+
clearTimeout(this.refreshTimer);
|
|
194
|
+
this.refreshTimer = null;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Subscribe to auth state changes
|
|
199
|
+
*/
|
|
200
|
+
onAuthStateChange(callback) {
|
|
201
|
+
this.listeners.add(callback);
|
|
202
|
+
// Immediately notify with current state
|
|
203
|
+
if (this.currentSession) {
|
|
204
|
+
callback("SIGNED_IN", this.currentSession);
|
|
205
|
+
}
|
|
206
|
+
return () => {
|
|
207
|
+
this.listeners.delete(callback);
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Notify all listeners of auth state change
|
|
212
|
+
*/
|
|
213
|
+
notifyListeners(event, session) {
|
|
214
|
+
for (const listener of this.listeners) {
|
|
215
|
+
try {
|
|
216
|
+
listener(event, session);
|
|
217
|
+
}
|
|
218
|
+
catch {
|
|
219
|
+
// Ignore listener errors
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Cleanup resources
|
|
225
|
+
*/
|
|
226
|
+
destroy() {
|
|
227
|
+
this.cancelRefresh();
|
|
228
|
+
this.listeners.clear();
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
//# sourceMappingURL=token-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-manager.js","sourceRoot":"","sources":["../../src/auth/token-manager.ts"],"names":[],"mappings":"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"}
|