@korajs/auth 1.0.0-beta.12 → 1.0.0-beta.13
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/README.md +52 -47
- package/dist/{create-org-session-RsDj9cl4.d.cts → create-org-session-ChFdulEM.d.cts} +211 -17
- package/dist/{create-org-session-RsDj9cl4.d.ts → create-org-session-ChFdulEM.d.ts} +211 -17
- package/dist/index.cjs +645 -150
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +27 -9
- package/dist/index.d.ts +27 -9
- package/dist/index.js +644 -150
- package/dist/index.js.map +1 -1
- package/dist/{operation-encryptor-DRmKNWpF.d.cts → operation-encryptor-DDdlb9bm.d.cts} +16 -0
- package/dist/{operation-encryptor-DRmKNWpF.d.ts → operation-encryptor-DDdlb9bm.d.ts} +16 -0
- package/dist/react.d.cts +2 -2
- package/dist/react.d.ts +2 -2
- package/dist/server.cjs +2852 -1675
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +779 -168
- package/dist/server.d.ts +779 -168
- package/dist/server.js +2831 -1665
- package/dist/server.js.map +1 -1
- package/dist/svelte.cjs +2 -2
- package/dist/svelte.cjs.map +1 -1
- package/dist/svelte.d.cts +2 -2
- package/dist/svelte.d.ts +2 -2
- package/dist/svelte.js +2 -2
- package/dist/svelte.js.map +1 -1
- package/dist/vue.d.cts +1 -1
- package/dist/vue.d.ts +1 -1
- package/package.json +7 -7
- package/src/admin/admin-api.ts +327 -0
- package/src/admin/audit-log.ts +324 -0
- package/src/admin/webhooks.ts +576 -0
- package/src/bindings/create-auth-session.ts +184 -0
- package/src/bindings/create-org-session.ts +130 -0
- package/src/client/auth-client.ts +1592 -0
- package/src/client/auth-sync.ts +213 -0
- package/src/client/device-session.ts +104 -0
- package/src/client/org-client.ts +399 -0
- package/src/client/quickstart.ts +108 -0
- package/src/client/storage.ts +94 -0
- package/src/device/device-identity.ts +330 -0
- package/src/device/device-store.ts +379 -0
- package/src/encryption/auto-lock.ts +170 -0
- package/src/encryption/database-encryption.ts +265 -0
- package/src/encryption/key-derivation.ts +149 -0
- package/src/encryption/operation-encryptor.ts +361 -0
- package/src/index.ts +132 -0
- package/src/mfa/totp.ts +826 -0
- package/src/org/org-routes.ts +758 -0
- package/src/org/org-store.ts +490 -0
- package/src/org/org-types.ts +230 -0
- package/src/passkey/passkey-client.ts +597 -0
- package/src/passkey/passkey-server.ts +779 -0
- package/src/postgres/ensure-schema.ts +65 -0
- package/src/provider/adapter.ts +246 -0
- package/src/provider/built-in/auth-routes.ts +1313 -0
- package/src/provider/built-in/email-verification.ts +303 -0
- package/src/provider/built-in/password-hash.ts +118 -0
- package/src/provider/built-in/password-reset.ts +416 -0
- package/src/provider/built-in/postgres-user-store.ts +328 -0
- package/src/provider/built-in/quickstart-server.ts +760 -0
- package/src/provider/built-in/sqlite-user-store.ts +322 -0
- package/src/provider/built-in/sync-scopes.ts +85 -0
- package/src/provider/built-in/user-store.ts +465 -0
- package/src/provider/external/clerk-adapter.ts +157 -0
- package/src/provider/external/external-jwt-provider.ts +491 -0
- package/src/provider/external/supabase-adapter.ts +163 -0
- package/src/provider/oauth/linked-identity-store.ts +108 -0
- package/src/provider/oauth/oauth-flow.ts +550 -0
- package/src/provider/oauth/oauth-types.ts +184 -0
- package/src/provider/oauth/postgres-oauth-store.ts +296 -0
- package/src/provider/oauth/sqlite-oauth-store.ts +272 -0
- package/src/rbac/rbac-engine.ts +323 -0
- package/src/rbac/rbac-types.ts +210 -0
- package/src/rbac/scope-resolver.ts +140 -0
- package/src/react/AuthProvider.tsx +97 -0
- package/src/react/OrgProvider.tsx +41 -0
- package/src/react/auth-context.ts +26 -0
- package/src/react/hooks.ts +110 -0
- package/src/react/org-hooks.ts +214 -0
- package/src/react.ts +26 -0
- package/src/server.ts +334 -0
- package/src/session/session.ts +401 -0
- package/src/svelte/auth-context.ts +50 -0
- package/src/svelte/org-context.ts +32 -0
- package/src/svelte/org-hooks.ts +201 -0
- package/src/svelte/use-auth.ts +115 -0
- package/src/svelte.ts +25 -0
- package/src/tokens/encrypted-token-store.ts +360 -0
- package/src/tokens/jwt.ts +236 -0
- package/src/tokens/postgres-token-revocation-store.ts +140 -0
- package/src/tokens/sqlite-token-revocation-store.ts +121 -0
- package/src/tokens/token-manager.ts +821 -0
- package/src/tokens/token-store.ts +192 -0
- package/src/types.ts +394 -0
- package/src/vue/auth-context.ts +10 -0
- package/src/vue/auth-provider-types.ts +5 -0
- package/src/vue/auth-provider.ts +76 -0
- package/src/vue/org-hooks.ts +193 -0
- package/src/vue/org-provider.ts +49 -0
- package/src/vue/use-auth.ts +139 -0
- package/src/vue.ts +10 -0
package/dist/server.d.ts
CHANGED
|
@@ -1,65 +1,105 @@
|
|
|
1
|
-
import { A as AuthTokens, T as TokenPayload } from './operation-encryptor-
|
|
2
|
-
export { O as OperationEncryptionError, e as OperationEncryptor, f as OperationEncryptorConfig, h as computePublicKeyThumbprint, l as isEncryptedField, v as verifyChallenge } from './operation-encryptor-
|
|
3
|
-
import { KoraError } from '@korajs/core';
|
|
1
|
+
import { A as AuthTokens, T as TokenPayload } from './operation-encryptor-DDdlb9bm.js';
|
|
2
|
+
export { O as OperationEncryptionError, e as OperationEncryptor, f as OperationEncryptorConfig, h as computePublicKeyThumbprint, l as isEncryptedField, v as verifyChallenge } from './operation-encryptor-DDdlb9bm.js';
|
|
3
|
+
import { ScopeMap, KoraError } from '@korajs/core';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Default window during which a just-rotated refresh token may be presented
|
|
7
|
+
* once more and receive the SAME successor pair (NEW-AUTH-3). Covers a rotation
|
|
8
|
+
* response lost on a flaky network without opening a long replay window.
|
|
9
|
+
*/
|
|
10
|
+
declare const DEFAULT_REFRESH_REUSE_GRACE_MS = 30000;
|
|
11
|
+
/**
|
|
12
|
+
* Payload of an `mfa_pending` token: proof that the first factor succeeded,
|
|
13
|
+
* accepted ONLY by the MFA verification step and rejected everywhere else
|
|
14
|
+
* (`validateToken` does not recognise its type).
|
|
15
|
+
*/
|
|
16
|
+
interface MfaPendingPayload {
|
|
17
|
+
jti: string;
|
|
18
|
+
sub: string;
|
|
19
|
+
dev: string;
|
|
20
|
+
type: 'mfa_pending';
|
|
21
|
+
iat: number;
|
|
22
|
+
exp: number;
|
|
23
|
+
/** Methods already satisfied (for example `['pwd']`). */
|
|
24
|
+
amr: string[];
|
|
25
|
+
}
|
|
26
|
+
/** Result of an atomic {@link TokenRevocationStore.consume}. */
|
|
27
|
+
interface ConsumeResult {
|
|
28
|
+
/** True only for the single call that consumed the id first. */
|
|
29
|
+
firstUse: boolean;
|
|
30
|
+
/** When the id was first consumed (milliseconds since epoch). */
|
|
31
|
+
consumedAt: number;
|
|
32
|
+
}
|
|
5
33
|
/**
|
|
6
34
|
* Interface for server-side token revocation storage.
|
|
7
35
|
*
|
|
8
36
|
* Implementing this interface allows the TokenManager to:
|
|
9
|
-
* - Revoke individual tokens by
|
|
10
|
-
* -
|
|
11
|
-
* - Invalidate
|
|
37
|
+
* - Revoke individual tokens (and token families) by id
|
|
38
|
+
* - Rotate refresh tokens atomically (each refresh token mints at most one successor)
|
|
39
|
+
* - Invalidate every credential a device or a user obtained before a point in time
|
|
12
40
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
41
|
+
* Shipped implementations: {@link InMemoryTokenRevocationStore} (development),
|
|
42
|
+
* `SqliteTokenRevocationStore` and `PostgresTokenRevocationStore`. The SQLite and
|
|
43
|
+
* Postgres user stores expose one sharing their database through
|
|
44
|
+
* `getTokenRevocationStore()`, which `createKoraAuthServer` uses by default.
|
|
15
45
|
*/
|
|
16
46
|
interface TokenRevocationStore {
|
|
17
47
|
/**
|
|
18
|
-
* Check whether a token has been revoked.
|
|
19
|
-
* @param jti - The JWT ID to check
|
|
20
|
-
* @returns true if the token has been revoked
|
|
48
|
+
* Check whether a token (or token family, keyed `family:<id>`) has been revoked.
|
|
49
|
+
* @param jti - The JWT ID (or family key) to check
|
|
21
50
|
*/
|
|
22
51
|
isRevoked(jti: string): Promise<boolean>;
|
|
23
52
|
/**
|
|
24
|
-
* Revoke a specific token
|
|
25
|
-
* @param jti - The JWT ID to revoke
|
|
26
|
-
* @param expiresAt -
|
|
27
|
-
* The store may use this to auto-purge expired revocations.
|
|
53
|
+
* Revoke a specific token (or token family) by id.
|
|
54
|
+
* @param jti - The JWT ID (or family key) to revoke
|
|
55
|
+
* @param expiresAt - Expiry in seconds since epoch; the store may purge after it
|
|
28
56
|
*/
|
|
29
57
|
revoke(jti: string, expiresAt: number): Promise<void>;
|
|
30
58
|
/**
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* @param
|
|
59
|
+
* Atomically mark an id as consumed (test-and-set). Exactly one concurrent
|
|
60
|
+
* caller observes `firstUse: true`, on every instance sharing the store.
|
|
61
|
+
* @param jti - The id to consume
|
|
62
|
+
* @param expiresAt - Expiry in seconds since epoch; the store may purge after it
|
|
34
63
|
*/
|
|
35
|
-
|
|
64
|
+
consume(jti: string, expiresAt: number): Promise<ConsumeResult>;
|
|
65
|
+
/** Whether an id has been consumed (read-only; never consumes). */
|
|
66
|
+
isConsumed(jti: string): Promise<boolean>;
|
|
36
67
|
/**
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
68
|
+
* Reject every token for a device issued at or before `before` (ms). Later
|
|
69
|
+
* tokens (a fresh sign-in on the same device) stay valid. Monotonic: a
|
|
70
|
+
* smaller `before` never lowers an existing cut-off.
|
|
40
71
|
*/
|
|
41
|
-
|
|
72
|
+
revokeAllForDevice(deviceId: string, before?: number): Promise<void>;
|
|
73
|
+
/** Cut-off (ms) recorded by {@link revokeAllForDevice}, or null. */
|
|
74
|
+
getDeviceRevokedBefore(deviceId: string): Promise<number | null>;
|
|
75
|
+
/**
|
|
76
|
+
* Reject every token for a user issued at or before `before` (ms). Used by
|
|
77
|
+
* password reset and change, admin session revocation and user deletion.
|
|
78
|
+
*/
|
|
79
|
+
revokeAllForUser(userId: string, before?: number): Promise<void>;
|
|
80
|
+
/** Cut-off (ms) recorded by {@link revokeAllForUser}, or null. */
|
|
81
|
+
getUserRevokedBefore(userId: string): Promise<number | null>;
|
|
42
82
|
}
|
|
43
83
|
/**
|
|
44
84
|
* In-memory token revocation store.
|
|
45
85
|
*
|
|
46
|
-
* Suitable for development and testing.
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
* **Not suitable for production**: revocations are lost on server restart
|
|
50
|
-
* and not shared across server instances. Use a Redis or database-backed
|
|
51
|
-
* store in production.
|
|
86
|
+
* Suitable for development and testing. Revocations are lost on restart and not
|
|
87
|
+
* shared across instances, so `createKoraAuthServer` refuses it in production
|
|
88
|
+
* unless `allowInMemory: true` is set.
|
|
52
89
|
*/
|
|
53
90
|
declare class InMemoryTokenRevocationStore implements TokenRevocationStore {
|
|
54
91
|
private readonly revokedTokens;
|
|
55
|
-
private readonly
|
|
92
|
+
private readonly consumed;
|
|
93
|
+
private readonly deviceCutoffs;
|
|
94
|
+
private readonly userCutoffs;
|
|
56
95
|
isRevoked(jti: string): Promise<boolean>;
|
|
57
96
|
revoke(jti: string, expiresAt: number): Promise<void>;
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
97
|
+
consume(jti: string, expiresAt: number): Promise<ConsumeResult>;
|
|
98
|
+
isConsumed(jti: string): Promise<boolean>;
|
|
99
|
+
revokeAllForDevice(deviceId: string, before?: number): Promise<void>;
|
|
100
|
+
getDeviceRevokedBefore(deviceId: string): Promise<number | null>;
|
|
101
|
+
revokeAllForUser(userId: string, before?: number): Promise<void>;
|
|
102
|
+
getUserRevokedBefore(userId: string): Promise<number | null>;
|
|
63
103
|
/**
|
|
64
104
|
* Remove expired revocations to prevent unbounded memory growth.
|
|
65
105
|
* Call periodically (e.g., every hour) in long-running servers.
|
|
@@ -78,8 +118,6 @@ interface TokenManagerConfig {
|
|
|
78
118
|
*
|
|
79
119
|
* For key rotation, provide an array of secrets. The first secret is used for
|
|
80
120
|
* signing new tokens; all secrets are tried during verification (newest first).
|
|
81
|
-
* This allows graceful rotation: add the new secret at index 0, then remove
|
|
82
|
-
* the old secret after all tokens signed with it have expired.
|
|
83
121
|
*/
|
|
84
122
|
secret: string | string[];
|
|
85
123
|
/** Access token lifetime in milliseconds (default: 15 minutes) */
|
|
@@ -89,22 +127,48 @@ interface TokenManagerConfig {
|
|
|
89
127
|
/** Device credential lifetime in milliseconds (default: 90 days) */
|
|
90
128
|
deviceCredentialLifetime?: number;
|
|
91
129
|
/**
|
|
92
|
-
* Optional token revocation store. When provided, enables
|
|
93
|
-
*
|
|
94
|
-
* - Refresh token reuse detection (consumed tokens are tracked)
|
|
95
|
-
* - Device-level token invalidation via `revokeDeviceTokens()`
|
|
96
|
-
*
|
|
130
|
+
* Optional token revocation store. When provided, enables revocation,
|
|
131
|
+
* atomic refresh rotation with reuse detection, and device/user cut-offs.
|
|
97
132
|
* Without a revocation store, tokens are valid until they expire.
|
|
98
133
|
*/
|
|
99
134
|
revocationStore?: TokenRevocationStore;
|
|
135
|
+
/**
|
|
136
|
+
* Window (ms) during which a just-rotated refresh token is accepted ONCE more
|
|
137
|
+
* and returns the same successor pair. Set 0 to disable. Default 30 seconds.
|
|
138
|
+
*/
|
|
139
|
+
refreshReuseGraceMs?: number;
|
|
100
140
|
}
|
|
141
|
+
/** Options for minting a token set. */
|
|
142
|
+
interface IssueTokenOptions {
|
|
143
|
+
/** Refresh-token family to continue. A new family is started when omitted. */
|
|
144
|
+
family?: string;
|
|
145
|
+
/** Authentication methods (RFC 8176) to record, for example `['pwd', 'otp']`. */
|
|
146
|
+
amr?: string[];
|
|
147
|
+
}
|
|
148
|
+
/** Why a refresh was refused. */
|
|
149
|
+
type RefreshFailureReason = 'invalid' | 'revoked' | 'reused' | 'in_progress' | 'device_revoked' | 'user_revoked';
|
|
150
|
+
/** Outcome of {@link TokenManager.rotateRefreshToken}. */
|
|
151
|
+
type RefreshResult = {
|
|
152
|
+
ok: true;
|
|
153
|
+
tokens: {
|
|
154
|
+
accessToken: string;
|
|
155
|
+
refreshToken: string;
|
|
156
|
+
};
|
|
157
|
+
/** The verified payload of the refresh token that was presented. */
|
|
158
|
+
payload: TokenPayload;
|
|
159
|
+
/** True when this was the one grace replay of a just-rotated token. */
|
|
160
|
+
replayed: boolean;
|
|
161
|
+
} | {
|
|
162
|
+
ok: false;
|
|
163
|
+
reason: RefreshFailureReason;
|
|
164
|
+
};
|
|
101
165
|
/**
|
|
102
166
|
* Server-side token manager responsible for issuing, refreshing, and validating
|
|
103
167
|
* Kora authentication tokens.
|
|
104
168
|
*
|
|
105
169
|
* Uses HMAC-SHA256 signed JWTs with unique `jti` identifiers for every token.
|
|
106
|
-
* Supports key rotation (multiple secrets), token revocation,
|
|
107
|
-
*
|
|
170
|
+
* Supports key rotation (multiple secrets), token revocation, token families and
|
|
171
|
+
* atomic, rotation-safe refresh.
|
|
108
172
|
*
|
|
109
173
|
* @example
|
|
110
174
|
* ```typescript
|
|
@@ -113,14 +177,9 @@ interface TokenManagerConfig {
|
|
|
113
177
|
* revocationStore: new InMemoryTokenRevocationStore(),
|
|
114
178
|
* })
|
|
115
179
|
*
|
|
116
|
-
* // Issue all tokens at once
|
|
117
180
|
* const tokens = tokenManager.issueTokens('user-123', 'device-456')
|
|
118
|
-
*
|
|
119
|
-
*
|
|
120
|
-
* const payload = tokenManager.validateToken(tokens.accessToken)
|
|
121
|
-
*
|
|
122
|
-
* // Refresh when the access token expires
|
|
123
|
-
* const newTokens = await tokenManager.refreshAccessToken(tokens.refreshToken)
|
|
181
|
+
* const payload = await tokenManager.validateTokenWithRevocation(tokens.accessToken)
|
|
182
|
+
* const next = await tokenManager.rotateRefreshToken(tokens.refreshToken)
|
|
124
183
|
* ```
|
|
125
184
|
*/
|
|
126
185
|
declare class TokenManager {
|
|
@@ -130,46 +189,38 @@ declare class TokenManager {
|
|
|
130
189
|
private readonly refreshTokenLifetime;
|
|
131
190
|
private readonly deviceCredentialLifetime;
|
|
132
191
|
private readonly revocationStore;
|
|
192
|
+
private readonly refreshReuseGraceMs;
|
|
193
|
+
/** Refresh jtis whose rotation is executing on this instance right now. */
|
|
194
|
+
private readonly rotating;
|
|
133
195
|
constructor(config: TokenManagerConfig);
|
|
134
196
|
/**
|
|
135
197
|
* Generate a cryptographically random secret suitable for HMAC-SHA256 signing.
|
|
136
198
|
*
|
|
137
|
-
* Returns a 64-character hex string (32 bytes / 256 bits of entropy).
|
|
138
|
-
* Store this securely (environment variable, secrets manager) — never in source code.
|
|
139
|
-
*
|
|
140
199
|
* @returns A random 256-bit hex-encoded secret
|
|
141
200
|
*/
|
|
142
201
|
static generateSecret(): string;
|
|
202
|
+
/** The revocation store this manager enforces, if any. */
|
|
203
|
+
getRevocationStore(): TokenRevocationStore | undefined;
|
|
143
204
|
/**
|
|
144
205
|
* Issue a signed JWT access token.
|
|
145
206
|
*
|
|
146
|
-
* Access tokens are short-lived (default 15 minutes) and used to authorize
|
|
147
|
-
* API requests. When expired, use {@link refreshAccessToken} with a valid
|
|
148
|
-
* refresh token to obtain a new one.
|
|
149
|
-
*
|
|
150
207
|
* @param userId - The subject (user ID) to encode in the token
|
|
151
208
|
* @param deviceId - The device ID of the requesting device
|
|
209
|
+
* @param options - Family and authentication methods to record
|
|
152
210
|
* @returns A signed JWT string with type 'access'
|
|
153
211
|
*/
|
|
154
|
-
issueAccessToken(userId: string, deviceId: string): string;
|
|
212
|
+
issueAccessToken(userId: string, deviceId: string, options?: IssueTokenOptions): string;
|
|
155
213
|
/**
|
|
156
214
|
* Issue a signed JWT refresh token.
|
|
157
215
|
*
|
|
158
|
-
* Refresh tokens are longer-lived (default 90 days) and used exclusively
|
|
159
|
-
* to obtain new access tokens via {@link refreshAccessToken}. They should
|
|
160
|
-
* be stored securely and never sent to resource APIs.
|
|
161
|
-
*
|
|
162
216
|
* @param userId - The subject (user ID) to encode in the token
|
|
163
217
|
* @param deviceId - The device ID of the requesting device
|
|
218
|
+
* @param options - Family and authentication methods to record
|
|
164
219
|
* @returns A signed JWT string with type 'refresh'
|
|
165
220
|
*/
|
|
166
|
-
issueRefreshToken(userId: string, deviceId: string): string;
|
|
221
|
+
issueRefreshToken(userId: string, deviceId: string, options?: IssueTokenOptions): string;
|
|
167
222
|
/**
|
|
168
|
-
* Issue a signed device credential token.
|
|
169
|
-
*
|
|
170
|
-
* Device credentials are long-lived tokens bound to a device's public key.
|
|
171
|
-
* They include a `mustCheckinBy` deadline; if the device does not check in
|
|
172
|
-
* before this deadline, the credential should be treated as revoked.
|
|
223
|
+
* Issue a signed device credential token bound to a device's public key.
|
|
173
224
|
*
|
|
174
225
|
* @param userId - The subject (user ID) to encode in the token
|
|
175
226
|
* @param deviceId - The device ID of the requesting device
|
|
@@ -178,77 +229,192 @@ declare class TokenManager {
|
|
|
178
229
|
*/
|
|
179
230
|
issueDeviceCredential(userId: string, deviceId: string, publicKeyThumbprint: string): string;
|
|
180
231
|
/**
|
|
181
|
-
* Issue a complete set of authentication tokens.
|
|
182
|
-
*
|
|
183
|
-
* Always issues an access token and refresh token. If a `publicKeyThumbprint`
|
|
184
|
-
* is provided, also issues a device credential.
|
|
232
|
+
* Issue a complete set of authentication tokens for a new session. The access
|
|
233
|
+
* and refresh token share a fresh family id.
|
|
185
234
|
*
|
|
186
235
|
* @param userId - The subject (user ID) to encode in the tokens
|
|
187
236
|
* @param deviceId - The device ID of the requesting device
|
|
188
|
-
* @param publicKeyThumbprint - Optional
|
|
189
|
-
*
|
|
237
|
+
* @param publicKeyThumbprint - Optional device key thumbprint; adds a device credential
|
|
238
|
+
* @param options - Authentication methods to record
|
|
190
239
|
* @returns An {@link AuthTokens} object containing the issued tokens
|
|
191
240
|
*/
|
|
192
|
-
issueTokens(userId: string, deviceId: string, publicKeyThumbprint?: string): AuthTokens;
|
|
241
|
+
issueTokens(userId: string, deviceId: string, publicKeyThumbprint?: string, options?: Omit<IssueTokenOptions, 'family'>): AuthTokens;
|
|
242
|
+
/**
|
|
243
|
+
* Issue a short-lived `mfa_pending` token after a successful first factor
|
|
244
|
+
* for a user enrolled in MFA (AUTH-10). It grants nothing by itself.
|
|
245
|
+
*
|
|
246
|
+
* @param userId - The user who passed the first factor
|
|
247
|
+
* @param deviceId - The device the session will be bound to
|
|
248
|
+
* @param amr - Methods already satisfied (for example `['pwd']`)
|
|
249
|
+
* @returns A signed JWT of type `mfa_pending`
|
|
250
|
+
*/
|
|
251
|
+
issueMfaPendingToken(userId: string, deviceId: string, amr: string[]): string;
|
|
252
|
+
/**
|
|
253
|
+
* Verify an `mfa_pending` token without consuming it, so a mistyped code can
|
|
254
|
+
* be retried with the same token (TOTP checks have their own backoff).
|
|
255
|
+
*
|
|
256
|
+
* @param token - The `mfa_pending` JWT
|
|
257
|
+
* @returns Its payload, or null when invalid, expired or already redeemed
|
|
258
|
+
*/
|
|
259
|
+
verifyMfaPendingToken(token: string): Promise<MfaPendingPayload | null>;
|
|
260
|
+
/**
|
|
261
|
+
* Redeem an `mfa_pending` token exactly once (atomic across instances).
|
|
262
|
+
*
|
|
263
|
+
* @param payload - A payload returned by {@link verifyMfaPendingToken}
|
|
264
|
+
* @returns True only for the single successful redemption
|
|
265
|
+
*/
|
|
266
|
+
redeemMfaPendingToken(payload: MfaPendingPayload): Promise<boolean>;
|
|
193
267
|
/**
|
|
194
|
-
* Validate and decode a token.
|
|
268
|
+
* Validate and decode a token's signature, expiry and claims.
|
|
195
269
|
*
|
|
196
|
-
*
|
|
197
|
-
*
|
|
198
|
-
* Returns null (rather than throwing) for invalid or expired tokens, so callers
|
|
199
|
-
* can handle authentication failure without try/catch.
|
|
270
|
+
* This does NOT consult revocation. Every request-authorization path must use
|
|
271
|
+
* {@link validateTokenWithRevocation} (or `BuiltInAuthRoutes.authenticateAccess`).
|
|
200
272
|
*
|
|
201
273
|
* @param token - The JWT string to validate
|
|
202
|
-
* @returns The decoded {@link TokenPayload}
|
|
203
|
-
* invalid, expired, or missing required claims
|
|
274
|
+
* @returns The decoded {@link TokenPayload}, or null if invalid or expired
|
|
204
275
|
*/
|
|
205
276
|
validateToken(token: string): TokenPayload | null;
|
|
206
277
|
/**
|
|
207
|
-
* Validate a token and check
|
|
208
|
-
*
|
|
209
|
-
* Like {@link validateToken}, but also checks whether the token's `jti` has been
|
|
210
|
-
* revoked or belongs to a revoked device. Requires a revocation store to be configured.
|
|
278
|
+
* Validate a token and check every revocation primitive: the token's own
|
|
279
|
+
* `jti`, its family, the device cut-off and the per-user cut-off.
|
|
211
280
|
*
|
|
212
281
|
* @param token - The JWT string to validate
|
|
213
282
|
* @returns The decoded {@link TokenPayload} if valid and not revoked, or null otherwise
|
|
214
283
|
*/
|
|
215
284
|
validateTokenWithRevocation(token: string): Promise<TokenPayload | null>;
|
|
285
|
+
/**
|
|
286
|
+
* Why an otherwise valid token is no longer accepted, or null when it is.
|
|
287
|
+
*/
|
|
288
|
+
revocationReason(payload: TokenPayload): Promise<'revoked' | 'device_revoked' | 'user_revoked' | null>;
|
|
216
289
|
/**
|
|
217
290
|
* Revoke a specific token by its JWT ID.
|
|
218
291
|
*
|
|
219
|
-
* Requires a revocation store to be configured. After revocation, the token
|
|
220
|
-
* will be rejected by {@link validateTokenWithRevocation}.
|
|
221
|
-
*
|
|
222
292
|
* @param jti - The JWT ID of the token to revoke
|
|
223
293
|
* @param expiresAt - The token's expiration time (seconds since epoch)
|
|
224
294
|
*/
|
|
225
295
|
revokeToken(jti: string, expiresAt: number): Promise<void>;
|
|
226
296
|
/**
|
|
227
|
-
* Revoke
|
|
297
|
+
* Revoke a whole refresh-token family (one sign-in and all its rotations,
|
|
298
|
+
* including the access tokens minted along the way).
|
|
228
299
|
*
|
|
229
|
-
*
|
|
230
|
-
*
|
|
300
|
+
* @param family - The family id (`fam` claim)
|
|
301
|
+
* @param expiresAt - Latest expiry of any token in the family (seconds since epoch)
|
|
302
|
+
*/
|
|
303
|
+
revokeFamily(family: string, expiresAt: number): Promise<void>;
|
|
304
|
+
/**
|
|
305
|
+
* Revoke every token issued to a device up to now. A later sign-in on the
|
|
306
|
+
* same device issues tokens that are accepted again.
|
|
231
307
|
*
|
|
232
308
|
* @param deviceId - The device ID whose tokens should be revoked
|
|
233
309
|
*/
|
|
234
310
|
revokeDeviceTokens(deviceId: string): Promise<void>;
|
|
235
311
|
/**
|
|
236
|
-
*
|
|
312
|
+
* Revoke every token issued to a user up to now (password reset or change,
|
|
313
|
+
* admin session revocation, account deletion).
|
|
314
|
+
*
|
|
315
|
+
* @param userId - The user whose credentials should be revoked
|
|
316
|
+
*/
|
|
317
|
+
revokeAllForUser(userId: string): Promise<void>;
|
|
318
|
+
/**
|
|
319
|
+
* Rotate a refresh token: atomically consume it and mint its successor pair.
|
|
320
|
+
*
|
|
321
|
+
* - Each refresh `jti` mints at most one successor family member (AUTH-6).
|
|
322
|
+
* - A concurrent duplicate on this instance gets `in_progress` (retry later).
|
|
323
|
+
* - Within the grace window, presenting a just-rotated token ONCE more returns
|
|
324
|
+
* the SAME successor pair, so a response lost on the wire does not sign the
|
|
325
|
+
* user out (NEW-AUTH-3).
|
|
326
|
+
* - Any other reuse revokes the token FAMILY, never the device (NEW-AUTH-1).
|
|
237
327
|
*
|
|
238
|
-
*
|
|
239
|
-
*
|
|
240
|
-
|
|
241
|
-
|
|
328
|
+
* @param refreshToken - The refresh token JWT string
|
|
329
|
+
* @returns The successor pair, or the reason the refresh was refused
|
|
330
|
+
*/
|
|
331
|
+
rotateRefreshToken(refreshToken: string): Promise<RefreshResult>;
|
|
332
|
+
/**
|
|
333
|
+
* Refresh an access token using a valid refresh token.
|
|
242
334
|
*
|
|
243
|
-
*
|
|
335
|
+
* Convenience wrapper over {@link rotateRefreshToken} that collapses every
|
|
336
|
+
* failure to null. HTTP handlers should use `rotateRefreshToken` so they can
|
|
337
|
+
* tell a client to retry (`in_progress`) instead of signing it out.
|
|
244
338
|
*
|
|
245
339
|
* @param refreshToken - The refresh token JWT string
|
|
246
|
-
* @returns A new access/refresh token pair, or null if the refresh
|
|
340
|
+
* @returns A new access/refresh token pair, or null if the refresh was refused
|
|
247
341
|
*/
|
|
248
342
|
refreshAccessToken(refreshToken: string): Promise<{
|
|
249
343
|
accessToken: string;
|
|
250
344
|
refreshToken: string;
|
|
251
345
|
} | null>;
|
|
346
|
+
private rotate;
|
|
347
|
+
/**
|
|
348
|
+
* Deterministically mint the successor pair of a refresh token. The jtis and
|
|
349
|
+
* issue time derive from the consumed token and its consumption time, so a
|
|
350
|
+
* grace replay re-signs byte-identical tokens without storing them.
|
|
351
|
+
*/
|
|
352
|
+
private successorTokens;
|
|
353
|
+
private deriveJti;
|
|
354
|
+
private basePayload;
|
|
355
|
+
private sign;
|
|
356
|
+
private verifySignature;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
type MaybePromise<T> = T | Promise<T>;
|
|
360
|
+
/**
|
|
361
|
+
* Verified identity of a sync session, derived on the server from a validated
|
|
362
|
+
* access token. Never contains client-supplied values.
|
|
363
|
+
*/
|
|
364
|
+
interface VerifiedSyncClaims {
|
|
365
|
+
/** Verified user id (`sub`). */
|
|
366
|
+
userId: string;
|
|
367
|
+
/** Verified device id (`dev`). */
|
|
368
|
+
deviceId: string;
|
|
369
|
+
/** The user's email from the user store. */
|
|
370
|
+
email: string;
|
|
371
|
+
/** The user's display name from the user store. */
|
|
372
|
+
name: string;
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* Server-side scope derivation for the sync auth provider (AUTH-1).
|
|
376
|
+
*
|
|
377
|
+
* The result is the session's complete grant: the client handshake can only
|
|
378
|
+
* narrow it, and a schema-scoped collection whose binding is unresolved is
|
|
379
|
+
* denied (`SCOPE_REQUIRED`), never widened.
|
|
380
|
+
*/
|
|
381
|
+
interface SyncScopeOptions {
|
|
382
|
+
/**
|
|
383
|
+
* Extra verified scope values merged over the default `{ userId }`, for
|
|
384
|
+
* example `{ orgId: await orgOf(userId) }`. Every schema-scoped collection is
|
|
385
|
+
* bound from these values. Return `undefined`/`null` for a key to deny the
|
|
386
|
+
* collections that need it.
|
|
387
|
+
*/
|
|
388
|
+
scopeValues?: (claims: VerifiedSyncClaims) => MaybePromise<Record<string, unknown>>;
|
|
389
|
+
/**
|
|
390
|
+
* Full explicit grant. When provided it replaces the default derivation:
|
|
391
|
+
* collections it omits are not visible. Use `claimScopes(values, explicit)`
|
|
392
|
+
* from `@korajs/core` to combine claim binding with explicit predicates.
|
|
393
|
+
*/
|
|
394
|
+
resolveScopes?: (claims: VerifiedSyncClaims) => MaybePromise<ScopeMap>;
|
|
395
|
+
}
|
|
396
|
+
/** Auth context returned to `@korajs/server` by the sync auth provider. */
|
|
397
|
+
interface SyncAuthContext {
|
|
398
|
+
userId: string;
|
|
399
|
+
scopes?: Record<string, Record<string, unknown>>;
|
|
400
|
+
metadata?: Record<string, unknown>;
|
|
401
|
+
/** Access-token expiry (ms since epoch); a session must not outlive it (AUTH-11). */
|
|
402
|
+
expiresAt?: number;
|
|
403
|
+
}
|
|
404
|
+
/** Structural `AuthProvider` returned by `toSyncAuthProvider()`. */
|
|
405
|
+
interface SyncAuthProvider {
|
|
406
|
+
authenticate(token: string): Promise<SyncAuthContext | null>;
|
|
407
|
+
/**
|
|
408
|
+
* Revocation feed (device revoke, sign-out, password reset or change, admin
|
|
409
|
+
* revoke). `KoraSyncServer` subscribes automatically and terminates the
|
|
410
|
+
* matching live sessions (AUTH-11).
|
|
411
|
+
*
|
|
412
|
+
* @returns An unsubscribe function
|
|
413
|
+
*/
|
|
414
|
+
onRevoke?(listener: (event: {
|
|
415
|
+
userId: string;
|
|
416
|
+
deviceId?: string;
|
|
417
|
+
}) => void | Promise<void>): () => void;
|
|
252
418
|
}
|
|
253
419
|
|
|
254
420
|
/**
|
|
@@ -302,6 +468,13 @@ interface AuthDevice {
|
|
|
302
468
|
declare class DuplicateEmailError extends KoraError {
|
|
303
469
|
constructor();
|
|
304
470
|
}
|
|
471
|
+
/**
|
|
472
|
+
* Thrown when a device id is already registered to a different user. A token's
|
|
473
|
+
* `dev` claim must always name a device owned by its `sub` (AUTH-5).
|
|
474
|
+
*/
|
|
475
|
+
declare class DeviceOwnershipError extends KoraError {
|
|
476
|
+
constructor(deviceId: string);
|
|
477
|
+
}
|
|
305
478
|
/**
|
|
306
479
|
* Generic interface for user and device persistence.
|
|
307
480
|
*
|
|
@@ -334,7 +507,11 @@ interface UserStore {
|
|
|
334
507
|
findByEmail(email: string): Promise<StoredUser | null>;
|
|
335
508
|
/** Find a user by ID. */
|
|
336
509
|
findById(id: string): Promise<StoredUser | null>;
|
|
337
|
-
/**
|
|
510
|
+
/**
|
|
511
|
+
* Register a device for a user. Idempotent for the same owner (a revoked
|
|
512
|
+
* device is re-activated). Must throw {@link DeviceOwnershipError} when the id
|
|
513
|
+
* already belongs to a different user.
|
|
514
|
+
*/
|
|
338
515
|
registerDevice(params: {
|
|
339
516
|
id: string;
|
|
340
517
|
userId: string;
|
|
@@ -359,6 +536,12 @@ interface UserStore {
|
|
|
359
536
|
delete(userId: string): Promise<void>;
|
|
360
537
|
/** Update the last-seen timestamp for a device. No-op if device does not exist. */
|
|
361
538
|
touchDevice(deviceId: string): Promise<void>;
|
|
539
|
+
/**
|
|
540
|
+
* Optional token revocation store that lives with the user data (same
|
|
541
|
+
* database). `createKoraAuthServer` uses it by default, so revocations
|
|
542
|
+
* persist and are shared exactly as far as users are.
|
|
543
|
+
*/
|
|
544
|
+
getTokenRevocationStore?(): TokenRevocationStore;
|
|
362
545
|
}
|
|
363
546
|
/**
|
|
364
547
|
* In-memory user and device store for the built-in auth provider.
|
|
@@ -387,6 +570,10 @@ declare class InMemoryUserStore implements UserStore {
|
|
|
387
570
|
private readonly devicesById;
|
|
388
571
|
/** Device IDs indexed by user ID for fast listing */
|
|
389
572
|
private readonly devicesByUserId;
|
|
573
|
+
/** Revocations live with the users, so every server sharing this store shares them. */
|
|
574
|
+
private readonly revocationStore;
|
|
575
|
+
/** The token revocation store that shares this store's lifetime. */
|
|
576
|
+
getTokenRevocationStore(): TokenRevocationStore;
|
|
390
577
|
/**
|
|
391
578
|
* Create a new user account.
|
|
392
579
|
*
|
|
@@ -421,9 +608,10 @@ declare class InMemoryUserStore implements UserStore {
|
|
|
421
608
|
/**
|
|
422
609
|
* Register a device for a user.
|
|
423
610
|
*
|
|
424
|
-
* If a device with the same ID already exists
|
|
425
|
-
* returned as-is (idempotent registration). If it was
|
|
426
|
-
* it is re-activated with updated details.
|
|
611
|
+
* If a device with the same ID already exists for the same user and is not
|
|
612
|
+
* revoked, it is returned as-is (idempotent registration). If it was
|
|
613
|
+
* previously revoked, it is re-activated with updated details. A device id
|
|
614
|
+
* owned by another user is refused.
|
|
427
615
|
*
|
|
428
616
|
* @param params - Device registration parameters
|
|
429
617
|
* @param params.id - Unique device identifier
|
|
@@ -431,6 +619,7 @@ declare class InMemoryUserStore implements UserStore {
|
|
|
431
619
|
* @param params.publicKey - Base64url-encoded device public key or thumbprint
|
|
432
620
|
* @param params.name - Human-readable device name
|
|
433
621
|
* @returns The registered device record
|
|
622
|
+
* @throws {DeviceOwnershipError} If the id is registered to another user
|
|
434
623
|
*/
|
|
435
624
|
registerDevice(params: {
|
|
436
625
|
id: string;
|
|
@@ -598,6 +787,61 @@ interface AuthRoutesConfig {
|
|
|
598
787
|
* (10 attempts per minute).
|
|
599
788
|
*/
|
|
600
789
|
rateLimiter?: RateLimiter;
|
|
790
|
+
/**
|
|
791
|
+
* Called after credentials are revoked (sign-out, device revocation, user-wide
|
|
792
|
+
* revocation). `createKoraAuthServer().bindSyncServer()` uses it to end live
|
|
793
|
+
* sync sessions (AUTH-11).
|
|
794
|
+
*/
|
|
795
|
+
onRevoke?: (event: AuthRevocationEvent) => void | Promise<void>;
|
|
796
|
+
/**
|
|
797
|
+
* Second-factor verifier (a `TotpManager` fits). When configured, users with
|
|
798
|
+
* MFA enabled get `{ mfaRequired, mfaToken }` from sign-in instead of tokens,
|
|
799
|
+
* and only `POST /auth/mfa/verify` issues their session (AUTH-10).
|
|
800
|
+
*/
|
|
801
|
+
mfa?: MfaVerifier;
|
|
802
|
+
}
|
|
803
|
+
/** Second-factor checks used at sign-in. `TotpManager` implements this. */
|
|
804
|
+
interface MfaVerifier {
|
|
805
|
+
isEnabled(userId: string): Promise<boolean>;
|
|
806
|
+
verify(userId: string, code: string): Promise<boolean>;
|
|
807
|
+
verifyRecoveryCode?(userId: string, recoveryCode: string): Promise<boolean>;
|
|
808
|
+
}
|
|
809
|
+
/** Sign-in result for a user who still has to pass the second factor. */
|
|
810
|
+
interface MfaChallenge {
|
|
811
|
+
mfaRequired: true;
|
|
812
|
+
/** Short-lived token accepted only by `POST /auth/mfa/verify`. */
|
|
813
|
+
mfaToken: string;
|
|
814
|
+
}
|
|
815
|
+
/** Successful primary authentication: a session, or an MFA challenge. */
|
|
816
|
+
type SignInResult = {
|
|
817
|
+
user: AuthUser;
|
|
818
|
+
tokens: AuthTokens;
|
|
819
|
+
} | MfaChallenge;
|
|
820
|
+
/**
|
|
821
|
+
* Describes credentials that were just revoked, so live sessions holding them
|
|
822
|
+
* (for example open sync connections) can be terminated.
|
|
823
|
+
*/
|
|
824
|
+
type AuthRevocationEvent = {
|
|
825
|
+
kind: 'device';
|
|
826
|
+
userId: string;
|
|
827
|
+
deviceId: string;
|
|
828
|
+
} | {
|
|
829
|
+
kind: 'user';
|
|
830
|
+
userId: string;
|
|
831
|
+
} | {
|
|
832
|
+
kind: 'session';
|
|
833
|
+
userId: string;
|
|
834
|
+
deviceId: string;
|
|
835
|
+
family: string | null;
|
|
836
|
+
};
|
|
837
|
+
/** Result of {@link BuiltInAuthRoutes.authenticateAccess}. */
|
|
838
|
+
interface AuthenticatedAccess {
|
|
839
|
+
/** Verified access-token payload. */
|
|
840
|
+
payload: TokenPayload;
|
|
841
|
+
/** The token's user, as stored. */
|
|
842
|
+
user: StoredUser;
|
|
843
|
+
/** The token's device record (null when the token was minted without one). */
|
|
844
|
+
device: AuthDevice | null;
|
|
601
845
|
}
|
|
602
846
|
/**
|
|
603
847
|
* Response envelope returned by all auth route handlers.
|
|
@@ -608,12 +852,19 @@ interface AuthRoutesConfig {
|
|
|
608
852
|
interface AuthRouteResponse<T> {
|
|
609
853
|
/** HTTP status code */
|
|
610
854
|
status: number;
|
|
611
|
-
/**
|
|
855
|
+
/**
|
|
856
|
+
* Either the success payload or an error message. `code` is a stable,
|
|
857
|
+
* machine-readable Kora error code; clients use it to tell a definitive
|
|
858
|
+
* rejection from a proxy or captive-portal response.
|
|
859
|
+
*/
|
|
612
860
|
body: {
|
|
613
861
|
data: T;
|
|
614
862
|
} | {
|
|
615
863
|
error: string;
|
|
864
|
+
code?: string;
|
|
616
865
|
};
|
|
866
|
+
/** Optional response headers (for example `Retry-After`). */
|
|
867
|
+
headers?: Record<string, string>;
|
|
617
868
|
}
|
|
618
869
|
/**
|
|
619
870
|
* HTTP route handlers for the built-in Kora auth provider.
|
|
@@ -654,7 +905,70 @@ declare class BuiltInAuthRoutes {
|
|
|
654
905
|
private readonly tokenManager;
|
|
655
906
|
private readonly challengeStore;
|
|
656
907
|
private readonly rateLimiter;
|
|
908
|
+
private readonly revokeListeners;
|
|
909
|
+
/** Lazily computed hash used to equalize sign-in timing for unknown emails. */
|
|
910
|
+
private dummyCredential;
|
|
911
|
+
private readonly mfa;
|
|
657
912
|
constructor(config: AuthRoutesConfig);
|
|
913
|
+
/**
|
|
914
|
+
* Finish a successful primary authentication (password, OAuth): issue a
|
|
915
|
+
* session, or an MFA challenge when the user is enrolled in MFA. No
|
|
916
|
+
* full-privilege token is ever issued to an MFA user without a fresh second
|
|
917
|
+
* factor.
|
|
918
|
+
*
|
|
919
|
+
* @param user - The authenticated user
|
|
920
|
+
* @param deviceId - The (already registered) device
|
|
921
|
+
* @param amr - Methods satisfied so far (for example `['pwd']`)
|
|
922
|
+
*/
|
|
923
|
+
completePrimaryAuthentication(user: AuthUser, deviceId: string, amr: string[]): Promise<AuthRouteResponse<SignInResult>>;
|
|
924
|
+
/**
|
|
925
|
+
* Handle the second factor (POST /auth/mfa/verify).
|
|
926
|
+
*
|
|
927
|
+
* Exchanges an `mfa_pending` token plus a TOTP code (or recovery code) for a
|
|
928
|
+
* session whose tokens carry `amr` including `otp` (or `rcv`). The pending
|
|
929
|
+
* token is redeemed once; a wrong code can be retried until it expires.
|
|
930
|
+
*/
|
|
931
|
+
handleMfaVerify(body: {
|
|
932
|
+
mfaToken?: unknown;
|
|
933
|
+
code?: unknown;
|
|
934
|
+
recoveryCode?: unknown;
|
|
935
|
+
}): Promise<AuthRouteResponse<{
|
|
936
|
+
user: AuthUser;
|
|
937
|
+
tokens: AuthTokens;
|
|
938
|
+
}>>;
|
|
939
|
+
/**
|
|
940
|
+
* Subscribe to credential revocations (sign-out, device revocation,
|
|
941
|
+
* user-wide revocation).
|
|
942
|
+
*
|
|
943
|
+
* @param listener - Called after each revocation is persisted
|
|
944
|
+
* @returns An unsubscribe function
|
|
945
|
+
*/
|
|
946
|
+
onRevoke(listener: (event: AuthRevocationEvent) => void | Promise<void>): () => void;
|
|
947
|
+
/**
|
|
948
|
+
* Authenticate an access token for any request-authorization path.
|
|
949
|
+
*
|
|
950
|
+
* The single check used by every HTTP route and by the sync provider: it
|
|
951
|
+
* verifies signature and expiry, the token's own revocation, its family, the
|
|
952
|
+
* device cut-off, the per-user cut-off, that the user still exists, and that
|
|
953
|
+
* the device record is neither revoked nor owned by someone else.
|
|
954
|
+
*
|
|
955
|
+
* @param token - Raw access token (without "Bearer ")
|
|
956
|
+
* @returns The verified access, or null when the token must be rejected
|
|
957
|
+
*/
|
|
958
|
+
authenticateAccess(token: string): Promise<AuthenticatedAccess | null>;
|
|
959
|
+
/**
|
|
960
|
+
* Revoke every credential a user holds (password reset or change, admin
|
|
961
|
+
* session revocation, account deletion) and notify revocation listeners.
|
|
962
|
+
*
|
|
963
|
+
* @param userId - The user whose sessions end now
|
|
964
|
+
*/
|
|
965
|
+
revokeAllForUser(userId: string): Promise<void>;
|
|
966
|
+
private emitRevoke;
|
|
967
|
+
/**
|
|
968
|
+
* Register the device for a successful primary authentication, refusing ids
|
|
969
|
+
* owned by another user.
|
|
970
|
+
*/
|
|
971
|
+
private registerSignInDevice;
|
|
658
972
|
/**
|
|
659
973
|
* Handle user sign-up (POST /auth/signup).
|
|
660
974
|
*
|
|
@@ -699,10 +1013,7 @@ declare class BuiltInAuthRoutes {
|
|
|
699
1013
|
password: string;
|
|
700
1014
|
deviceId?: string;
|
|
701
1015
|
devicePublicKey?: string;
|
|
702
|
-
}, clientIp?: string): Promise<AuthRouteResponse<
|
|
703
|
-
user: AuthUser;
|
|
704
|
-
tokens: AuthTokens;
|
|
705
|
-
}>>;
|
|
1016
|
+
}, clientIp?: string): Promise<AuthRouteResponse<SignInResult>>;
|
|
706
1017
|
/**
|
|
707
1018
|
* Handle token refresh (POST /auth/refresh).
|
|
708
1019
|
*
|
|
@@ -832,17 +1143,24 @@ declare class BuiltInAuthRoutes {
|
|
|
832
1143
|
* @returns A 64-character hex string (32 random bytes)
|
|
833
1144
|
*/
|
|
834
1145
|
static generateChallenge(): string;
|
|
1146
|
+
private getDummyCredential;
|
|
835
1147
|
/**
|
|
836
1148
|
* Creates a sync server auth provider compatible with `@korajs/server`.
|
|
837
1149
|
*
|
|
838
1150
|
* The returned object implements the `AuthProvider` interface from
|
|
839
1151
|
* `@korajs/server`, validating access tokens and returning an auth
|
|
840
|
-
* context containing the user ID
|
|
841
|
-
*
|
|
1152
|
+
* context containing the user ID, device metadata and a SERVER-DERIVED
|
|
1153
|
+
* scope grant. The client handshake can only narrow that grant (AUTH-1).
|
|
1154
|
+
*
|
|
1155
|
+
* By default the grant binds every schema-scoped collection from
|
|
1156
|
+
* `{ userId: <verified sub> }`. Collections scoped by any other key (for
|
|
1157
|
+
* example `orgId`) are denied until `scopeValues` or `resolveScopes`
|
|
1158
|
+
* supplies it; they are never widened to "every tenant".
|
|
842
1159
|
*
|
|
843
1160
|
* Also checks device revocation status during authentication, ensuring
|
|
844
1161
|
* that revoked devices are rejected even if their tokens haven't expired.
|
|
845
1162
|
*
|
|
1163
|
+
* @param options - Optional server-side scope derivation
|
|
846
1164
|
* @returns An object with an `authenticate` method suitable for KoraSyncServer's `auth` config
|
|
847
1165
|
*
|
|
848
1166
|
* @example
|
|
@@ -850,17 +1168,13 @@ declare class BuiltInAuthRoutes {
|
|
|
850
1168
|
* const routes = new BuiltInAuthRoutes({ userStore, tokenManager })
|
|
851
1169
|
* const syncServer = new KoraSyncServer({
|
|
852
1170
|
* store,
|
|
853
|
-
* auth: routes.toSyncAuthProvider(
|
|
1171
|
+
* auth: routes.toSyncAuthProvider({
|
|
1172
|
+
* scopeValues: async ({ userId }) => ({ orgId: await orgOf(userId) }),
|
|
1173
|
+
* }),
|
|
854
1174
|
* })
|
|
855
1175
|
* ```
|
|
856
1176
|
*/
|
|
857
|
-
toSyncAuthProvider():
|
|
858
|
-
authenticate(token: string): Promise<{
|
|
859
|
-
userId: string;
|
|
860
|
-
scopes?: Record<string, Record<string, unknown>>;
|
|
861
|
-
metadata?: Record<string, unknown>;
|
|
862
|
-
} | null>;
|
|
863
|
-
};
|
|
1177
|
+
toSyncAuthProvider(options?: SyncScopeOptions): SyncAuthProvider;
|
|
864
1178
|
}
|
|
865
1179
|
|
|
866
1180
|
/**
|
|
@@ -1021,6 +1335,20 @@ declare class InMemoryLinkedIdentityStore implements LinkedIdentityStore {
|
|
|
1021
1335
|
delete(userId: string, provider: string): Promise<void>;
|
|
1022
1336
|
}
|
|
1023
1337
|
|
|
1338
|
+
/** What an OAuth flow is for. A state minted for one purpose is useless for the other. */
|
|
1339
|
+
type OAuthFlowPurpose = 'signin' | 'link';
|
|
1340
|
+
/** Binding requested when an authorization URL is created (AUTH-3). */
|
|
1341
|
+
interface OAuthFlowBinding {
|
|
1342
|
+
/** The flow purpose. Defaults to `'signin'`. */
|
|
1343
|
+
purpose?: OAuthFlowPurpose;
|
|
1344
|
+
/** For `'link'`: the authenticated Kora user starting the flow. */
|
|
1345
|
+
userId?: string;
|
|
1346
|
+
/**
|
|
1347
|
+
* A random secret held only by the initiating client (an HttpOnly cookie on
|
|
1348
|
+
* the web, memory for native/PKCE). Only its SHA-256 is stored server-side.
|
|
1349
|
+
*/
|
|
1350
|
+
binding?: string;
|
|
1351
|
+
}
|
|
1024
1352
|
/**
|
|
1025
1353
|
* In-memory OAuth state store for development.
|
|
1026
1354
|
* Use Redis or a database in production for multi-server deployments.
|
|
@@ -1080,7 +1408,7 @@ declare class OAuthManager {
|
|
|
1080
1408
|
* Generate an authorization URL for the user to visit.
|
|
1081
1409
|
* Returns the URL and the state parameter for CSRF validation.
|
|
1082
1410
|
*/
|
|
1083
|
-
getAuthorizationUrl(providerId: string, metadata?: Record<string, unknown
|
|
1411
|
+
getAuthorizationUrl(providerId: string, metadata?: Record<string, unknown>, flow?: OAuthFlowBinding): Promise<{
|
|
1084
1412
|
url: string;
|
|
1085
1413
|
state: string;
|
|
1086
1414
|
}>;
|
|
@@ -1089,12 +1417,17 @@ declare class OAuthManager {
|
|
|
1089
1417
|
* Validates the state parameter, exchanges the code for tokens,
|
|
1090
1418
|
* and fetches user info.
|
|
1091
1419
|
*
|
|
1420
|
+
* The state is single-use and is redeemable only for the purpose it was
|
|
1421
|
+
* minted for, by the same user (for linking) and the same client binding.
|
|
1422
|
+
* A mismatch is rejected before the code is exchanged.
|
|
1423
|
+
*
|
|
1092
1424
|
* @param providerId - The OAuth provider
|
|
1093
1425
|
* @param code - The authorization code from the callback
|
|
1094
1426
|
* @param state - The state parameter from the callback
|
|
1427
|
+
* @param expected - The purpose, user and client binding of this callback
|
|
1095
1428
|
* @returns Tokens and user info from the provider
|
|
1096
1429
|
*/
|
|
1097
|
-
handleCallback(providerId: string, code: string, state: string): Promise<{
|
|
1430
|
+
handleCallback(providerId: string, code: string, state: string, expected?: OAuthFlowBinding): Promise<{
|
|
1098
1431
|
tokens: OAuthTokens;
|
|
1099
1432
|
userInfo: OAuthUserInfo;
|
|
1100
1433
|
stateMetadata?: Record<string, unknown>;
|
|
@@ -1156,9 +1489,21 @@ interface OAuthServerConfig extends Omit<OAuthManagerConfig, 'providers'> {
|
|
|
1156
1489
|
*/
|
|
1157
1490
|
allowUnlinkLastIdentity?: boolean;
|
|
1158
1491
|
}
|
|
1159
|
-
interface CreateKoraAuthServerOptions {
|
|
1492
|
+
interface CreateKoraAuthServerOptions extends SyncScopeOptions {
|
|
1160
1493
|
/** Existing user store. Defaults to InMemoryUserStore for development. */
|
|
1161
1494
|
userStore?: UserStore;
|
|
1495
|
+
/**
|
|
1496
|
+
* Token revocation store. Defaults to the user store's own revocation store
|
|
1497
|
+
* (`userStore.getTokenRevocationStore()`), so revocations persist and are shared
|
|
1498
|
+
* exactly as far as the users are.
|
|
1499
|
+
*/
|
|
1500
|
+
revocationStore?: TokenRevocationStore;
|
|
1501
|
+
/**
|
|
1502
|
+
* Allow in-memory user or revocation stores when `NODE_ENV=production`.
|
|
1503
|
+
* In-memory stores lose every account and every revocation on restart and are
|
|
1504
|
+
* not shared between instances, so production refuses them by default.
|
|
1505
|
+
*/
|
|
1506
|
+
allowInMemory?: boolean;
|
|
1162
1507
|
/** Existing token manager. Overrides `jwtSecret` and `tokenManager` options. */
|
|
1163
1508
|
tokenManager?: TokenManager;
|
|
1164
1509
|
/** JWT secret. Required in production when `tokenManager` is not provided. */
|
|
@@ -1171,6 +1516,22 @@ interface CreateKoraAuthServerOptions {
|
|
|
1171
1516
|
oauth?: OAuthServerConfig;
|
|
1172
1517
|
challengeStore?: ChallengeStore;
|
|
1173
1518
|
rateLimiter?: RateLimiter;
|
|
1519
|
+
/**
|
|
1520
|
+
* Second-factor verifier (for example a `TotpManager`). Users with MFA enabled
|
|
1521
|
+
* then sign in in two steps: `{ mfaRequired, mfaToken }`, then
|
|
1522
|
+
* `POST /auth/mfa/verify` with `{ mfaToken, code }`.
|
|
1523
|
+
*/
|
|
1524
|
+
mfa?: MfaVerifier;
|
|
1525
|
+
}
|
|
1526
|
+
/**
|
|
1527
|
+
* The part of `KoraSyncServer` the auth server needs to end live sessions.
|
|
1528
|
+
* Structural, so `@korajs/auth` does not depend on `@korajs/server`.
|
|
1529
|
+
*/
|
|
1530
|
+
interface SyncSessionTerminator {
|
|
1531
|
+
terminateSessions(filter: {
|
|
1532
|
+
userId?: string;
|
|
1533
|
+
deviceId?: string;
|
|
1534
|
+
}): unknown;
|
|
1174
1535
|
}
|
|
1175
1536
|
interface KoraAuthServer {
|
|
1176
1537
|
routes: BuiltInAuthRoutes;
|
|
@@ -1178,8 +1539,31 @@ interface KoraAuthServer {
|
|
|
1178
1539
|
tokenManager: TokenManager;
|
|
1179
1540
|
oauth?: OAuthManager;
|
|
1180
1541
|
linkedIdentityStore?: LinkedIdentityStore;
|
|
1181
|
-
auth
|
|
1542
|
+
/** Sync auth provider with server-derived scopes (pass to `KoraSyncServer`). */
|
|
1543
|
+
auth: SyncAuthProvider;
|
|
1182
1544
|
handleRequest(request: KoraAuthHttpRequest): Promise<AuthRouteResponse<unknown>>;
|
|
1545
|
+
/**
|
|
1546
|
+
* Revoke every credential a user holds (call after a password change made
|
|
1547
|
+
* outside these routes, an admin action or an account deletion).
|
|
1548
|
+
*/
|
|
1549
|
+
revokeAllForUser(userId: string): Promise<void>;
|
|
1550
|
+
/** Subscribe to credential revocations. Returns an unsubscribe function. */
|
|
1551
|
+
onRevoke(listener: (event: AuthRevocationEvent) => void | Promise<void>): () => void;
|
|
1552
|
+
/**
|
|
1553
|
+
* End live sync sessions when their credentials are revoked (AUTH-11).
|
|
1554
|
+
* Requires a sync server exposing `terminateSessions({ userId, deviceId })`.
|
|
1555
|
+
* A `KoraSyncServer` constructed with this server's `auth` provider already
|
|
1556
|
+
* follows its revocations; call this for a server built with a wrapping or
|
|
1557
|
+
* custom provider. Binding both is harmless. Returns an unsubscribe function.
|
|
1558
|
+
*/
|
|
1559
|
+
bindSyncServer(server: SyncSessionTerminator): () => void;
|
|
1560
|
+
}
|
|
1561
|
+
/**
|
|
1562
|
+
* Thrown by `createKoraAuthServer` in production when a store would silently
|
|
1563
|
+
* lose accounts or revocations on restart.
|
|
1564
|
+
*/
|
|
1565
|
+
declare class InMemoryAuthStoreError extends KoraError {
|
|
1566
|
+
constructor(which: 'userStore' | 'revocationStore');
|
|
1183
1567
|
}
|
|
1184
1568
|
/**
|
|
1185
1569
|
* Create the built-in Kora auth server with production-shaped defaults.
|
|
@@ -1189,6 +1573,85 @@ interface KoraAuthServer {
|
|
|
1189
1573
|
*/
|
|
1190
1574
|
declare function createKoraAuthServer(options?: CreateKoraAuthServerOptions): KoraAuthServer;
|
|
1191
1575
|
|
|
1576
|
+
/**
|
|
1577
|
+
* Minimal better-sqlite3 subset to avoid a hard dependency on the package.
|
|
1578
|
+
*/
|
|
1579
|
+
interface SqliteRevocationDatabase {
|
|
1580
|
+
exec(source: string): void;
|
|
1581
|
+
prepare(source: string): {
|
|
1582
|
+
run(...params: unknown[]): {
|
|
1583
|
+
changes: number;
|
|
1584
|
+
};
|
|
1585
|
+
get(...params: unknown[]): unknown;
|
|
1586
|
+
};
|
|
1587
|
+
}
|
|
1588
|
+
/**
|
|
1589
|
+
* SQLite-backed {@link TokenRevocationStore} (better-sqlite3).
|
|
1590
|
+
*
|
|
1591
|
+
* Revocations survive restarts and are shared by every process using the same
|
|
1592
|
+
* database file. `consume` is a single `INSERT ... ON CONFLICT DO NOTHING`, so it
|
|
1593
|
+
* is atomic: better-sqlite3 executes statements synchronously under SQLite's
|
|
1594
|
+
* write lock.
|
|
1595
|
+
*
|
|
1596
|
+
* @example
|
|
1597
|
+
* ```typescript
|
|
1598
|
+
* const userStore = await createSqliteUserStore({ filename: './auth.db' })
|
|
1599
|
+
* const revocationStore = userStore.getTokenRevocationStore()
|
|
1600
|
+
* ```
|
|
1601
|
+
*/
|
|
1602
|
+
declare class SqliteTokenRevocationStore implements TokenRevocationStore {
|
|
1603
|
+
private readonly db;
|
|
1604
|
+
constructor(db: SqliteRevocationDatabase);
|
|
1605
|
+
isRevoked(jti: string): Promise<boolean>;
|
|
1606
|
+
revoke(jti: string, expiresAt: number): Promise<void>;
|
|
1607
|
+
consume(jti: string, expiresAt: number): Promise<ConsumeResult>;
|
|
1608
|
+
isConsumed(jti: string): Promise<boolean>;
|
|
1609
|
+
revokeAllForDevice(deviceId: string, before?: number): Promise<void>;
|
|
1610
|
+
getDeviceRevokedBefore(deviceId: string): Promise<number | null>;
|
|
1611
|
+
revokeAllForUser(userId: string, before?: number): Promise<void>;
|
|
1612
|
+
getUserRevokedBefore(userId: string): Promise<number | null>;
|
|
1613
|
+
/** Delete revocations and consumptions whose tokens have expired. */
|
|
1614
|
+
cleanup(): Promise<void>;
|
|
1615
|
+
private setCutoff;
|
|
1616
|
+
private getCutoff;
|
|
1617
|
+
}
|
|
1618
|
+
|
|
1619
|
+
/**
|
|
1620
|
+
* Minimal postgres-js tagged-template client.
|
|
1621
|
+
*/
|
|
1622
|
+
type PostgresRevocationClient = (template: TemplateStringsArray, ...args: unknown[]) => Promise<Record<string, unknown>[]>;
|
|
1623
|
+
/**
|
|
1624
|
+
* PostgreSQL-backed {@link TokenRevocationStore} (postgres-js).
|
|
1625
|
+
*
|
|
1626
|
+
* Shared by every server instance using the database. `consume` is one
|
|
1627
|
+
* `INSERT ... ON CONFLICT DO NOTHING RETURNING`, so exactly one concurrent
|
|
1628
|
+
* caller, on any instance, consumes a refresh token (AUTH-6).
|
|
1629
|
+
*
|
|
1630
|
+
* @example
|
|
1631
|
+
* ```typescript
|
|
1632
|
+
* const userStore = await createPostgresUserStore({ connectionString })
|
|
1633
|
+
* const revocationStore = userStore.getTokenRevocationStore()
|
|
1634
|
+
* ```
|
|
1635
|
+
*/
|
|
1636
|
+
declare class PostgresTokenRevocationStore implements TokenRevocationStore {
|
|
1637
|
+
private readonly sql;
|
|
1638
|
+
private readonly ready;
|
|
1639
|
+
constructor(sql: PostgresRevocationClient);
|
|
1640
|
+
private ensureTables;
|
|
1641
|
+
isRevoked(jti: string): Promise<boolean>;
|
|
1642
|
+
revoke(jti: string, expiresAt: number): Promise<void>;
|
|
1643
|
+
consume(jti: string, expiresAt: number): Promise<ConsumeResult>;
|
|
1644
|
+
isConsumed(jti: string): Promise<boolean>;
|
|
1645
|
+
revokeAllForDevice(deviceId: string, before?: number): Promise<void>;
|
|
1646
|
+
getDeviceRevokedBefore(deviceId: string): Promise<number | null>;
|
|
1647
|
+
revokeAllForUser(userId: string, before?: number): Promise<void>;
|
|
1648
|
+
getUserRevokedBefore(userId: string): Promise<number | null>;
|
|
1649
|
+
/** Delete revocations and consumptions whose tokens have expired. */
|
|
1650
|
+
cleanup(): Promise<void>;
|
|
1651
|
+
private setCutoff;
|
|
1652
|
+
private getCutoff;
|
|
1653
|
+
}
|
|
1654
|
+
|
|
1192
1655
|
/**
|
|
1193
1656
|
* Creates a signed JWT (HS256) from a payload and secret.
|
|
1194
1657
|
*
|
|
@@ -1348,8 +1811,12 @@ interface PasswordResetStore {
|
|
|
1348
1811
|
store(token: PasswordResetToken): Promise<void>;
|
|
1349
1812
|
/** Look up a token. Returns null if not found. */
|
|
1350
1813
|
get(token: string): Promise<PasswordResetToken | null>;
|
|
1351
|
-
/**
|
|
1352
|
-
|
|
1814
|
+
/**
|
|
1815
|
+
* Mark a token as consumed. Should be atomic and return `true` only for the
|
|
1816
|
+
* call that consumed a not-yet-consumed token; `false` makes the reset fail
|
|
1817
|
+
* as already used. Returning nothing is accepted for older stores.
|
|
1818
|
+
*/
|
|
1819
|
+
consume(token: string): Promise<boolean | undefined>;
|
|
1353
1820
|
/** Count active (non-consumed, non-expired) tokens for an email. */
|
|
1354
1821
|
countActiveForEmail(email: string): Promise<number>;
|
|
1355
1822
|
/** Remove expired tokens. */
|
|
@@ -1368,11 +1835,23 @@ interface PasswordResetConfig {
|
|
|
1368
1835
|
/** Max reset requests per email in the TTL window. Defaults to 3. */
|
|
1369
1836
|
maxRequestsPerEmail?: number;
|
|
1370
1837
|
/**
|
|
1371
|
-
* Callback invoked when a reset is requested.
|
|
1372
|
-
*
|
|
1373
|
-
* If not provided, the token is returned in the route response (development mode).
|
|
1838
|
+
* Callback invoked when a reset is requested. The developer must implement
|
|
1839
|
+
* email sending: the token must only ever reach the account's mailbox.
|
|
1374
1840
|
*/
|
|
1375
1841
|
onResetRequested?: (email: string, token: string, expiresAt: number) => void | Promise<void>;
|
|
1842
|
+
/**
|
|
1843
|
+
* Development only: also return the reset token in the `requestReset`
|
|
1844
|
+
* response when no `onResetRequested` callback is configured. Ignored when
|
|
1845
|
+
* `NODE_ENV=production`; the token is never disclosed to the requester there.
|
|
1846
|
+
* @default false
|
|
1847
|
+
*/
|
|
1848
|
+
exposeTokenForDevelopment?: boolean;
|
|
1849
|
+
/**
|
|
1850
|
+
* Called after a password is reset or changed, once every earlier credential
|
|
1851
|
+
* of the user has been revoked. Use it to end live sessions (for example
|
|
1852
|
+
* `authServer.revokeAllForUser` when the user store has no revocation store).
|
|
1853
|
+
*/
|
|
1854
|
+
onPasswordChanged?: (userId: string) => void | Promise<void>;
|
|
1376
1855
|
}
|
|
1377
1856
|
declare class PasswordResetError extends KoraError {
|
|
1378
1857
|
constructor(message: string, code: string, context?: Record<string, unknown>);
|
|
@@ -1390,7 +1869,7 @@ declare class InMemoryPasswordResetStore implements PasswordResetStore {
|
|
|
1390
1869
|
private tokens;
|
|
1391
1870
|
store(token: PasswordResetToken): Promise<void>;
|
|
1392
1871
|
get(token: string): Promise<PasswordResetToken | null>;
|
|
1393
|
-
consume(token: string): Promise<
|
|
1872
|
+
consume(token: string): Promise<boolean>;
|
|
1394
1873
|
countActiveForEmail(email: string): Promise<number>;
|
|
1395
1874
|
cleanExpired(): Promise<number>;
|
|
1396
1875
|
}
|
|
@@ -1419,13 +1898,16 @@ declare class PasswordResetManager {
|
|
|
1419
1898
|
private readonly tokenTtlMs;
|
|
1420
1899
|
private readonly maxRequestsPerEmail;
|
|
1421
1900
|
private readonly onResetRequested?;
|
|
1901
|
+
private readonly exposeTokenForDevelopment;
|
|
1902
|
+
private readonly onPasswordChanged?;
|
|
1903
|
+
private warnedNoDelivery;
|
|
1422
1904
|
constructor(config: PasswordResetConfig);
|
|
1423
1905
|
/**
|
|
1424
1906
|
* Request a password reset for an email.
|
|
1425
|
-
* Always returns success to prevent email enumeration.
|
|
1907
|
+
* Always returns the same success response to prevent email enumeration.
|
|
1426
1908
|
*
|
|
1427
|
-
*
|
|
1428
|
-
*
|
|
1909
|
+
* The token is delivered only through `onResetRequested`. It is returned in
|
|
1910
|
+
* the response only with `exposeTokenForDevelopment: true` outside production.
|
|
1429
1911
|
*/
|
|
1430
1912
|
requestReset(email: string): Promise<{
|
|
1431
1913
|
status: number;
|
|
@@ -1464,6 +1946,11 @@ declare class PasswordResetManager {
|
|
|
1464
1946
|
error: string;
|
|
1465
1947
|
};
|
|
1466
1948
|
}>;
|
|
1949
|
+
/**
|
|
1950
|
+
* A password change kills every earlier credential (AUTH-7b): every access
|
|
1951
|
+
* and refresh token issued before now is rejected on every path.
|
|
1952
|
+
*/
|
|
1953
|
+
private revokeEarlierCredentials;
|
|
1467
1954
|
}
|
|
1468
1955
|
|
|
1469
1956
|
/**
|
|
@@ -1641,7 +2128,10 @@ interface SqliteDatabase$1 {
|
|
|
1641
2128
|
*/
|
|
1642
2129
|
declare class SqliteUserStore implements UserStore {
|
|
1643
2130
|
private readonly db;
|
|
2131
|
+
private revocationStore;
|
|
1644
2132
|
constructor(db: SqliteDatabase$1);
|
|
2133
|
+
/** Token revocations stored in the same SQLite database as the users. */
|
|
2134
|
+
getTokenRevocationStore(): TokenRevocationStore;
|
|
1645
2135
|
private ensureTables;
|
|
1646
2136
|
createUser(params: {
|
|
1647
2137
|
email: string;
|
|
@@ -1711,7 +2201,10 @@ interface PostgresClient$1 {
|
|
|
1711
2201
|
declare class PostgresUserStore implements UserStore {
|
|
1712
2202
|
private readonly sql;
|
|
1713
2203
|
private readonly ready;
|
|
2204
|
+
private revocationStore;
|
|
1714
2205
|
constructor(sql: PostgresClient$1);
|
|
2206
|
+
/** Token revocations stored in the same Postgres database as the users. */
|
|
2207
|
+
getTokenRevocationStore(): TokenRevocationStore;
|
|
1715
2208
|
private ensureTables;
|
|
1716
2209
|
createUser(params: {
|
|
1717
2210
|
email: string;
|
|
@@ -2044,6 +2537,22 @@ interface ExternalJwtProviderConfig {
|
|
|
2044
2537
|
* ```
|
|
2045
2538
|
*/
|
|
2046
2539
|
mapClaims?: (claims: Record<string, unknown>) => ExternalUserInfo;
|
|
2540
|
+
/**
|
|
2541
|
+
* Accepted audience(s) (`aud`). When set, tokens for any other audience are
|
|
2542
|
+
* rejected, even if they share the signing secret. Defaults to
|
|
2543
|
+
* `'authenticated'` when `providerName` is `'supabase'`.
|
|
2544
|
+
*/
|
|
2545
|
+
audience?: string | string[];
|
|
2546
|
+
/** Accepted issuer(s) (`iss`). When set, tokens from any other issuer are rejected. */
|
|
2547
|
+
issuer?: string | string[];
|
|
2548
|
+
/**
|
|
2549
|
+
* Verified scope values for the sync grant, derived from the validated claims
|
|
2550
|
+
* (AUTH-1). Merged over `{ userId }`; every schema-scoped collection is bound
|
|
2551
|
+
* from them, and a collection whose binding is missing is denied.
|
|
2552
|
+
*/
|
|
2553
|
+
scopeValues?: (claims: Record<string, unknown>) => Record<string, unknown>;
|
|
2554
|
+
/** Full explicit sync grant from the validated claims (replaces the default). */
|
|
2555
|
+
resolveScopes?: (claims: Record<string, unknown>) => ScopeMap | Promise<ScopeMap>;
|
|
2047
2556
|
}
|
|
2048
2557
|
/**
|
|
2049
2558
|
* Authentication provider adapter for external JWT issuers.
|
|
@@ -2088,6 +2597,10 @@ declare class ExternalJwtProvider implements AuthProviderAdapter {
|
|
|
2088
2597
|
private readonly jwtSecret;
|
|
2089
2598
|
private readonly customValidateToken;
|
|
2090
2599
|
private readonly mapClaims;
|
|
2600
|
+
private readonly audiences;
|
|
2601
|
+
private readonly issuers;
|
|
2602
|
+
private readonly scopeValues;
|
|
2603
|
+
private readonly resolveScopes;
|
|
2091
2604
|
constructor(config: ExternalJwtProviderConfig);
|
|
2092
2605
|
/**
|
|
2093
2606
|
* Not supported for external providers.
|
|
@@ -2200,6 +2713,8 @@ declare class ExternalJwtProvider implements AuthProviderAdapter {
|
|
|
2200
2713
|
* @returns The decoded claims object, or null if the token is invalid
|
|
2201
2714
|
*/
|
|
2202
2715
|
private extractClaims;
|
|
2716
|
+
/** Enforce configured `aud` / `iss` so tokens for another service are refused. */
|
|
2717
|
+
private audienceAndIssuerMatch;
|
|
2203
2718
|
}
|
|
2204
2719
|
|
|
2205
2720
|
/**
|
|
@@ -2506,6 +3021,12 @@ declare function verifyRegistrationResponse(params: {
|
|
|
2506
3021
|
expectedChallenge: string;
|
|
2507
3022
|
expectedOrigin: string;
|
|
2508
3023
|
expectedRpId: string;
|
|
3024
|
+
/**
|
|
3025
|
+
* Require the User Verified (UV) flag. Kora's options request
|
|
3026
|
+
* `userVerification: 'required'`, so the response must honour it.
|
|
3027
|
+
* @default true
|
|
3028
|
+
*/
|
|
3029
|
+
requireUserVerification?: boolean;
|
|
2509
3030
|
}): Promise<RegistrationVerificationResult>;
|
|
2510
3031
|
/** Options returned by generateAuthenticationOptions for the client. */
|
|
2511
3032
|
interface AuthenticationOptions {
|
|
@@ -2605,6 +3126,12 @@ declare function verifyAuthenticationResponse(params: {
|
|
|
2605
3126
|
expectedRpId: string;
|
|
2606
3127
|
publicKey: string;
|
|
2607
3128
|
previousSignCount: number;
|
|
3129
|
+
/**
|
|
3130
|
+
* Require the User Verified (UV) flag. Kora's options request
|
|
3131
|
+
* `userVerification: 'required'`, so the assertion must honour it.
|
|
3132
|
+
* @default true
|
|
3133
|
+
*/
|
|
3134
|
+
requireUserVerification?: boolean;
|
|
2608
3135
|
}): Promise<AuthenticationVerificationResult>;
|
|
2609
3136
|
|
|
2610
3137
|
/**
|
|
@@ -2808,7 +3335,11 @@ interface OrgStore {
|
|
|
2808
3335
|
/** Consume an invitation (mark as accepted). Returns the invitation details. */
|
|
2809
3336
|
consumeInvitation(token: string): Promise<OrgInvitation>;
|
|
2810
3337
|
/** Revoke a pending invitation. */
|
|
2811
|
-
|
|
3338
|
+
/**
|
|
3339
|
+
* Revoke a pending invitation of `orgId`. Must throw InvitationNotFoundError
|
|
3340
|
+
* when the invitation belongs to another org (AUTH-4).
|
|
3341
|
+
*/
|
|
3342
|
+
revokeInvitation(orgId: string, invitationId: string): Promise<void>;
|
|
2812
3343
|
/** List pending invitations for an organization. */
|
|
2813
3344
|
listPendingInvitations(orgId: string): Promise<OrgInvitation[]>;
|
|
2814
3345
|
/** List pending invitations for a specific email address. */
|
|
@@ -2847,7 +3378,7 @@ declare class InMemoryOrgStore implements OrgStore {
|
|
|
2847
3378
|
createInvitation(orgId: string, invitedBy: string, params: CreateInvitationParams): Promise<OrgInvitation>;
|
|
2848
3379
|
getInvitationByToken(token: string): Promise<OrgInvitation | null>;
|
|
2849
3380
|
consumeInvitation(token: string): Promise<OrgInvitation>;
|
|
2850
|
-
revokeInvitation(invitationId: string): Promise<void>;
|
|
3381
|
+
revokeInvitation(orgId: string, invitationId: string): Promise<void>;
|
|
2851
3382
|
listPendingInvitations(orgId: string): Promise<OrgInvitation[]>;
|
|
2852
3383
|
listInvitationsForEmail(email: string): Promise<OrgInvitation[]>;
|
|
2853
3384
|
cleanExpiredInvitations(): Promise<number>;
|
|
@@ -2873,7 +3404,26 @@ interface OrgRouteResponse<T> {
|
|
|
2873
3404
|
interface OrgRoutesConfig {
|
|
2874
3405
|
/** The organization store backing all org operations */
|
|
2875
3406
|
orgStore: OrgStore;
|
|
3407
|
+
/**
|
|
3408
|
+
* Resolves a user's email and its verification status (a `UserStore`
|
|
3409
|
+
* satisfies this). Invitations are listed and accepted only for the caller's
|
|
3410
|
+
* own VERIFIED email; without a lookup, pass the verified identity to
|
|
3411
|
+
* `acceptInvitation` / `listMyInvitations` explicitly.
|
|
3412
|
+
*/
|
|
3413
|
+
userLookup?: {
|
|
3414
|
+
findById(userId: string): Promise<{
|
|
3415
|
+
email: string;
|
|
3416
|
+
emailVerified: boolean;
|
|
3417
|
+
} | null>;
|
|
3418
|
+
};
|
|
3419
|
+
}
|
|
3420
|
+
/** A server-verified email identity used to match invitations. */
|
|
3421
|
+
interface VerifiedEmailIdentity {
|
|
3422
|
+
email: string;
|
|
3423
|
+
emailVerified: boolean;
|
|
2876
3424
|
}
|
|
3425
|
+
/** Invitation as shown to its invitee: the secret token is never included. */
|
|
3426
|
+
type InviteeInvitation = Omit<OrgInvitation, 'token'>;
|
|
2877
3427
|
/**
|
|
2878
3428
|
* Server-side route handlers for organization management.
|
|
2879
3429
|
*
|
|
@@ -2893,6 +3443,7 @@ interface OrgRoutesConfig {
|
|
|
2893
3443
|
*/
|
|
2894
3444
|
declare class OrgRoutes {
|
|
2895
3445
|
private readonly store;
|
|
3446
|
+
private readonly userLookup;
|
|
2896
3447
|
constructor(config: OrgRoutesConfig);
|
|
2897
3448
|
/**
|
|
2898
3449
|
* Create a new organization. The authenticated user becomes the owner.
|
|
@@ -2965,11 +3516,17 @@ declare class OrgRoutes {
|
|
|
2965
3516
|
role?: unknown;
|
|
2966
3517
|
}): Promise<OrgRouteResponse<OrgInvitation>>;
|
|
2967
3518
|
/**
|
|
2968
|
-
* Accept an invitation by its token. The authenticated user joins the org
|
|
3519
|
+
* Accept an invitation by its token. The authenticated user joins the org
|
|
3520
|
+
* only if the invitation was addressed to the user's own verified email.
|
|
3521
|
+
*
|
|
3522
|
+
* @param userId - The authenticated user
|
|
3523
|
+
* @param params - The invitation token
|
|
3524
|
+
* @param identity - The user's verified email identity; resolved through
|
|
3525
|
+
* `userLookup` when omitted
|
|
2969
3526
|
*/
|
|
2970
3527
|
acceptInvitation(userId: string, params: {
|
|
2971
3528
|
token?: unknown;
|
|
2972
|
-
}): Promise<OrgRouteResponse<Membership>>;
|
|
3529
|
+
}, identity?: VerifiedEmailIdentity): Promise<OrgRouteResponse<Membership>>;
|
|
2973
3530
|
/**
|
|
2974
3531
|
* Revoke a pending invitation. Requires admin or higher.
|
|
2975
3532
|
*/
|
|
@@ -2981,9 +3538,15 @@ declare class OrgRoutes {
|
|
|
2981
3538
|
*/
|
|
2982
3539
|
listPendingInvitations(userId: string, orgId: string): Promise<OrgRouteResponse<OrgInvitation[]>>;
|
|
2983
3540
|
/**
|
|
2984
|
-
* List pending invitations
|
|
3541
|
+
* List pending invitations addressed to the authenticated user's own
|
|
3542
|
+
* verified email. The email is resolved server-side (never taken from the
|
|
3543
|
+
* request) and invitation tokens are never returned.
|
|
3544
|
+
*
|
|
3545
|
+
* @param userId - The authenticated user
|
|
3546
|
+
* @param identity - The user's verified identity; resolved through `userLookup` when omitted
|
|
2985
3547
|
*/
|
|
2986
|
-
listMyInvitations(
|
|
3548
|
+
listMyInvitations(userId: string, identity?: VerifiedEmailIdentity): Promise<OrgRouteResponse<InviteeInvitation[]>>;
|
|
3549
|
+
private resolveIdentity;
|
|
2987
3550
|
/**
|
|
2988
3551
|
* Check if the caller has the required role in the org.
|
|
2989
3552
|
* Returns an error response if not authorized, or null if authorized.
|
|
@@ -3609,6 +4172,10 @@ interface TotpSecret {
|
|
|
3609
4172
|
* until the first code is consumed.
|
|
3610
4173
|
*/
|
|
3611
4174
|
lastUsedTimeStep?: number;
|
|
4175
|
+
/** Consecutive failed code checks (TOTP or recovery). Reset on success. */
|
|
4176
|
+
failedAttempts?: number;
|
|
4177
|
+
/** While set and in the future, every code check fails without being evaluated. */
|
|
4178
|
+
lockedUntil?: number;
|
|
3612
4179
|
}
|
|
3613
4180
|
/**
|
|
3614
4181
|
* Setup result returned when enabling TOTP MFA.
|
|
@@ -3635,6 +4202,14 @@ interface TotpStore {
|
|
|
3635
4202
|
declare class TotpError extends KoraError {
|
|
3636
4203
|
constructor(message: string, code: string, context?: Record<string, unknown>);
|
|
3637
4204
|
}
|
|
4205
|
+
/**
|
|
4206
|
+
* Thrown by `disable` / `regenerateRecoveryCodes` while code checks are locked
|
|
4207
|
+
* after repeated failures (AUTH-10). `verify` returns false instead.
|
|
4208
|
+
*/
|
|
4209
|
+
declare class TotpLockedError extends TotpError {
|
|
4210
|
+
readonly lockedUntil: number;
|
|
4211
|
+
constructor(lockedUntil: number);
|
|
4212
|
+
}
|
|
3638
4213
|
declare class TotpInvalidCodeError extends TotpError {
|
|
3639
4214
|
constructor();
|
|
3640
4215
|
}
|
|
@@ -3659,31 +4234,6 @@ declare class InMemoryTotpStore implements TotpStore {
|
|
|
3659
4234
|
getByUserId(userId: string): Promise<TotpSecret | null>;
|
|
3660
4235
|
delete(userId: string): Promise<void>;
|
|
3661
4236
|
}
|
|
3662
|
-
/**
|
|
3663
|
-
* Manages TOTP-based Multi-Factor Authentication.
|
|
3664
|
-
*
|
|
3665
|
-
* Implements RFC 6238 (TOTP) and RFC 4226 (HOTP) with Web Crypto API.
|
|
3666
|
-
* Compatible with Google Authenticator, Authy, 1Password, and other
|
|
3667
|
-
* TOTP-compatible authenticator apps.
|
|
3668
|
-
*
|
|
3669
|
-
* @example
|
|
3670
|
-
* ```typescript
|
|
3671
|
-
* const totp = new TotpManager({
|
|
3672
|
-
* issuer: 'MyApp',
|
|
3673
|
-
* store: new InMemoryTotpStore(),
|
|
3674
|
-
* })
|
|
3675
|
-
*
|
|
3676
|
-
* // Step 1: Enable MFA (returns QR code URI and recovery codes)
|
|
3677
|
-
* const setup = await totp.enable('user-123', 'alice@example.com')
|
|
3678
|
-
* // Show setup.uri as QR code, show setup.recoveryCodes once
|
|
3679
|
-
*
|
|
3680
|
-
* // Step 2: Verify setup with a code from authenticator app
|
|
3681
|
-
* await totp.verifySetup('user-123', '123456')
|
|
3682
|
-
*
|
|
3683
|
-
* // Step 3: On login, verify TOTP code
|
|
3684
|
-
* const valid = await totp.verify('user-123', '654321')
|
|
3685
|
-
* ```
|
|
3686
|
-
*/
|
|
3687
4237
|
declare class TotpManager {
|
|
3688
4238
|
private readonly store;
|
|
3689
4239
|
private readonly issuer;
|
|
@@ -3739,7 +4289,19 @@ declare class TotpManager {
|
|
|
3739
4289
|
* Get the number of remaining recovery codes for a user.
|
|
3740
4290
|
*/
|
|
3741
4291
|
remainingRecoveryCodes(userId: string): Promise<number>;
|
|
3742
|
-
private
|
|
4292
|
+
private isLocked;
|
|
4293
|
+
private assertNotLocked;
|
|
4294
|
+
/**
|
|
4295
|
+
* Count a failed code check. After FREE_FAILURES consecutive failures, code
|
|
4296
|
+
* checks lock for an exponentially growing period (30s, 60s, ... up to 15
|
|
4297
|
+
* minutes), which bounds online guessing of a 6-digit code (AUTH-10) without
|
|
4298
|
+
* a permanent lockout an attacker could trigger at will.
|
|
4299
|
+
*/
|
|
4300
|
+
private recordFailure;
|
|
4301
|
+
/** consumeCode plus failure accounting. */
|
|
4302
|
+
private checkTotp;
|
|
4303
|
+
/** A matching time-step that has not been consumed yet, or null. */
|
|
4304
|
+
private matchFreshTimeStep;
|
|
3743
4305
|
/**
|
|
3744
4306
|
* Find the TOTP time-step counter (within the acceptance window) whose
|
|
3745
4307
|
* generated code matches `code`, or null if none matches. Comparison is
|
|
@@ -3922,6 +4484,19 @@ interface AdminApiConfig {
|
|
|
3922
4484
|
sessionStore?: SessionStore;
|
|
3923
4485
|
/** Audit logger (optional) */
|
|
3924
4486
|
auditLogger?: AuditLogger;
|
|
4487
|
+
/**
|
|
4488
|
+
* Authorization check for the acting admin. When set, every method that
|
|
4489
|
+
* takes an `adminId` throws {@link AdminUnauthorizedError} unless it returns
|
|
4490
|
+
* true. Without it, the app must gate access to AdminApi itself.
|
|
4491
|
+
*/
|
|
4492
|
+
isAdmin?: (adminId: string) => boolean | Promise<boolean>;
|
|
4493
|
+
/**
|
|
4494
|
+
* Revoke every credential of a user (defaults to the user store's token
|
|
4495
|
+
* revocation store). Called by `revokeUserSessions`, `suspend`-style flows
|
|
4496
|
+
* and `deleteUser`, so JWTs and refresh tokens die with the session rows.
|
|
4497
|
+
* Pass `authServer.revokeAllForUser` to also end live sync sessions.
|
|
4498
|
+
*/
|
|
4499
|
+
revokeAllForUser?: (userId: string) => Promise<void>;
|
|
3925
4500
|
}
|
|
3926
4501
|
/**
|
|
3927
4502
|
* Paginated result set.
|
|
@@ -3990,7 +4565,13 @@ declare class AdminApi {
|
|
|
3990
4565
|
private readonly userStore;
|
|
3991
4566
|
private readonly sessionStore;
|
|
3992
4567
|
private readonly auditLogger;
|
|
4568
|
+
private readonly isAdmin;
|
|
4569
|
+
private readonly revokeAllForUserFn;
|
|
3993
4570
|
constructor(config: AdminApiConfig);
|
|
4571
|
+
/** Throws AdminUnauthorizedError unless the configured `isAdmin` accepts the actor. */
|
|
4572
|
+
private authorize;
|
|
4573
|
+
/** Kill every JWT and refresh token of a user, not just session rows (AUTH-7b/AUTH-14). */
|
|
4574
|
+
private revokeCredentials;
|
|
3994
4575
|
/**
|
|
3995
4576
|
* Get a user by ID with full details.
|
|
3996
4577
|
*/
|
|
@@ -4150,9 +4731,24 @@ declare class InMemoryWebhookStore implements WebhookStore {
|
|
|
4150
4731
|
declare class WebhookManager {
|
|
4151
4732
|
private readonly store;
|
|
4152
4733
|
private readonly fetchFn;
|
|
4734
|
+
private readonly allowPrivateTargets;
|
|
4735
|
+
private readonly resolveHost;
|
|
4736
|
+
/**
|
|
4737
|
+
* @param config.store - Endpoint and delivery storage
|
|
4738
|
+
* @param config.fetch - Custom fetch. A custom fetch (for example through an
|
|
4739
|
+
* egress proxy) owns its own network policy, so Kora skips its delivery-time
|
|
4740
|
+
* DNS check unless `resolveHost` is also given.
|
|
4741
|
+
* @param config.allowPrivateTargets - Development only: allow http:// and
|
|
4742
|
+
* loopback/private/link-local targets. Default false.
|
|
4743
|
+
* @param config.resolveHost - Resolve a hostname to addresses at delivery time
|
|
4744
|
+
* (defaults to node:dns when no custom fetch is given), so a public name that
|
|
4745
|
+
* points at a private address is refused (SSRF, DNS rebinding).
|
|
4746
|
+
*/
|
|
4153
4747
|
constructor(config: {
|
|
4154
4748
|
store: WebhookStore;
|
|
4155
4749
|
fetch?: typeof globalThis.fetch;
|
|
4750
|
+
allowPrivateTargets?: boolean;
|
|
4751
|
+
resolveHost?: (host: string) => Promise<string[]>;
|
|
4156
4752
|
});
|
|
4157
4753
|
/**
|
|
4158
4754
|
* Register a new webhook endpoint.
|
|
@@ -4192,11 +4788,26 @@ declare class WebhookManager {
|
|
|
4192
4788
|
*/
|
|
4193
4789
|
dispatch(event: WebhookEvent, data: Record<string, unknown>): Promise<void>;
|
|
4194
4790
|
private deliverToEndpoint;
|
|
4791
|
+
private assertDeliverable;
|
|
4792
|
+
}
|
|
4793
|
+
/** Thrown when a webhook URL points at a non-public target. */
|
|
4794
|
+
declare class WebhookTargetError extends WebhookError {
|
|
4795
|
+
constructor(url: string);
|
|
4195
4796
|
}
|
|
4196
4797
|
/**
|
|
4197
|
-
* Verify a webhook payload signature.
|
|
4198
|
-
*
|
|
4798
|
+
* Verify a webhook payload signature (`X-Webhook-Signature: t=<unix>,v1=<hex>`).
|
|
4799
|
+
*
|
|
4800
|
+
* The HMAC covers `${t}.${payload}`, and a delivery older (or newer) than
|
|
4801
|
+
* `toleranceSeconds` is rejected, so a captured delivery cannot be replayed.
|
|
4802
|
+
*
|
|
4803
|
+
* @param payload - The raw request body
|
|
4804
|
+
* @param signature - The `X-Webhook-Signature` header
|
|
4805
|
+
* @param secret - The endpoint secret
|
|
4806
|
+
* @param options - `toleranceSeconds` (default 300)
|
|
4807
|
+
* @returns True when the signature is valid and fresh
|
|
4199
4808
|
*/
|
|
4200
|
-
declare function verifyWebhookSignature(payload: string, signature: string, secret: string
|
|
4809
|
+
declare function verifyWebhookSignature(payload: string, signature: string, secret: string, options?: {
|
|
4810
|
+
toleranceSeconds?: number;
|
|
4811
|
+
}): Promise<boolean>;
|
|
4201
4812
|
|
|
4202
|
-
export { AdminApi, type AdminApiConfig, AdminApiError, AdminUnauthorizedError, AdminUserNotFoundError, type AdminUserUpdate, type AuditAction, type AuditEntry, AuditLogError, type AuditLogQuery, type AuditLogStore, AuditLogger, type AuthDevice, type AuthProviderAdapter, AuthProviderError, type AuthRouteResponse, type AuthRoutesConfig, type AuthUser, type AuthenticationOptions, type AuthenticationVerificationResult, BUILT_IN_ROLES, BuiltInAuthRoutes, BuiltInProvider, CannotRemoveOwnerError, type ChallengeStore, CircularInheritanceError, type ClerkAdapterConfig, type CollectionScopeResolver, type CreateInvitationParams, type CreateKoraAuthServerOptions, type CreateOrgParams, type CreateSessionParams, DuplicateEmailError, DuplicateLinkedIdentityError, type EmailVerificationConfig, EmailVerificationError, EmailVerificationManager, type EmailVerificationStore, type EmailVerificationToken, ExternalAuthOperationNotSupportedError, ExternalJwtProvider, type ExternalJwtProviderConfig, ExternalTokenValidationError, type ExternalUserInfo, INVITATION_STATUSES, InMemoryAuditLogStore, InMemoryChallengeStore, InMemoryEmailVerificationStore, InMemoryLinkedIdentityStore, InMemoryOAuthStateStore, InMemoryOrgStore, InMemoryPasswordResetStore, InMemoryRateLimiter, InMemorySessionStore, InMemoryTokenRevocationStore, InMemoryTotpStore, InMemoryUserStore, InMemoryWebhookStore, InsufficientRoleError, InvalidPermissionError, InvitationExpiredError, InvitationNotFoundError, type InvitationStatus, type KoraAuthHttpRequest, type KoraAuthServer, type LinkedIdentity, type LinkedIdentityStore, MemberAlreadyExistsError, type Membership, MembershipNotFoundError, OAuthCodeExchangeError, OAuthError, OAuthManager, type OAuthManagerConfig, type OAuthProviderConfig, OAuthProviderNotFoundError, type OAuthServerConfig, type OAuthState, OAuthStateMismatchError, type OAuthStateStore, type OAuthTokens, type OAuthUserInfo, OAuthUserInfoError, ORG_ROLES, OrgError, type OrgInvitation, OrgNotFoundError, type OrgRole, type OrgRouteResponse, OrgRoutes, type OrgRoutesConfig, OrgScopeResolver, OrgSlugTakenError, type OrgStore, type Organization, type PaginatedResult, PasskeyVerificationError, type PasswordResetConfig, PasswordResetError, PasswordResetManager, type PasswordResetStore, type PasswordResetToken, type Permission, PostgresLinkedIdentityStore, PostgresOAuthStateStore, PostgresUserStore, ROLE_HIERARCHY, type RateLimiter, type RbacConfig, RbacEngine, RbacError, type RegistrationOptions, type RegistrationVerificationResult, ResetRateLimitedError, ResetTokenExpiredError, ResetTokenNotFoundError, type RoleDefinition, RoleNotFoundError, type ScopeContext, type ScopeFilter, type Session, SessionError, SessionExpiredError, SessionLimitExceededError, SessionManager, type SessionManagerConfig, SessionMfaRequiredError, SessionNotFoundError, type SessionStore, type SignInParams, type SignUpParams, SqliteLinkedIdentityStore, SqliteOAuthStateStore, SqliteUserStore, type StoredUser, type SupabaseAdapterConfig, type SyncScopes, TokenManager, type TokenManagerConfig, type TokenRevocationStore, TotpAlreadyEnabledError, type TotpConfig, TotpError, TotpInvalidCodeError, TotpManager, TotpNotEnabledError, TotpNotVerifiedError, TotpRecoveryExhaustedError, type TotpSecret, type TotpSetupResult, type TotpStore, type UpdateOrgParams, type UserListQuery, type UserStore, VerificationTokenExpiredError, VerificationTokenNotFoundError, type WebhookDelivery, type WebhookEndpoint, WebhookEndpointNotFoundError, WebhookError, type WebhookEvent, WebhookManager, type WebhookPayload, type WebhookStore, base32Decode, base32Encode, createClerkAdapter, createKoraAuthServer, createPostgresLinkedIdentityStore, createPostgresOAuthStateStore, createPostgresOAuthStores, createPostgresUserStore, createSqliteLinkedIdentityStore, createSqliteOAuthStateStore, createSqliteOAuthStores, createSqliteUserStore, createSupabaseAdapter, decodeJwt, defineRoles, encodeJwt, generateAuthenticationOptions, generateRegistrationOptions, githubProvider, googleProvider, hasRoleLevel, hashPassword, isExpired, microsoftProvider, parsePermission, permissionCovers, verifyAuthenticationResponse, verifyJwt, verifyPassword, verifyRegistrationResponse, verifyWebhookSignature };
|
|
4813
|
+
export { AdminApi, type AdminApiConfig, AdminApiError, AdminUnauthorizedError, AdminUserNotFoundError, type AdminUserUpdate, type AuditAction, type AuditEntry, AuditLogError, type AuditLogQuery, type AuditLogStore, AuditLogger, type AuthDevice, type AuthProviderAdapter, AuthProviderError, type AuthRevocationEvent, type AuthRouteResponse, type AuthRoutesConfig, type AuthUser, type AuthenticatedAccess, type AuthenticationOptions, type AuthenticationVerificationResult, BUILT_IN_ROLES, BuiltInAuthRoutes, BuiltInProvider, CannotRemoveOwnerError, type ChallengeStore, CircularInheritanceError, type ClerkAdapterConfig, type CollectionScopeResolver, type ConsumeResult, type CreateInvitationParams, type CreateKoraAuthServerOptions, type CreateOrgParams, type CreateSessionParams, DEFAULT_REFRESH_REUSE_GRACE_MS, DeviceOwnershipError, DuplicateEmailError, DuplicateLinkedIdentityError, type EmailVerificationConfig, EmailVerificationError, EmailVerificationManager, type EmailVerificationStore, type EmailVerificationToken, ExternalAuthOperationNotSupportedError, ExternalJwtProvider, type ExternalJwtProviderConfig, ExternalTokenValidationError, type ExternalUserInfo, INVITATION_STATUSES, InMemoryAuditLogStore, InMemoryAuthStoreError, InMemoryChallengeStore, InMemoryEmailVerificationStore, InMemoryLinkedIdentityStore, InMemoryOAuthStateStore, InMemoryOrgStore, InMemoryPasswordResetStore, InMemoryRateLimiter, InMemorySessionStore, InMemoryTokenRevocationStore, InMemoryTotpStore, InMemoryUserStore, InMemoryWebhookStore, InsufficientRoleError, InvalidPermissionError, InvitationExpiredError, InvitationNotFoundError, type InvitationStatus, type IssueTokenOptions, type KoraAuthHttpRequest, type KoraAuthServer, type LinkedIdentity, type LinkedIdentityStore, MemberAlreadyExistsError, type Membership, MembershipNotFoundError, type MfaChallenge, type MfaPendingPayload, type MfaVerifier, OAuthCodeExchangeError, OAuthError, OAuthManager, type OAuthManagerConfig, type OAuthProviderConfig, OAuthProviderNotFoundError, type OAuthServerConfig, type OAuthState, OAuthStateMismatchError, type OAuthStateStore, type OAuthTokens, type OAuthUserInfo, OAuthUserInfoError, ORG_ROLES, OrgError, type OrgInvitation, OrgNotFoundError, type OrgRole, type OrgRouteResponse, OrgRoutes, type OrgRoutesConfig, OrgScopeResolver, OrgSlugTakenError, type OrgStore, type Organization, type PaginatedResult, PasskeyVerificationError, type PasswordResetConfig, PasswordResetError, PasswordResetManager, type PasswordResetStore, type PasswordResetToken, type Permission, PostgresLinkedIdentityStore, PostgresOAuthStateStore, type PostgresRevocationClient, PostgresTokenRevocationStore, PostgresUserStore, ROLE_HIERARCHY, type RateLimiter, type RbacConfig, RbacEngine, RbacError, type RefreshFailureReason, type RefreshResult, type RegistrationOptions, type RegistrationVerificationResult, ResetRateLimitedError, ResetTokenExpiredError, ResetTokenNotFoundError, type RoleDefinition, RoleNotFoundError, type ScopeContext, type ScopeFilter, type Session, SessionError, SessionExpiredError, SessionLimitExceededError, SessionManager, type SessionManagerConfig, SessionMfaRequiredError, SessionNotFoundError, type SessionStore, type SignInParams, type SignInResult, type SignUpParams, SqliteLinkedIdentityStore, SqliteOAuthStateStore, type SqliteRevocationDatabase, SqliteTokenRevocationStore, SqliteUserStore, type StoredUser, type SupabaseAdapterConfig, type SyncAuthContext, type SyncAuthProvider, type SyncScopeOptions, type SyncScopes, type SyncSessionTerminator, TokenManager, type TokenManagerConfig, type TokenRevocationStore, TotpAlreadyEnabledError, type TotpConfig, TotpError, TotpInvalidCodeError, TotpLockedError, TotpManager, TotpNotEnabledError, TotpNotVerifiedError, TotpRecoveryExhaustedError, type TotpSecret, type TotpSetupResult, type TotpStore, type UpdateOrgParams, type UserListQuery, type UserStore, VerificationTokenExpiredError, VerificationTokenNotFoundError, type VerifiedSyncClaims, type WebhookDelivery, type WebhookEndpoint, WebhookEndpointNotFoundError, WebhookError, type WebhookEvent, WebhookManager, type WebhookPayload, type WebhookStore, WebhookTargetError, base32Decode, base32Encode, createClerkAdapter, createKoraAuthServer, createPostgresLinkedIdentityStore, createPostgresOAuthStateStore, createPostgresOAuthStores, createPostgresUserStore, createSqliteLinkedIdentityStore, createSqliteOAuthStateStore, createSqliteOAuthStores, createSqliteUserStore, createSupabaseAdapter, decodeJwt, defineRoles, encodeJwt, generateAuthenticationOptions, generateRegistrationOptions, githubProvider, googleProvider, hasRoleLevel, hashPassword, isExpired, microsoftProvider, parsePermission, permissionCovers, verifyAuthenticationResponse, verifyJwt, verifyPassword, verifyRegistrationResponse, verifyWebhookSignature };
|