@korajs/auth 0.3.3 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +81 -28
- package/dist/{chunk-FSU4SK32.js → chunk-IO2MCCG2.js} +35 -40
- package/dist/chunk-IO2MCCG2.js.map +1 -0
- package/dist/{chunk-HOZXDR6Y.js → chunk-L7GXPS74.js} +3 -9
- package/dist/chunk-L7GXPS74.js.map +1 -0
- package/dist/index.cjs +978 -606
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +77 -118
- package/dist/index.d.ts +77 -118
- package/dist/index.js +686 -313
- package/dist/index.js.map +1 -1
- package/dist/org-client-q2u55qod.d.cts +665 -0
- package/dist/org-client-q2u55qod.d.ts +665 -0
- package/dist/{password-hash-HDH6VQCQ.js → password-hash-QRBG6BNJ.js} +2 -2
- package/dist/react.cjs +79 -0
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +17 -1
- package/dist/react.d.ts +17 -1
- package/dist/react.js +79 -0
- package/dist/react.js.map +1 -1
- package/dist/server.cjs +2524 -1672
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +415 -224
- package/dist/server.d.ts +415 -224
- package/dist/server.js +2325 -1484
- package/dist/server.js.map +1 -1
- package/package.json +5 -5
- package/dist/chunk-FSU4SK32.js.map +0 -1
- package/dist/chunk-HOZXDR6Y.js.map +0 -1
- package/dist/org-client-BVTLKcIk.d.cts +0 -355
- package/dist/org-client-BVTLKcIk.d.ts +0 -355
- /package/dist/{password-hash-HDH6VQCQ.js.map → password-hash-QRBG6BNJ.js.map} +0 -0
|
@@ -0,0 +1,665 @@
|
|
|
1
|
+
import { KoraError } from '@korajs/core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Thrown when a device key store operation fails.
|
|
5
|
+
* Provides context about the operation and the underlying cause.
|
|
6
|
+
*/
|
|
7
|
+
declare class DeviceKeyStoreError extends KoraError {
|
|
8
|
+
constructor(message: string, context?: Record<string, unknown>);
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Persistent storage interface for ECDSA P-256 device key pairs.
|
|
12
|
+
*
|
|
13
|
+
* Device key pairs are used for proof-of-possession authentication:
|
|
14
|
+
* the private key never leaves the device, and the public key is
|
|
15
|
+
* registered with the server. This store persists key pairs across
|
|
16
|
+
* page reloads and app restarts.
|
|
17
|
+
*
|
|
18
|
+
* In the browser, CryptoKey objects are structured-cloneable, so they
|
|
19
|
+
* can be stored directly in IndexedDB without serialization. In Node.js
|
|
20
|
+
* or test environments, an in-memory implementation is used instead.
|
|
21
|
+
*/
|
|
22
|
+
interface DeviceKeyStore {
|
|
23
|
+
/**
|
|
24
|
+
* Persist a key pair for the given device.
|
|
25
|
+
*
|
|
26
|
+
* Overwrites any previously stored key pair for the same device ID.
|
|
27
|
+
*
|
|
28
|
+
* @param deviceId - The unique device identifier (typically a UUID v7)
|
|
29
|
+
* @param keyPair - The ECDSA P-256 CryptoKeyPair to store
|
|
30
|
+
* @throws {DeviceKeyStoreError} If the storage operation fails
|
|
31
|
+
*/
|
|
32
|
+
saveKeyPair(deviceId: string, keyPair: CryptoKeyPair): Promise<void>;
|
|
33
|
+
/**
|
|
34
|
+
* Load a previously stored key pair for the given device.
|
|
35
|
+
*
|
|
36
|
+
* @param deviceId - The unique device identifier
|
|
37
|
+
* @returns The stored CryptoKeyPair, or null if no key pair exists for the device
|
|
38
|
+
* @throws {DeviceKeyStoreError} If the storage operation fails
|
|
39
|
+
*/
|
|
40
|
+
loadKeyPair(deviceId: string): Promise<CryptoKeyPair | null>;
|
|
41
|
+
/**
|
|
42
|
+
* Delete a stored key pair for the given device.
|
|
43
|
+
*
|
|
44
|
+
* No-op if no key pair exists for the device ID.
|
|
45
|
+
*
|
|
46
|
+
* @param deviceId - The unique device identifier
|
|
47
|
+
* @throws {DeviceKeyStoreError} If the storage operation fails
|
|
48
|
+
*/
|
|
49
|
+
deleteKeyPair(deviceId: string): Promise<void>;
|
|
50
|
+
/**
|
|
51
|
+
* Check whether a key pair exists for the given device.
|
|
52
|
+
*
|
|
53
|
+
* @param deviceId - The unique device identifier
|
|
54
|
+
* @returns True if a key pair is stored for the device, false otherwise
|
|
55
|
+
* @throws {DeviceKeyStoreError} If the storage operation fails
|
|
56
|
+
*/
|
|
57
|
+
hasKeyPair(deviceId: string): Promise<boolean>;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Browser-based device key store backed by IndexedDB.
|
|
61
|
+
*
|
|
62
|
+
* CryptoKey objects are structured-cloneable, so they can be stored
|
|
63
|
+
* directly in IndexedDB without needing to export/import them as JWK.
|
|
64
|
+
* This preserves the non-extractable flag on private keys, ensuring
|
|
65
|
+
* they cannot be read even from storage.
|
|
66
|
+
*
|
|
67
|
+
* The database uses a single object store (`keypairs`) with the device ID
|
|
68
|
+
* as the key and the full CryptoKeyPair as the value.
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```typescript
|
|
72
|
+
* const store = new IndexedDBDeviceKeyStore()
|
|
73
|
+
* const keyPair = await generateDeviceKeyPair()
|
|
74
|
+
* await store.saveKeyPair('device-123', keyPair)
|
|
75
|
+
*
|
|
76
|
+
* const loaded = await store.loadKeyPair('device-123')
|
|
77
|
+
* // loaded.privateKey is still non-extractable
|
|
78
|
+
* ```
|
|
79
|
+
*/
|
|
80
|
+
declare class IndexedDBDeviceKeyStore implements DeviceKeyStore {
|
|
81
|
+
private dbPromise;
|
|
82
|
+
/**
|
|
83
|
+
* Opens (or creates) the IndexedDB database.
|
|
84
|
+
*
|
|
85
|
+
* The database connection is lazily initialized on first use and
|
|
86
|
+
* reused for subsequent operations. If the database does not exist,
|
|
87
|
+
* it is created with the `keypairs` object store.
|
|
88
|
+
*/
|
|
89
|
+
private openDatabase;
|
|
90
|
+
/** @inheritdoc */
|
|
91
|
+
saveKeyPair(deviceId: string, keyPair: CryptoKeyPair): Promise<void>;
|
|
92
|
+
/** @inheritdoc */
|
|
93
|
+
loadKeyPair(deviceId: string): Promise<CryptoKeyPair | null>;
|
|
94
|
+
/** @inheritdoc */
|
|
95
|
+
deleteKeyPair(deviceId: string): Promise<void>;
|
|
96
|
+
/** @inheritdoc */
|
|
97
|
+
hasKeyPair(deviceId: string): Promise<boolean>;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* In-memory device key store for Node.js and testing environments.
|
|
101
|
+
*
|
|
102
|
+
* Key pairs are stored in a plain Map and do not survive process restarts.
|
|
103
|
+
* This is suitable for server-side rendering, Node.js scripts, and unit tests
|
|
104
|
+
* where IndexedDB is not available.
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* ```typescript
|
|
108
|
+
* const store = new InMemoryDeviceKeyStore()
|
|
109
|
+
* const keyPair = await generateDeviceKeyPair()
|
|
110
|
+
* await store.saveKeyPair('device-123', keyPair)
|
|
111
|
+
*
|
|
112
|
+
* const loaded = await store.loadKeyPair('device-123')
|
|
113
|
+
* ```
|
|
114
|
+
*/
|
|
115
|
+
declare class InMemoryDeviceKeyStore implements DeviceKeyStore {
|
|
116
|
+
private readonly store;
|
|
117
|
+
/** @inheritdoc */
|
|
118
|
+
saveKeyPair(deviceId: string, keyPair: CryptoKeyPair): Promise<void>;
|
|
119
|
+
/** @inheritdoc */
|
|
120
|
+
loadKeyPair(deviceId: string): Promise<CryptoKeyPair | null>;
|
|
121
|
+
/** @inheritdoc */
|
|
122
|
+
deleteKeyPair(deviceId: string): Promise<void>;
|
|
123
|
+
/** @inheritdoc */
|
|
124
|
+
hasKeyPair(deviceId: string): Promise<boolean>;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Creates a DeviceKeyStore appropriate for the current environment.
|
|
128
|
+
*
|
|
129
|
+
* In browsers where IndexedDB is available, returns an {@link IndexedDBDeviceKeyStore}
|
|
130
|
+
* that persists CryptoKeyPair objects as structured clones. In Node.js, SSR,
|
|
131
|
+
* or environments without IndexedDB, returns an {@link InMemoryDeviceKeyStore}.
|
|
132
|
+
*
|
|
133
|
+
* @returns A DeviceKeyStore instance for the current environment
|
|
134
|
+
*
|
|
135
|
+
* @example
|
|
136
|
+
* ```typescript
|
|
137
|
+
* const store = createDeviceKeyStore()
|
|
138
|
+
* const keyPair = await generateDeviceKeyPair()
|
|
139
|
+
* await store.saveKeyPair(deviceId, keyPair)
|
|
140
|
+
* ```
|
|
141
|
+
*/
|
|
142
|
+
declare function createDeviceKeyStore(): DeviceKeyStore;
|
|
143
|
+
|
|
144
|
+
type MaybePromise$1<T> = T | Promise<T>;
|
|
145
|
+
/**
|
|
146
|
+
* Minimal key-value credential storage interface used by Kora auth adapters.
|
|
147
|
+
*
|
|
148
|
+
* This intentionally matches the shape of secure stores across runtimes:
|
|
149
|
+
* browser Storage, Tauri secure storage plugins, Expo SecureStore, iOS Keychain,
|
|
150
|
+
* Android Keystore wrappers, and encrypted desktop stores.
|
|
151
|
+
*/
|
|
152
|
+
interface AuthKeyValueStorage {
|
|
153
|
+
getItem(key: string): MaybePromise$1<string | null>;
|
|
154
|
+
setItem(key: string, value: string): MaybePromise$1<void>;
|
|
155
|
+
removeItem(key: string): MaybePromise$1<void>;
|
|
156
|
+
}
|
|
157
|
+
interface AuthTokenStorageOptions {
|
|
158
|
+
/** Backing credential store. */
|
|
159
|
+
store: AuthKeyValueStorage;
|
|
160
|
+
/** Storage key prefix. Defaults to `kora_auth`. */
|
|
161
|
+
prefix?: string;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Creates an `AuthTokenStorage` adapter from a runtime key-value store.
|
|
165
|
+
*
|
|
166
|
+
* Use this with platform credential stores instead of wiring `AuthClient`
|
|
167
|
+
* directly to browser localStorage in desktop and mobile apps.
|
|
168
|
+
*/
|
|
169
|
+
declare function createAuthTokenStorage(options: AuthTokenStorageOptions): AuthTokenStorage;
|
|
170
|
+
/**
|
|
171
|
+
* Creates an in-memory token storage adapter.
|
|
172
|
+
*
|
|
173
|
+
* Useful for tests, demos, and SSR. Production desktop and mobile apps should
|
|
174
|
+
* prefer a secure platform-backed store.
|
|
175
|
+
*/
|
|
176
|
+
declare function createMemoryAuthTokenStorage(): AuthTokenStorage;
|
|
177
|
+
/**
|
|
178
|
+
* Adapts Web Storage-compatible APIs such as `localStorage` or `sessionStorage`.
|
|
179
|
+
*/
|
|
180
|
+
declare function createWebStorageAuthTokenStorage(storage: Storage, prefix?: string): AuthTokenStorage;
|
|
181
|
+
|
|
182
|
+
interface AuthDeviceIdentity {
|
|
183
|
+
/** Stable local device ID used in token `dev` claims. */
|
|
184
|
+
deviceId: string;
|
|
185
|
+
/** Public proof-of-possession key serialized as a JSON Web Key string. */
|
|
186
|
+
devicePublicKey: string;
|
|
187
|
+
}
|
|
188
|
+
interface AuthDeviceIdentityProvider {
|
|
189
|
+
getDeviceIdentity(): Promise<AuthDeviceIdentity>;
|
|
190
|
+
}
|
|
191
|
+
interface PersistentDeviceIdentityOptions {
|
|
192
|
+
/** Store for the stable device ID. Use a platform credential store in production. */
|
|
193
|
+
storage: AuthKeyValueStorage;
|
|
194
|
+
/** Store for the non-extractable device key pair. Defaults to IndexedDB when available. */
|
|
195
|
+
keyStore?: DeviceKeyStore;
|
|
196
|
+
/** Storage key for the device ID. Defaults to `kora_auth_device_id`. */
|
|
197
|
+
deviceIdKey?: string;
|
|
198
|
+
/** Optional device ID generator for tests or custom device registries. */
|
|
199
|
+
generateDeviceId?: () => string;
|
|
200
|
+
}
|
|
201
|
+
declare class AuthDeviceIdentityError extends KoraError {
|
|
202
|
+
constructor(message: string, context?: Record<string, unknown>);
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Creates a persistent device identity provider for `AuthClient`.
|
|
206
|
+
*
|
|
207
|
+
* The provider keeps a stable device ID in the supplied key-value store and a
|
|
208
|
+
* non-extractable ECDSA P-256 key pair in the supplied `DeviceKeyStore`. The
|
|
209
|
+
* public key is returned during sign-up/sign-in so the server can bind tokens
|
|
210
|
+
* to a real offline device instead of a transient browser session.
|
|
211
|
+
*/
|
|
212
|
+
declare function createPersistentDeviceIdentity(options: PersistentDeviceIdentityOptions): AuthDeviceIdentityProvider;
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Thrown when an authentication operation fails.
|
|
216
|
+
* Includes a machine-readable code and optional context for debugging.
|
|
217
|
+
*/
|
|
218
|
+
declare class AuthError extends KoraError {
|
|
219
|
+
constructor(message: string, code: string, context?: Record<string, unknown>);
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Possible authentication states for the client.
|
|
223
|
+
* - 'loading': Initial state while restoring tokens from storage
|
|
224
|
+
* - 'authenticated': User is signed in with a valid session
|
|
225
|
+
* - 'unauthenticated': No valid session exists
|
|
226
|
+
*/
|
|
227
|
+
type AuthState = 'loading' | 'authenticated' | 'unauthenticated';
|
|
228
|
+
/**
|
|
229
|
+
* Authenticated user information.
|
|
230
|
+
*/
|
|
231
|
+
interface AuthUser {
|
|
232
|
+
/** Unique user identifier */
|
|
233
|
+
id: string;
|
|
234
|
+
/** User email address */
|
|
235
|
+
email: string;
|
|
236
|
+
/** Display name (may be absent if user did not provide one) */
|
|
237
|
+
name: string | null;
|
|
238
|
+
}
|
|
239
|
+
interface LinkedOAuthAccount {
|
|
240
|
+
id: string;
|
|
241
|
+
userId: string;
|
|
242
|
+
provider: string;
|
|
243
|
+
providerUserId: string;
|
|
244
|
+
email: string | null;
|
|
245
|
+
linkedAt: number;
|
|
246
|
+
}
|
|
247
|
+
interface OAuthAuthorizationResult {
|
|
248
|
+
url: string;
|
|
249
|
+
state: string;
|
|
250
|
+
}
|
|
251
|
+
interface OAuthAuthorizationOptions {
|
|
252
|
+
/**
|
|
253
|
+
* Redirect the current browser window to the provider after creating the URL.
|
|
254
|
+
* Defaults to true when `window.location.assign` is available.
|
|
255
|
+
*/
|
|
256
|
+
redirect?: boolean;
|
|
257
|
+
/**
|
|
258
|
+
* Optional app-specific return path stored in OAuth state metadata.
|
|
259
|
+
*/
|
|
260
|
+
returnTo?: string;
|
|
261
|
+
/**
|
|
262
|
+
* Optional extra metadata stored in OAuth state. Use this for app handoff data.
|
|
263
|
+
*/
|
|
264
|
+
metadata?: Record<string, unknown>;
|
|
265
|
+
deviceId?: string;
|
|
266
|
+
devicePublicKey?: string;
|
|
267
|
+
}
|
|
268
|
+
interface OAuthCallbackParams {
|
|
269
|
+
code: string;
|
|
270
|
+
state: string;
|
|
271
|
+
deviceId?: string;
|
|
272
|
+
devicePublicKey?: string;
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Configuration for the AuthClient.
|
|
276
|
+
*/
|
|
277
|
+
interface AuthClientConfig {
|
|
278
|
+
/** Base URL of the auth server (e.g. 'http://localhost:3001') */
|
|
279
|
+
serverUrl: string;
|
|
280
|
+
/** Storage key prefix for tokens. Defaults to 'kora_auth' */
|
|
281
|
+
storageKey?: string;
|
|
282
|
+
/**
|
|
283
|
+
* Optional token storage adapter.
|
|
284
|
+
*
|
|
285
|
+
* Use this for runtimes where localStorage is not the right place for
|
|
286
|
+
* credentials, such as React Native/Expo SecureStore, iOS Keychain,
|
|
287
|
+
* Android Keystore, or a Tauri secure storage plugin.
|
|
288
|
+
*/
|
|
289
|
+
storage?: AuthTokenStorage;
|
|
290
|
+
/**
|
|
291
|
+
* Optional fetch implementation. Defaults to globalThis.fetch.
|
|
292
|
+
* Useful for tests, SSR adapters, and mobile runtimes with a custom fetch.
|
|
293
|
+
*/
|
|
294
|
+
fetch?: typeof fetch;
|
|
295
|
+
/**
|
|
296
|
+
* Optional local device identity provider.
|
|
297
|
+
*
|
|
298
|
+
* When configured, sign-up and sign-in automatically include stable
|
|
299
|
+
* `deviceId` and `devicePublicKey` fields unless the caller provides them.
|
|
300
|
+
*/
|
|
301
|
+
deviceIdentity?: AuthDeviceIdentityProvider;
|
|
302
|
+
}
|
|
303
|
+
type MaybePromise<T> = T | Promise<T>;
|
|
304
|
+
interface AuthTokenStorage {
|
|
305
|
+
getAccessToken(): MaybePromise<string | null>;
|
|
306
|
+
getRefreshToken(): MaybePromise<string | null>;
|
|
307
|
+
setTokens(access: string, refresh: string): MaybePromise<void>;
|
|
308
|
+
clear(): MaybePromise<void>;
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Client-side authentication manager for Kora.js.
|
|
312
|
+
*
|
|
313
|
+
* Manages token storage, session restoration, sign-up, sign-in, sign-out,
|
|
314
|
+
* token refresh, and auth state change notifications. Framework-agnostic --
|
|
315
|
+
* works in any JavaScript environment with `fetch` and optionally `localStorage`.
|
|
316
|
+
*
|
|
317
|
+
* @example
|
|
318
|
+
* ```typescript
|
|
319
|
+
* const auth = new AuthClient({ serverUrl: 'http://localhost:3001' })
|
|
320
|
+
* await auth.initialize()
|
|
321
|
+
*
|
|
322
|
+
* if (!auth.isAuthenticated) {
|
|
323
|
+
* await auth.signIn({ email: 'user@example.com', password: 'secret' })
|
|
324
|
+
* }
|
|
325
|
+
*
|
|
326
|
+
* const unsub = auth.onAuthChange((state) => {
|
|
327
|
+
* console.log('Auth state:', state)
|
|
328
|
+
* })
|
|
329
|
+
* ```
|
|
330
|
+
*/
|
|
331
|
+
declare class AuthClient {
|
|
332
|
+
private readonly serverUrl;
|
|
333
|
+
private readonly storage;
|
|
334
|
+
private readonly fetchFn;
|
|
335
|
+
private readonly deviceIdentity;
|
|
336
|
+
private readonly listeners;
|
|
337
|
+
private _state;
|
|
338
|
+
private _user;
|
|
339
|
+
private _refreshPromise;
|
|
340
|
+
private _initialized;
|
|
341
|
+
/**
|
|
342
|
+
* Creates a new AuthClient.
|
|
343
|
+
*
|
|
344
|
+
* @param config - Auth client configuration
|
|
345
|
+
*/
|
|
346
|
+
constructor(config: AuthClientConfig);
|
|
347
|
+
/** Current authentication state. */
|
|
348
|
+
get state(): AuthState;
|
|
349
|
+
/** Current authenticated user, or null if not signed in. */
|
|
350
|
+
get currentUser(): AuthUser | null;
|
|
351
|
+
/** Whether the user is currently authenticated. */
|
|
352
|
+
get isAuthenticated(): boolean;
|
|
353
|
+
/**
|
|
354
|
+
* Initialize the auth client by restoring a session from stored tokens.
|
|
355
|
+
*
|
|
356
|
+
* Loads tokens from storage, validates the access token, and attempts a
|
|
357
|
+
* refresh if the access token is expired but a refresh token is available.
|
|
358
|
+
* Safe to call multiple times -- subsequent calls are no-ops once initialized.
|
|
359
|
+
*/
|
|
360
|
+
initialize(): Promise<void>;
|
|
361
|
+
/**
|
|
362
|
+
* Register a new user account.
|
|
363
|
+
*
|
|
364
|
+
* @param params - Sign-up credentials
|
|
365
|
+
* @returns The newly created AuthUser
|
|
366
|
+
* @throws {AuthError} If the request fails or the server returns an error
|
|
367
|
+
*/
|
|
368
|
+
signUp(params: {
|
|
369
|
+
email: string;
|
|
370
|
+
password: string;
|
|
371
|
+
name?: string;
|
|
372
|
+
deviceId?: string;
|
|
373
|
+
devicePublicKey?: string;
|
|
374
|
+
}): Promise<AuthUser>;
|
|
375
|
+
/**
|
|
376
|
+
* Sign in with email and password.
|
|
377
|
+
*
|
|
378
|
+
* @param params - Sign-in credentials
|
|
379
|
+
* @returns The authenticated AuthUser
|
|
380
|
+
* @throws {AuthError} If the credentials are invalid or the request fails
|
|
381
|
+
*/
|
|
382
|
+
signIn(params: {
|
|
383
|
+
email: string;
|
|
384
|
+
password: string;
|
|
385
|
+
deviceId?: string;
|
|
386
|
+
devicePublicKey?: string;
|
|
387
|
+
}): Promise<AuthUser>;
|
|
388
|
+
/**
|
|
389
|
+
* Create an OAuth authorization URL and optionally redirect the current window.
|
|
390
|
+
*
|
|
391
|
+
* For web apps, call this from a button click and keep the default redirect behavior.
|
|
392
|
+
* For desktop/mobile, pass `redirect: false`, open the returned URL with the runtime's
|
|
393
|
+
* browser API, then call `completeOAuthSignIn()` after receiving the callback.
|
|
394
|
+
*/
|
|
395
|
+
signInWithOAuth(provider: string, options?: OAuthAuthorizationOptions): Promise<OAuthAuthorizationResult>;
|
|
396
|
+
/**
|
|
397
|
+
* Complete an OAuth sign-in callback and store the issued Kora tokens.
|
|
398
|
+
*/
|
|
399
|
+
completeOAuthSignIn(provider: string, params: OAuthCallbackParams): Promise<AuthUser>;
|
|
400
|
+
/**
|
|
401
|
+
* Create an OAuth authorization URL for linking another provider to the current user.
|
|
402
|
+
*/
|
|
403
|
+
getOAuthAuthorizationUrl(provider: string, options?: OAuthAuthorizationOptions): Promise<OAuthAuthorizationResult>;
|
|
404
|
+
/**
|
|
405
|
+
* Link an OAuth provider to the current authenticated user.
|
|
406
|
+
*/
|
|
407
|
+
linkOAuth(provider: string, params: OAuthCallbackParams): Promise<LinkedOAuthAccount>;
|
|
408
|
+
/**
|
|
409
|
+
* List OAuth accounts linked to the current authenticated user.
|
|
410
|
+
*/
|
|
411
|
+
listLinkedAccounts(): Promise<LinkedOAuthAccount[]>;
|
|
412
|
+
/**
|
|
413
|
+
* Unlink an OAuth provider from the current authenticated user.
|
|
414
|
+
*/
|
|
415
|
+
unlinkOAuth(provider: string): Promise<void>;
|
|
416
|
+
/**
|
|
417
|
+
* Sign out the current user.
|
|
418
|
+
*
|
|
419
|
+
* Clears local tokens and attempts to revoke the refresh token on the server
|
|
420
|
+
* (best-effort — succeeds even if the server is unreachable). This ensures that
|
|
421
|
+
* stolen refresh tokens cannot be used after the user explicitly signs out.
|
|
422
|
+
*/
|
|
423
|
+
signOut(): Promise<void>;
|
|
424
|
+
/**
|
|
425
|
+
* Get a valid access token, automatically refreshing if expired.
|
|
426
|
+
*
|
|
427
|
+
* @returns A valid access token string, or null if the user is not
|
|
428
|
+
* authenticated and refresh is not possible
|
|
429
|
+
*/
|
|
430
|
+
getAccessToken(): Promise<string | null>;
|
|
431
|
+
/**
|
|
432
|
+
* Get a valid token for the sync engine handshake.
|
|
433
|
+
* Alias for {@link getAccessToken}.
|
|
434
|
+
*
|
|
435
|
+
* @returns A valid access token string, or null if unavailable
|
|
436
|
+
*/
|
|
437
|
+
getSyncToken(): Promise<string | null>;
|
|
438
|
+
/**
|
|
439
|
+
* Subscribe to authentication state changes.
|
|
440
|
+
*
|
|
441
|
+
* The callback is invoked whenever the auth state transitions (e.g., from
|
|
442
|
+
* 'unauthenticated' to 'authenticated' on sign-in).
|
|
443
|
+
*
|
|
444
|
+
* @param callback - Function called with the new AuthState on each change
|
|
445
|
+
* @returns An unsubscribe function that removes the listener
|
|
446
|
+
*
|
|
447
|
+
* @example
|
|
448
|
+
* ```typescript
|
|
449
|
+
* const unsub = auth.onAuthChange((state) => {
|
|
450
|
+
* console.log('Auth state changed to:', state)
|
|
451
|
+
* })
|
|
452
|
+
* // Later: unsub()
|
|
453
|
+
* ```
|
|
454
|
+
*/
|
|
455
|
+
onAuthChange(callback: (state: AuthState) => void): () => void;
|
|
456
|
+
/**
|
|
457
|
+
* Update internal state and notify all listeners.
|
|
458
|
+
*/
|
|
459
|
+
private setState;
|
|
460
|
+
/**
|
|
461
|
+
* Restore a session from a valid access token by fetching the user profile.
|
|
462
|
+
* Falls back to extracting the user ID from the token payload if the
|
|
463
|
+
* /auth/me request fails (offline scenario).
|
|
464
|
+
*/
|
|
465
|
+
private restoreSession;
|
|
466
|
+
/**
|
|
467
|
+
* Fetch the current user profile from the server.
|
|
468
|
+
*/
|
|
469
|
+
private fetchUserProfile;
|
|
470
|
+
private createOAuthAuthorization;
|
|
471
|
+
private withDeviceIdentity;
|
|
472
|
+
/**
|
|
473
|
+
* Refresh the access token using a refresh token.
|
|
474
|
+
* De-duplicates concurrent refresh calls so only one network request is made.
|
|
475
|
+
*/
|
|
476
|
+
private refreshAccessToken;
|
|
477
|
+
private requireAccessToken;
|
|
478
|
+
/**
|
|
479
|
+
* Execute the token refresh network request.
|
|
480
|
+
*/
|
|
481
|
+
private performRefresh;
|
|
482
|
+
/**
|
|
483
|
+
* Make an HTTP request to the auth server.
|
|
484
|
+
*
|
|
485
|
+
* @param path - URL path relative to serverUrl (e.g. '/auth/signin')
|
|
486
|
+
* @param options - Request options
|
|
487
|
+
* @returns Parsed JSON response body
|
|
488
|
+
* @throws {AuthError} On network failure or non-2xx response
|
|
489
|
+
*/
|
|
490
|
+
private request;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* Organization info returned by the server.
|
|
495
|
+
*/
|
|
496
|
+
interface ClientOrganization {
|
|
497
|
+
id: string;
|
|
498
|
+
name: string;
|
|
499
|
+
slug: string;
|
|
500
|
+
ownerId: string;
|
|
501
|
+
createdAt: number;
|
|
502
|
+
updatedAt: number;
|
|
503
|
+
metadata: Record<string, unknown>;
|
|
504
|
+
}
|
|
505
|
+
/**
|
|
506
|
+
* Membership info returned by the server.
|
|
507
|
+
*/
|
|
508
|
+
interface ClientMembership {
|
|
509
|
+
id: string;
|
|
510
|
+
orgId: string;
|
|
511
|
+
userId: string;
|
|
512
|
+
role: string;
|
|
513
|
+
invitedBy: string | null;
|
|
514
|
+
joinedAt: number;
|
|
515
|
+
}
|
|
516
|
+
/**
|
|
517
|
+
* Invitation info returned by the server.
|
|
518
|
+
*/
|
|
519
|
+
interface ClientInvitation {
|
|
520
|
+
id: string;
|
|
521
|
+
orgId: string;
|
|
522
|
+
email: string;
|
|
523
|
+
role: string;
|
|
524
|
+
invitedBy: string;
|
|
525
|
+
token: string;
|
|
526
|
+
createdAt: number;
|
|
527
|
+
expiresAt: number;
|
|
528
|
+
status: string;
|
|
529
|
+
}
|
|
530
|
+
/**
|
|
531
|
+
* Configuration for the OrgClient.
|
|
532
|
+
*/
|
|
533
|
+
interface OrgClientConfig {
|
|
534
|
+
/** Base URL of the auth/org server */
|
|
535
|
+
serverUrl: string;
|
|
536
|
+
/** Function that returns a valid access token for authenticated requests */
|
|
537
|
+
getAccessToken: () => Promise<string | null>;
|
|
538
|
+
}
|
|
539
|
+
/**
|
|
540
|
+
* Thrown when an org operation fails.
|
|
541
|
+
*/
|
|
542
|
+
declare class OrgClientError extends KoraError {
|
|
543
|
+
constructor(message: string, code: string, context?: Record<string, unknown>);
|
|
544
|
+
}
|
|
545
|
+
/**
|
|
546
|
+
* Client-side organization manager.
|
|
547
|
+
*
|
|
548
|
+
* Handles org CRUD, member management, invitations, and active org switching.
|
|
549
|
+
* Framework-agnostic — works in any JavaScript environment with `fetch`.
|
|
550
|
+
*
|
|
551
|
+
* @example
|
|
552
|
+
* ```typescript
|
|
553
|
+
* const orgClient = new OrgClient({
|
|
554
|
+
* serverUrl: 'http://localhost:3001',
|
|
555
|
+
* getAccessToken: () => authClient.getAccessToken(),
|
|
556
|
+
* })
|
|
557
|
+
*
|
|
558
|
+
* const org = await orgClient.createOrg({ name: 'Acme Inc', slug: 'acme' })
|
|
559
|
+
* orgClient.switchOrg(org.id)
|
|
560
|
+
* ```
|
|
561
|
+
*/
|
|
562
|
+
declare class OrgClient {
|
|
563
|
+
private readonly serverUrl;
|
|
564
|
+
private readonly getAccessToken;
|
|
565
|
+
private readonly listeners;
|
|
566
|
+
private _activeOrgId;
|
|
567
|
+
private _activeOrg;
|
|
568
|
+
private _activeRole;
|
|
569
|
+
constructor(config: OrgClientConfig);
|
|
570
|
+
/** Currently active organization ID */
|
|
571
|
+
get activeOrgId(): string | null;
|
|
572
|
+
/** Currently active organization */
|
|
573
|
+
get activeOrg(): ClientOrganization | null;
|
|
574
|
+
/** Current user's role in the active organization */
|
|
575
|
+
get activeRole(): string | null;
|
|
576
|
+
/**
|
|
577
|
+
* Create a new organization.
|
|
578
|
+
*/
|
|
579
|
+
createOrg(params: {
|
|
580
|
+
name: string;
|
|
581
|
+
slug?: string;
|
|
582
|
+
metadata?: Record<string, unknown>;
|
|
583
|
+
}): Promise<ClientOrganization>;
|
|
584
|
+
/**
|
|
585
|
+
* List all organizations the current user belongs to.
|
|
586
|
+
*/
|
|
587
|
+
listOrgs(): Promise<ClientOrganization[]>;
|
|
588
|
+
/**
|
|
589
|
+
* Get an organization by ID.
|
|
590
|
+
*/
|
|
591
|
+
getOrg(orgId: string): Promise<ClientOrganization>;
|
|
592
|
+
/**
|
|
593
|
+
* Update an organization.
|
|
594
|
+
*/
|
|
595
|
+
updateOrg(orgId: string, params: {
|
|
596
|
+
name?: string;
|
|
597
|
+
slug?: string;
|
|
598
|
+
metadata?: Record<string, unknown>;
|
|
599
|
+
}): Promise<ClientOrganization>;
|
|
600
|
+
/**
|
|
601
|
+
* Delete an organization.
|
|
602
|
+
*/
|
|
603
|
+
deleteOrg(orgId: string): Promise<void>;
|
|
604
|
+
/**
|
|
605
|
+
* Switch the active organization context.
|
|
606
|
+
* Fetches the org details and the user's membership/role.
|
|
607
|
+
*/
|
|
608
|
+
switchOrg(orgId: string): Promise<void>;
|
|
609
|
+
/**
|
|
610
|
+
* Clear the active organization (no org selected).
|
|
611
|
+
*/
|
|
612
|
+
clearActiveOrg(): void;
|
|
613
|
+
/**
|
|
614
|
+
* List members of an organization.
|
|
615
|
+
*/
|
|
616
|
+
listMembers(orgId: string): Promise<ClientMembership[]>;
|
|
617
|
+
/**
|
|
618
|
+
* Remove a member from an organization.
|
|
619
|
+
*/
|
|
620
|
+
removeMember(orgId: string, userId: string): Promise<void>;
|
|
621
|
+
/**
|
|
622
|
+
* Update a member's role.
|
|
623
|
+
*/
|
|
624
|
+
updateMemberRole(orgId: string, userId: string, role: string): Promise<ClientMembership>;
|
|
625
|
+
/**
|
|
626
|
+
* Transfer ownership to another member.
|
|
627
|
+
*/
|
|
628
|
+
transferOwnership(orgId: string, newOwnerId: string): Promise<void>;
|
|
629
|
+
/**
|
|
630
|
+
* Leave an organization (remove yourself).
|
|
631
|
+
*/
|
|
632
|
+
leaveOrg(orgId: string): Promise<void>;
|
|
633
|
+
/**
|
|
634
|
+
* Invite a user to an organization by email.
|
|
635
|
+
*/
|
|
636
|
+
inviteMember(orgId: string, params: {
|
|
637
|
+
email: string;
|
|
638
|
+
role: string;
|
|
639
|
+
}): Promise<ClientInvitation>;
|
|
640
|
+
/**
|
|
641
|
+
* Accept an invitation by token.
|
|
642
|
+
*/
|
|
643
|
+
acceptInvitation(token: string): Promise<ClientMembership>;
|
|
644
|
+
/**
|
|
645
|
+
* List pending invitations for an organization.
|
|
646
|
+
*/
|
|
647
|
+
listInvitations(orgId: string): Promise<ClientInvitation[]>;
|
|
648
|
+
/**
|
|
649
|
+
* Revoke a pending invitation.
|
|
650
|
+
*/
|
|
651
|
+
revokeInvitation(orgId: string, invitationId: string): Promise<void>;
|
|
652
|
+
/**
|
|
653
|
+
* List pending invitations for the current user's email.
|
|
654
|
+
*/
|
|
655
|
+
listMyInvitations(email: string): Promise<ClientInvitation[]>;
|
|
656
|
+
/**
|
|
657
|
+
* Subscribe to active org changes.
|
|
658
|
+
* @returns Unsubscribe function
|
|
659
|
+
*/
|
|
660
|
+
onOrgChange(callback: (orgId: string | null) => void): () => void;
|
|
661
|
+
private notifyListeners;
|
|
662
|
+
private request;
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
export { type AuthClientConfig as A, type ClientInvitation as C, type DeviceKeyStore as D, InMemoryDeviceKeyStore as I, type LinkedOAuthAccount as L, type OAuthAuthorizationOptions as O, type PersistentDeviceIdentityOptions as P, type AuthTokenStorage as a, type AuthKeyValueStorage as b, type AuthDeviceIdentityProvider as c, AuthClient as d, type AuthState as e, type AuthDeviceIdentity as f, AuthDeviceIdentityError as g, AuthError as h, type AuthTokenStorageOptions as i, type AuthUser as j, type ClientMembership as k, type ClientOrganization as l, DeviceKeyStoreError as m, IndexedDBDeviceKeyStore as n, type OAuthAuthorizationResult as o, type OAuthCallbackParams as p, OrgClient as q, type OrgClientConfig as r, OrgClientError as s, createAuthTokenStorage as t, createDeviceKeyStore as u, createMemoryAuthTokenStorage as v, createPersistentDeviceIdentity as w, createWebStorageAuthTokenStorage as x };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
hashPassword,
|
|
3
3
|
verifyPassword
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-L7GXPS74.js";
|
|
5
5
|
export {
|
|
6
6
|
hashPassword,
|
|
7
7
|
verifyPassword
|
|
8
8
|
};
|
|
9
|
-
//# sourceMappingURL=password-hash-
|
|
9
|
+
//# sourceMappingURL=password-hash-QRBG6BNJ.js.map
|