@koolbase/core 10.4.0 → 11.1.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/dist/cjs/auth-errors.d.ts +93 -1
- package/dist/cjs/auth-errors.js +160 -3
- package/dist/cjs/auth.d.ts +26 -2
- package/dist/cjs/auth.js +93 -17
- package/dist/cjs/types.d.ts +25 -0
- package/dist/esm/auth-errors.d.ts +93 -1
- package/dist/esm/auth-errors.js +146 -2
- package/dist/esm/auth.d.ts +26 -2
- package/dist/esm/auth.js +94 -18
- package/dist/esm/types.d.ts +25 -0
- package/package.json +1 -1
|
@@ -17,7 +17,13 @@ export declare class UserDisabledError extends KoolbaseAuthError {
|
|
|
17
17
|
constructor();
|
|
18
18
|
}
|
|
19
19
|
export declare class WeakPasswordError extends KoolbaseAuthError {
|
|
20
|
-
|
|
20
|
+
/**
|
|
21
|
+
* The message is optional so the server's own rule can be carried through.
|
|
22
|
+
* The SDK checks length before sending; a project may require more than
|
|
23
|
+
* that, and "must be at least 8 characters" would then be wrong as well as
|
|
24
|
+
* unhelpful.
|
|
25
|
+
*/
|
|
26
|
+
constructor(message?: string);
|
|
21
27
|
}
|
|
22
28
|
export declare class SessionExpiredError extends KoolbaseAuthError {
|
|
23
29
|
constructor();
|
|
@@ -128,6 +134,92 @@ export declare class OAuthEmailConflictError extends KoolbaseAuthError {
|
|
|
128
134
|
export declare class GoogleSignInNotConfiguredError extends KoolbaseAuthError {
|
|
129
135
|
constructor();
|
|
130
136
|
}
|
|
137
|
+
/**
|
|
138
|
+
* The server answered a sign-in as though it succeeded, without the tokens a
|
|
139
|
+
* session needs.
|
|
140
|
+
*
|
|
141
|
+
* Distinct from verification_required, which is a legitimate session-less
|
|
142
|
+
* success the SDK reports through SignUpResult. This is the other case: the
|
|
143
|
+
* response claims authentication and cannot support it. Inventing a session
|
|
144
|
+
* from it is what produced a signed-in user whose every request went out as
|
|
145
|
+
* `Bearer undefined`, so the SDK refuses instead.
|
|
146
|
+
*/
|
|
147
|
+
/**
|
|
148
|
+
* Sign-in refused because the account has not verified its email or phone.
|
|
149
|
+
*
|
|
150
|
+
* In a project with require_verified_contact on, this is the most common auth
|
|
151
|
+
* error there is — the user registered, did not click the link, and came back.
|
|
152
|
+
* It arrived untyped until 11.1.0, so apps showed "something went wrong" for
|
|
153
|
+
* the one case with an obvious remedy: offer to resend.
|
|
154
|
+
*/
|
|
155
|
+
export declare class ContactNotVerifiedError extends KoolbaseAuthError {
|
|
156
|
+
constructor(message?: string);
|
|
157
|
+
}
|
|
158
|
+
/** The project has registration turned off. Not a credential problem. */
|
|
159
|
+
export declare class SignupsDisabledError extends KoolbaseAuthError {
|
|
160
|
+
constructor(message?: string);
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* A verification or reset link that has expired.
|
|
164
|
+
*
|
|
165
|
+
* Distinct from TokenAlreadyUsedError because the remedy differs: expired
|
|
166
|
+
* means request another, used means it already worked and they may simply be
|
|
167
|
+
* clicking an old email.
|
|
168
|
+
*/
|
|
169
|
+
export declare class TokenExpiredError extends KoolbaseAuthError {
|
|
170
|
+
constructor(message?: string);
|
|
171
|
+
}
|
|
172
|
+
/** A one-shot link clicked twice. Often means it already succeeded. */
|
|
173
|
+
export declare class TokenAlreadyUsedError extends KoolbaseAuthError {
|
|
174
|
+
constructor(message?: string);
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* The current password given for a password change was wrong.
|
|
178
|
+
*
|
|
179
|
+
* Named for the operation rather than the code: the server calls this
|
|
180
|
+
* `invalid_password`, which reads like a rejected new password and is not.
|
|
181
|
+
*/
|
|
182
|
+
export declare class CurrentPasswordIncorrectError extends KoolbaseAuthError {
|
|
183
|
+
constructor(message?: string);
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* An OAuth sign-in for an email that already has an account by another
|
|
187
|
+
* method. The remedy is to sign in the original way and connect the provider
|
|
188
|
+
* afterwards, which the server's message says.
|
|
189
|
+
*/
|
|
190
|
+
export declare class AccountExistsError extends KoolbaseAuthError {
|
|
191
|
+
constructor(message?: string);
|
|
192
|
+
}
|
|
193
|
+
/** A password attempt against an account that only has Google or Apple. */
|
|
194
|
+
export declare class OAuthOnlyAccountError extends KoolbaseAuthError {
|
|
195
|
+
constructor(message?: string);
|
|
196
|
+
}
|
|
197
|
+
/** A provider the project has not enabled. */
|
|
198
|
+
export declare class UnsupportedOAuthProviderError extends KoolbaseAuthError {
|
|
199
|
+
constructor(message?: string);
|
|
200
|
+
}
|
|
201
|
+
/** The call needs a signed-in user and did not have one. */
|
|
202
|
+
export declare class SessionRequiredError extends KoolbaseAuthError {
|
|
203
|
+
constructor(message?: string);
|
|
204
|
+
}
|
|
205
|
+
/** The caller is authenticated but not permitted to do this. */
|
|
206
|
+
export declare class InsufficientAuthorityError extends KoolbaseAuthError {
|
|
207
|
+
constructor(message?: string);
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Refused because it would remove the account's last way of signing in —
|
|
211
|
+
* unlinking the only provider, or clearing the only password.
|
|
212
|
+
*/
|
|
213
|
+
export declare class LastCredentialError extends KoolbaseAuthError {
|
|
214
|
+
constructor(message?: string);
|
|
215
|
+
}
|
|
216
|
+
/** Hiding account existence needs verification on; the project has it off. */
|
|
217
|
+
export declare class HideRequiresVerificationError extends KoolbaseAuthError {
|
|
218
|
+
constructor(message?: string);
|
|
219
|
+
}
|
|
220
|
+
export declare class MalformedSessionResponseError extends KoolbaseAuthError {
|
|
221
|
+
constructor(missing: string);
|
|
222
|
+
}
|
|
131
223
|
export declare class InvalidGoogleTokenError extends KoolbaseAuthError {
|
|
132
224
|
constructor();
|
|
133
225
|
}
|
package/dist/cjs/auth-errors.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.GoogleEmailRequiredError = exports.InvalidGoogleTokenError = exports.GoogleSignInNotConfiguredError = exports.OAuthEmailConflictError = exports.AppleEmailRequiredError = exports.InvalidAppleTokenError = exports.AppleSignInNotConfiguredError = exports.SmsConfigMissingError = exports.PhoneAlreadyLinkedError = exports.OtpRateLimitError = exports.OtpMaxAttemptsError = exports.OtpInvalidError = exports.OtpExpiredError = exports.InvalidPhoneNumberError = exports.NetworkError = exports.VerificationResendDailyCapError = exports.VerificationResendCooldownError = exports.RateLimitError = exports.UnlockTokenInvalidError = exports.AccountLockedError = exports.TokenRevokedError = exports.SessionExpiredError = exports.WeakPasswordError = exports.UserDisabledError = exports.EmailAlreadyInUseError = exports.InvalidCredentialsError = exports.KoolbaseAuthError = void 0;
|
|
3
|
+
exports.GoogleEmailRequiredError = exports.InvalidGoogleTokenError = exports.MalformedSessionResponseError = exports.HideRequiresVerificationError = exports.LastCredentialError = exports.InsufficientAuthorityError = exports.SessionRequiredError = exports.UnsupportedOAuthProviderError = exports.OAuthOnlyAccountError = exports.AccountExistsError = exports.CurrentPasswordIncorrectError = exports.TokenAlreadyUsedError = exports.TokenExpiredError = exports.SignupsDisabledError = exports.ContactNotVerifiedError = exports.GoogleSignInNotConfiguredError = exports.OAuthEmailConflictError = exports.AppleEmailRequiredError = exports.InvalidAppleTokenError = exports.AppleSignInNotConfiguredError = exports.SmsConfigMissingError = exports.PhoneAlreadyLinkedError = exports.OtpRateLimitError = exports.OtpMaxAttemptsError = exports.OtpInvalidError = exports.OtpExpiredError = exports.InvalidPhoneNumberError = exports.NetworkError = exports.VerificationResendDailyCapError = exports.VerificationResendCooldownError = exports.RateLimitError = exports.UnlockTokenInvalidError = exports.AccountLockedError = exports.TokenRevokedError = exports.SessionExpiredError = exports.WeakPasswordError = exports.UserDisabledError = exports.EmailAlreadyInUseError = exports.InvalidCredentialsError = exports.KoolbaseAuthError = void 0;
|
|
4
4
|
const errors_js_1 = require("./errors.js");
|
|
5
5
|
/**
|
|
6
6
|
* Base error type for all Koolbase auth errors. Catchable via
|
|
@@ -41,8 +41,14 @@ class UserDisabledError extends KoolbaseAuthError {
|
|
|
41
41
|
}
|
|
42
42
|
exports.UserDisabledError = UserDisabledError;
|
|
43
43
|
class WeakPasswordError extends KoolbaseAuthError {
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
/**
|
|
45
|
+
* The message is optional so the server's own rule can be carried through.
|
|
46
|
+
* The SDK checks length before sending; a project may require more than
|
|
47
|
+
* that, and "must be at least 8 characters" would then be wrong as well as
|
|
48
|
+
* unhelpful.
|
|
49
|
+
*/
|
|
50
|
+
constructor(message) {
|
|
51
|
+
super(message ?? 'Password must be at least 8 characters', 'weak_password');
|
|
46
52
|
this.name = 'WeakPasswordError';
|
|
47
53
|
Object.setPrototypeOf(this, WeakPasswordError.prototype);
|
|
48
54
|
}
|
|
@@ -261,6 +267,157 @@ class GoogleSignInNotConfiguredError extends KoolbaseAuthError {
|
|
|
261
267
|
}
|
|
262
268
|
}
|
|
263
269
|
exports.GoogleSignInNotConfiguredError = GoogleSignInNotConfiguredError;
|
|
270
|
+
/**
|
|
271
|
+
* The server answered a sign-in as though it succeeded, without the tokens a
|
|
272
|
+
* session needs.
|
|
273
|
+
*
|
|
274
|
+
* Distinct from verification_required, which is a legitimate session-less
|
|
275
|
+
* success the SDK reports through SignUpResult. This is the other case: the
|
|
276
|
+
* response claims authentication and cannot support it. Inventing a session
|
|
277
|
+
* from it is what produced a signed-in user whose every request went out as
|
|
278
|
+
* `Bearer undefined`, so the SDK refuses instead.
|
|
279
|
+
*/
|
|
280
|
+
/**
|
|
281
|
+
* Sign-in refused because the account has not verified its email or phone.
|
|
282
|
+
*
|
|
283
|
+
* In a project with require_verified_contact on, this is the most common auth
|
|
284
|
+
* error there is — the user registered, did not click the link, and came back.
|
|
285
|
+
* It arrived untyped until 11.1.0, so apps showed "something went wrong" for
|
|
286
|
+
* the one case with an obvious remedy: offer to resend.
|
|
287
|
+
*/
|
|
288
|
+
class ContactNotVerifiedError extends KoolbaseAuthError {
|
|
289
|
+
constructor(message) {
|
|
290
|
+
super(message ?? 'Verify your email or phone before signing in', 'contact_not_verified');
|
|
291
|
+
this.name = 'ContactNotVerifiedError';
|
|
292
|
+
Object.setPrototypeOf(this, ContactNotVerifiedError.prototype);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
exports.ContactNotVerifiedError = ContactNotVerifiedError;
|
|
296
|
+
/** The project has registration turned off. Not a credential problem. */
|
|
297
|
+
class SignupsDisabledError extends KoolbaseAuthError {
|
|
298
|
+
constructor(message) {
|
|
299
|
+
super(message ?? 'Registration is disabled for this project', 'signups_disabled');
|
|
300
|
+
this.name = 'SignupsDisabledError';
|
|
301
|
+
Object.setPrototypeOf(this, SignupsDisabledError.prototype);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
exports.SignupsDisabledError = SignupsDisabledError;
|
|
305
|
+
/**
|
|
306
|
+
* A verification or reset link that has expired.
|
|
307
|
+
*
|
|
308
|
+
* Distinct from TokenAlreadyUsedError because the remedy differs: expired
|
|
309
|
+
* means request another, used means it already worked and they may simply be
|
|
310
|
+
* clicking an old email.
|
|
311
|
+
*/
|
|
312
|
+
class TokenExpiredError extends KoolbaseAuthError {
|
|
313
|
+
constructor(message) {
|
|
314
|
+
super(message ?? 'This link has expired — request a new one', 'token_expired');
|
|
315
|
+
this.name = 'TokenExpiredError';
|
|
316
|
+
Object.setPrototypeOf(this, TokenExpiredError.prototype);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
exports.TokenExpiredError = TokenExpiredError;
|
|
320
|
+
/** A one-shot link clicked twice. Often means it already succeeded. */
|
|
321
|
+
class TokenAlreadyUsedError extends KoolbaseAuthError {
|
|
322
|
+
constructor(message) {
|
|
323
|
+
super(message ?? 'This link has already been used', 'token_used');
|
|
324
|
+
this.name = 'TokenAlreadyUsedError';
|
|
325
|
+
Object.setPrototypeOf(this, TokenAlreadyUsedError.prototype);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
exports.TokenAlreadyUsedError = TokenAlreadyUsedError;
|
|
329
|
+
/**
|
|
330
|
+
* The current password given for a password change was wrong.
|
|
331
|
+
*
|
|
332
|
+
* Named for the operation rather than the code: the server calls this
|
|
333
|
+
* `invalid_password`, which reads like a rejected new password and is not.
|
|
334
|
+
*/
|
|
335
|
+
class CurrentPasswordIncorrectError extends KoolbaseAuthError {
|
|
336
|
+
constructor(message) {
|
|
337
|
+
super(message ?? 'Current password is incorrect', 'invalid_password');
|
|
338
|
+
this.name = 'CurrentPasswordIncorrectError';
|
|
339
|
+
Object.setPrototypeOf(this, CurrentPasswordIncorrectError.prototype);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
exports.CurrentPasswordIncorrectError = CurrentPasswordIncorrectError;
|
|
343
|
+
/**
|
|
344
|
+
* An OAuth sign-in for an email that already has an account by another
|
|
345
|
+
* method. The remedy is to sign in the original way and connect the provider
|
|
346
|
+
* afterwards, which the server's message says.
|
|
347
|
+
*/
|
|
348
|
+
class AccountExistsError extends KoolbaseAuthError {
|
|
349
|
+
constructor(message) {
|
|
350
|
+
super(message ?? 'An account with this email already exists — sign in with your existing method first', 'account_exists');
|
|
351
|
+
this.name = 'AccountExistsError';
|
|
352
|
+
Object.setPrototypeOf(this, AccountExistsError.prototype);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
exports.AccountExistsError = AccountExistsError;
|
|
356
|
+
/** A password attempt against an account that only has Google or Apple. */
|
|
357
|
+
class OAuthOnlyAccountError extends KoolbaseAuthError {
|
|
358
|
+
constructor(message) {
|
|
359
|
+
super(message ?? 'This account uses Google or Apple sign-in and has no password', 'oauth_only_account');
|
|
360
|
+
this.name = 'OAuthOnlyAccountError';
|
|
361
|
+
Object.setPrototypeOf(this, OAuthOnlyAccountError.prototype);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
exports.OAuthOnlyAccountError = OAuthOnlyAccountError;
|
|
365
|
+
/** A provider the project has not enabled. */
|
|
366
|
+
class UnsupportedOAuthProviderError extends KoolbaseAuthError {
|
|
367
|
+
constructor(message) {
|
|
368
|
+
super(message ?? 'That sign-in provider is not supported', 'unsupported_oauth_provider');
|
|
369
|
+
this.name = 'UnsupportedOAuthProviderError';
|
|
370
|
+
Object.setPrototypeOf(this, UnsupportedOAuthProviderError.prototype);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
exports.UnsupportedOAuthProviderError = UnsupportedOAuthProviderError;
|
|
374
|
+
/** The call needs a signed-in user and did not have one. */
|
|
375
|
+
class SessionRequiredError extends KoolbaseAuthError {
|
|
376
|
+
constructor(message) {
|
|
377
|
+
super(message ?? 'This action requires a signed-in user', 'session_required');
|
|
378
|
+
this.name = 'SessionRequiredError';
|
|
379
|
+
Object.setPrototypeOf(this, SessionRequiredError.prototype);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
exports.SessionRequiredError = SessionRequiredError;
|
|
383
|
+
/** The caller is authenticated but not permitted to do this. */
|
|
384
|
+
class InsufficientAuthorityError extends KoolbaseAuthError {
|
|
385
|
+
constructor(message) {
|
|
386
|
+
super(message ?? 'You do not have permission to do that', 'insufficient_authority');
|
|
387
|
+
this.name = 'InsufficientAuthorityError';
|
|
388
|
+
Object.setPrototypeOf(this, InsufficientAuthorityError.prototype);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
exports.InsufficientAuthorityError = InsufficientAuthorityError;
|
|
392
|
+
/**
|
|
393
|
+
* Refused because it would remove the account's last way of signing in —
|
|
394
|
+
* unlinking the only provider, or clearing the only password.
|
|
395
|
+
*/
|
|
396
|
+
class LastCredentialError extends KoolbaseAuthError {
|
|
397
|
+
constructor(message) {
|
|
398
|
+
super(message ?? 'This is the account\'s only sign-in method and cannot be removed', 'last_credential');
|
|
399
|
+
this.name = 'LastCredentialError';
|
|
400
|
+
Object.setPrototypeOf(this, LastCredentialError.prototype);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
exports.LastCredentialError = LastCredentialError;
|
|
404
|
+
/** Hiding account existence needs verification on; the project has it off. */
|
|
405
|
+
class HideRequiresVerificationError extends KoolbaseAuthError {
|
|
406
|
+
constructor(message) {
|
|
407
|
+
super(message ?? 'Hiding account existence requires verified contact to be enabled', 'hide_requires_verification');
|
|
408
|
+
this.name = 'HideRequiresVerificationError';
|
|
409
|
+
Object.setPrototypeOf(this, HideRequiresVerificationError.prototype);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
exports.HideRequiresVerificationError = HideRequiresVerificationError;
|
|
413
|
+
class MalformedSessionResponseError extends KoolbaseAuthError {
|
|
414
|
+
constructor(missing) {
|
|
415
|
+
super(`The server returned a session without ${missing}. This is a protocol error, not a credential problem.`, 'malformed_session_response');
|
|
416
|
+
this.name = 'MalformedSessionResponseError';
|
|
417
|
+
Object.setPrototypeOf(this, MalformedSessionResponseError.prototype);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
exports.MalformedSessionResponseError = MalformedSessionResponseError;
|
|
264
421
|
class InvalidGoogleTokenError extends KoolbaseAuthError {
|
|
265
422
|
constructor() {
|
|
266
423
|
super('Invalid Google identity token', 'invalid_google_token');
|
package/dist/cjs/auth.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AuthStateListener, KoolbaseConfig, KoolbaseSession, KoolbaseUser, LinkPhoneParams, LoginParams, OtpSendResult, PhoneVerifyResult, RegisterParams, RestoreResult, ResendVerificationResult, SendOtpParams, SignInWithAppleParams, VerifyOtpParams } from './types.js';
|
|
1
|
+
import { AuthStateListener, KoolbaseConfig, KoolbaseSession, KoolbaseUser, LinkPhoneParams, LoginParams, OtpSendResult, PhoneVerifyResult, RegisterParams, RestoreResult, ResendVerificationResult, SendOtpParams, SignUpResult, SignInWithAppleParams, VerifyOtpParams } from './types.js';
|
|
2
2
|
import type { SignInWithGoogleParams } from './types.js';
|
|
3
3
|
export declare class KoolbaseAuth {
|
|
4
4
|
private config;
|
|
@@ -72,7 +72,19 @@ export declare class KoolbaseAuth {
|
|
|
72
72
|
clearStoredSession(): Promise<void>;
|
|
73
73
|
private clearSessionInternal;
|
|
74
74
|
restoreSession(): Promise<RestoreResult>;
|
|
75
|
-
|
|
75
|
+
/**
|
|
76
|
+
* Create an account.
|
|
77
|
+
*
|
|
78
|
+
* Two outcomes, and the caller must tell them apart. With
|
|
79
|
+
* require_verified_contact off, the account is created and signed in.
|
|
80
|
+
* With it on, the account is created and NO session is issued — the user
|
|
81
|
+
* verifies their email before their first sign-in. Both are successes.
|
|
82
|
+
*
|
|
83
|
+
* Switch on `status` rather than checking the session for null: the point
|
|
84
|
+
* of the union is that there is no path where an app reads the user and
|
|
85
|
+
* assumes it is signed in.
|
|
86
|
+
*/
|
|
87
|
+
register(params: RegisterParams): Promise<SignUpResult>;
|
|
76
88
|
login(params: LoginParams): Promise<KoolbaseSession>;
|
|
77
89
|
/**
|
|
78
90
|
* Sign in with Apple using a credential obtained from a native Apple
|
|
@@ -212,6 +224,18 @@ export declare class KoolbaseAuth {
|
|
|
212
224
|
* affects how a bare 401 (no code, older server) is interpreted.
|
|
213
225
|
*/
|
|
214
226
|
private parseSessionResponse;
|
|
227
|
+
/**
|
|
228
|
+
* A session, or a refusal — never a session-shaped object with nothing in
|
|
229
|
+
* it.
|
|
230
|
+
*
|
|
231
|
+
* This used to read the fields straight off the body, so a response with no
|
|
232
|
+
* tokens produced a session whose accessToken was undefined. It persisted,
|
|
233
|
+
* currentUser returned a user, and every authenticated request went out as
|
|
234
|
+
* `Bearer undefined` and came back 401 — signed in as far as the app could
|
|
235
|
+
* tell, and unable to do anything. A user object without tokens is not a
|
|
236
|
+
* session, and refusing is the only honest answer.
|
|
237
|
+
*/
|
|
238
|
+
private sessionFromBody;
|
|
215
239
|
private checkResponse;
|
|
216
240
|
/**
|
|
217
241
|
* Map a non-2xx credential/session response to a typed error.
|
package/dist/cjs/auth.js
CHANGED
|
@@ -208,6 +208,18 @@ class KoolbaseAuth {
|
|
|
208
208
|
}
|
|
209
209
|
}
|
|
210
210
|
// ─── Public auth API ────────────────────────────────────────────────────
|
|
211
|
+
/**
|
|
212
|
+
* Create an account.
|
|
213
|
+
*
|
|
214
|
+
* Two outcomes, and the caller must tell them apart. With
|
|
215
|
+
* require_verified_contact off, the account is created and signed in.
|
|
216
|
+
* With it on, the account is created and NO session is issued — the user
|
|
217
|
+
* verifies their email before their first sign-in. Both are successes.
|
|
218
|
+
*
|
|
219
|
+
* Switch on `status` rather than checking the session for null: the point
|
|
220
|
+
* of the union is that there is no path where an app reads the user and
|
|
221
|
+
* assumes it is signed in.
|
|
222
|
+
*/
|
|
211
223
|
async register(params) {
|
|
212
224
|
if (params.password.length < 8)
|
|
213
225
|
throw new auth_errors_js_1.WeakPasswordError();
|
|
@@ -215,9 +227,25 @@ class KoolbaseAuth {
|
|
|
215
227
|
method: 'POST',
|
|
216
228
|
body: params,
|
|
217
229
|
});
|
|
218
|
-
|
|
230
|
+
if (!res.ok)
|
|
231
|
+
await this.throwTypedError(res); // never returns
|
|
232
|
+
const data = await res.json();
|
|
233
|
+
// The server's own discriminator. It sends this deliberately — a 201
|
|
234
|
+
// meaning "created, not signed in" — and the SDK ignored it until now,
|
|
235
|
+
// building a session out of a body with no tokens in it.
|
|
236
|
+
if (data.verification_required === true) {
|
|
237
|
+
// Nothing is persisted and no listener fires: a pending signup is not
|
|
238
|
+
// an authentication event, and an existing session on this device
|
|
239
|
+
// belongs to whoever was already signed in.
|
|
240
|
+
return {
|
|
241
|
+
status: 'verification_required',
|
|
242
|
+
user: this.mapUser(data.user),
|
|
243
|
+
session: null,
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
const session = this.sessionFromBody(data);
|
|
219
247
|
await this.setSessionInternal(session);
|
|
220
|
-
return session.user;
|
|
248
|
+
return { status: 'authenticated', user: session.user, session };
|
|
221
249
|
}
|
|
222
250
|
async login(params) {
|
|
223
251
|
const res = await this.authRequest('/v1/sdk/auth/login', {
|
|
@@ -324,13 +352,7 @@ class KoolbaseAuth {
|
|
|
324
352
|
*/
|
|
325
353
|
async parseGoogleSessionResponse(res) {
|
|
326
354
|
if (res.status === 200) {
|
|
327
|
-
|
|
328
|
-
return {
|
|
329
|
-
accessToken: data.access_token,
|
|
330
|
-
refreshToken: data.refresh_token,
|
|
331
|
-
expiresAt: data.expires_at,
|
|
332
|
-
user: this.mapUser(data.user),
|
|
333
|
-
};
|
|
355
|
+
return this.sessionFromBody(await res.json());
|
|
334
356
|
}
|
|
335
357
|
let body = {};
|
|
336
358
|
try {
|
|
@@ -384,13 +406,7 @@ class KoolbaseAuth {
|
|
|
384
406
|
*/
|
|
385
407
|
async parseAppleSessionResponse(res) {
|
|
386
408
|
if (res.status === 200) {
|
|
387
|
-
|
|
388
|
-
return {
|
|
389
|
-
accessToken: data.access_token,
|
|
390
|
-
refreshToken: data.refresh_token,
|
|
391
|
-
expiresAt: data.expires_at,
|
|
392
|
-
user: this.mapUser(data.user),
|
|
393
|
-
};
|
|
409
|
+
return this.sessionFromBody(await res.json());
|
|
394
410
|
}
|
|
395
411
|
let body = {};
|
|
396
412
|
try {
|
|
@@ -713,7 +729,24 @@ class KoolbaseAuth {
|
|
|
713
729
|
async parseSessionResponse(res, isRefresh) {
|
|
714
730
|
if (!res.ok)
|
|
715
731
|
await this.throwTypedError(res, isRefresh); // never returns
|
|
716
|
-
|
|
732
|
+
return this.sessionFromBody(await res.json());
|
|
733
|
+
}
|
|
734
|
+
/**
|
|
735
|
+
* A session, or a refusal — never a session-shaped object with nothing in
|
|
736
|
+
* it.
|
|
737
|
+
*
|
|
738
|
+
* This used to read the fields straight off the body, so a response with no
|
|
739
|
+
* tokens produced a session whose accessToken was undefined. It persisted,
|
|
740
|
+
* currentUser returned a user, and every authenticated request went out as
|
|
741
|
+
* `Bearer undefined` and came back 401 — signed in as far as the app could
|
|
742
|
+
* tell, and unable to do anything. A user object without tokens is not a
|
|
743
|
+
* session, and refusing is the only honest answer.
|
|
744
|
+
*/
|
|
745
|
+
sessionFromBody(data) {
|
|
746
|
+
if (!data?.access_token)
|
|
747
|
+
throw new auth_errors_js_1.MalformedSessionResponseError('an access token');
|
|
748
|
+
if (!data?.refresh_token)
|
|
749
|
+
throw new auth_errors_js_1.MalformedSessionResponseError('a refresh token');
|
|
717
750
|
return {
|
|
718
751
|
accessToken: data.access_token,
|
|
719
752
|
refreshToken: data.refresh_token,
|
|
@@ -764,6 +797,49 @@ class KoolbaseAuth {
|
|
|
764
797
|
throw new auth_errors_js_1.UnlockTokenInvalidError();
|
|
765
798
|
case 'rate_limit':
|
|
766
799
|
throw new auth_errors_js_1.RateLimitError(msg || undefined);
|
|
800
|
+
// Verification state. Two codes, one situation — projectauth and the
|
|
801
|
+
// dashboard's auth package name it differently and an app should not
|
|
802
|
+
// have to know which spoke.
|
|
803
|
+
// Verification state. Two codes, one situation — projectauth and the
|
|
804
|
+
// dashboard's auth package name it differently and an app should not
|
|
805
|
+
// have to know which spoke.
|
|
806
|
+
case 'contact_not_verified':
|
|
807
|
+
case 'email_not_verified':
|
|
808
|
+
throw new auth_errors_js_1.ContactNotVerifiedError(msg || undefined);
|
|
809
|
+
// Registration
|
|
810
|
+
case 'signups_disabled':
|
|
811
|
+
throw new auth_errors_js_1.SignupsDisabledError(msg || undefined);
|
|
812
|
+
case 'weak_password':
|
|
813
|
+
// The class existed and was only ever thrown client-side for length.
|
|
814
|
+
// The server has its own rules, and a password that passes ours and
|
|
815
|
+
// fails theirs deserves the same error, not a generic one.
|
|
816
|
+
throw new auth_errors_js_1.WeakPasswordError(msg || undefined);
|
|
817
|
+
case 'account_exists':
|
|
818
|
+
throw new auth_errors_js_1.AccountExistsError(msg || undefined);
|
|
819
|
+
// Link tokens — verification and password reset
|
|
820
|
+
case 'token_expired':
|
|
821
|
+
throw new auth_errors_js_1.TokenExpiredError(msg || undefined);
|
|
822
|
+
case 'token_used':
|
|
823
|
+
throw new auth_errors_js_1.TokenAlreadyUsedError(msg || undefined);
|
|
824
|
+
case 'invalid_token':
|
|
825
|
+
throw new auth_errors_js_1.UnlockTokenInvalidError();
|
|
826
|
+
// Password change
|
|
827
|
+
case 'invalid_password':
|
|
828
|
+
throw new auth_errors_js_1.CurrentPasswordIncorrectError(msg || undefined);
|
|
829
|
+
// OAuth
|
|
830
|
+
case 'oauth_only_account':
|
|
831
|
+
throw new auth_errors_js_1.OAuthOnlyAccountError(msg || undefined);
|
|
832
|
+
case 'unsupported_oauth_provider':
|
|
833
|
+
throw new auth_errors_js_1.UnsupportedOAuthProviderError(msg || undefined);
|
|
834
|
+
// Authority
|
|
835
|
+
case 'session_required':
|
|
836
|
+
throw new auth_errors_js_1.SessionRequiredError(msg || undefined);
|
|
837
|
+
case 'insufficient_authority':
|
|
838
|
+
throw new auth_errors_js_1.InsufficientAuthorityError(msg || undefined);
|
|
839
|
+
case 'last_credential':
|
|
840
|
+
throw new auth_errors_js_1.LastCredentialError(msg || undefined);
|
|
841
|
+
case 'hide_requires_verification':
|
|
842
|
+
throw new auth_errors_js_1.HideRequiresVerificationError(msg || undefined);
|
|
767
843
|
case 'resend_cooldown':
|
|
768
844
|
throw new auth_errors_js_1.VerificationResendCooldownError(body.cooldown_until ? new Date(body.cooldown_until) : null, msg || undefined);
|
|
769
845
|
case 'resend_daily_cap':
|
package/dist/cjs/types.d.ts
CHANGED
|
@@ -63,6 +63,31 @@ export interface KoolbaseSession {
|
|
|
63
63
|
* cooldownUntil is when the next send becomes possible. The server throttles
|
|
64
64
|
* sends, and a countdown is a better answer to a user than a bare refusal.
|
|
65
65
|
*/
|
|
66
|
+
/**
|
|
67
|
+
* What register() answers with.
|
|
68
|
+
*
|
|
69
|
+
* A discriminated union rather than a nullable session, because registration
|
|
70
|
+
* succeeding and authentication succeeding are different outcomes and an app
|
|
71
|
+
* must handle both. A project with require_verified_contact on creates the
|
|
72
|
+
* account and issues no session — the server returns 201 with
|
|
73
|
+
* verification_required, deliberately not an error, since reporting failure
|
|
74
|
+
* for a signup that worked is worse than either alternative.
|
|
75
|
+
*
|
|
76
|
+
* Until 10.x this was typed as the user alone, and the SDK built a session
|
|
77
|
+
* from a response that had no tokens: currentUser returned someone whose
|
|
78
|
+
* every request went out as `Bearer undefined`. A nullable session would
|
|
79
|
+
* have let an app read result.user and reproduce that at one remove, so the
|
|
80
|
+
* status is the only way in.
|
|
81
|
+
*/
|
|
82
|
+
export type SignUpResult = {
|
|
83
|
+
status: 'authenticated';
|
|
84
|
+
user: KoolbaseUser;
|
|
85
|
+
session: KoolbaseSession;
|
|
86
|
+
} | {
|
|
87
|
+
status: 'verification_required';
|
|
88
|
+
user: KoolbaseUser;
|
|
89
|
+
session: null;
|
|
90
|
+
};
|
|
66
91
|
export interface ResendVerificationResult {
|
|
67
92
|
alreadyVerified: boolean;
|
|
68
93
|
/** When the link in the email stops working. Null when nothing was sent. */
|
|
@@ -17,7 +17,13 @@ export declare class UserDisabledError extends KoolbaseAuthError {
|
|
|
17
17
|
constructor();
|
|
18
18
|
}
|
|
19
19
|
export declare class WeakPasswordError extends KoolbaseAuthError {
|
|
20
|
-
|
|
20
|
+
/**
|
|
21
|
+
* The message is optional so the server's own rule can be carried through.
|
|
22
|
+
* The SDK checks length before sending; a project may require more than
|
|
23
|
+
* that, and "must be at least 8 characters" would then be wrong as well as
|
|
24
|
+
* unhelpful.
|
|
25
|
+
*/
|
|
26
|
+
constructor(message?: string);
|
|
21
27
|
}
|
|
22
28
|
export declare class SessionExpiredError extends KoolbaseAuthError {
|
|
23
29
|
constructor();
|
|
@@ -128,6 +134,92 @@ export declare class OAuthEmailConflictError extends KoolbaseAuthError {
|
|
|
128
134
|
export declare class GoogleSignInNotConfiguredError extends KoolbaseAuthError {
|
|
129
135
|
constructor();
|
|
130
136
|
}
|
|
137
|
+
/**
|
|
138
|
+
* The server answered a sign-in as though it succeeded, without the tokens a
|
|
139
|
+
* session needs.
|
|
140
|
+
*
|
|
141
|
+
* Distinct from verification_required, which is a legitimate session-less
|
|
142
|
+
* success the SDK reports through SignUpResult. This is the other case: the
|
|
143
|
+
* response claims authentication and cannot support it. Inventing a session
|
|
144
|
+
* from it is what produced a signed-in user whose every request went out as
|
|
145
|
+
* `Bearer undefined`, so the SDK refuses instead.
|
|
146
|
+
*/
|
|
147
|
+
/**
|
|
148
|
+
* Sign-in refused because the account has not verified its email or phone.
|
|
149
|
+
*
|
|
150
|
+
* In a project with require_verified_contact on, this is the most common auth
|
|
151
|
+
* error there is — the user registered, did not click the link, and came back.
|
|
152
|
+
* It arrived untyped until 11.1.0, so apps showed "something went wrong" for
|
|
153
|
+
* the one case with an obvious remedy: offer to resend.
|
|
154
|
+
*/
|
|
155
|
+
export declare class ContactNotVerifiedError extends KoolbaseAuthError {
|
|
156
|
+
constructor(message?: string);
|
|
157
|
+
}
|
|
158
|
+
/** The project has registration turned off. Not a credential problem. */
|
|
159
|
+
export declare class SignupsDisabledError extends KoolbaseAuthError {
|
|
160
|
+
constructor(message?: string);
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* A verification or reset link that has expired.
|
|
164
|
+
*
|
|
165
|
+
* Distinct from TokenAlreadyUsedError because the remedy differs: expired
|
|
166
|
+
* means request another, used means it already worked and they may simply be
|
|
167
|
+
* clicking an old email.
|
|
168
|
+
*/
|
|
169
|
+
export declare class TokenExpiredError extends KoolbaseAuthError {
|
|
170
|
+
constructor(message?: string);
|
|
171
|
+
}
|
|
172
|
+
/** A one-shot link clicked twice. Often means it already succeeded. */
|
|
173
|
+
export declare class TokenAlreadyUsedError extends KoolbaseAuthError {
|
|
174
|
+
constructor(message?: string);
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* The current password given for a password change was wrong.
|
|
178
|
+
*
|
|
179
|
+
* Named for the operation rather than the code: the server calls this
|
|
180
|
+
* `invalid_password`, which reads like a rejected new password and is not.
|
|
181
|
+
*/
|
|
182
|
+
export declare class CurrentPasswordIncorrectError extends KoolbaseAuthError {
|
|
183
|
+
constructor(message?: string);
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* An OAuth sign-in for an email that already has an account by another
|
|
187
|
+
* method. The remedy is to sign in the original way and connect the provider
|
|
188
|
+
* afterwards, which the server's message says.
|
|
189
|
+
*/
|
|
190
|
+
export declare class AccountExistsError extends KoolbaseAuthError {
|
|
191
|
+
constructor(message?: string);
|
|
192
|
+
}
|
|
193
|
+
/** A password attempt against an account that only has Google or Apple. */
|
|
194
|
+
export declare class OAuthOnlyAccountError extends KoolbaseAuthError {
|
|
195
|
+
constructor(message?: string);
|
|
196
|
+
}
|
|
197
|
+
/** A provider the project has not enabled. */
|
|
198
|
+
export declare class UnsupportedOAuthProviderError extends KoolbaseAuthError {
|
|
199
|
+
constructor(message?: string);
|
|
200
|
+
}
|
|
201
|
+
/** The call needs a signed-in user and did not have one. */
|
|
202
|
+
export declare class SessionRequiredError extends KoolbaseAuthError {
|
|
203
|
+
constructor(message?: string);
|
|
204
|
+
}
|
|
205
|
+
/** The caller is authenticated but not permitted to do this. */
|
|
206
|
+
export declare class InsufficientAuthorityError extends KoolbaseAuthError {
|
|
207
|
+
constructor(message?: string);
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Refused because it would remove the account's last way of signing in —
|
|
211
|
+
* unlinking the only provider, or clearing the only password.
|
|
212
|
+
*/
|
|
213
|
+
export declare class LastCredentialError extends KoolbaseAuthError {
|
|
214
|
+
constructor(message?: string);
|
|
215
|
+
}
|
|
216
|
+
/** Hiding account existence needs verification on; the project has it off. */
|
|
217
|
+
export declare class HideRequiresVerificationError extends KoolbaseAuthError {
|
|
218
|
+
constructor(message?: string);
|
|
219
|
+
}
|
|
220
|
+
export declare class MalformedSessionResponseError extends KoolbaseAuthError {
|
|
221
|
+
constructor(missing: string);
|
|
222
|
+
}
|
|
131
223
|
export declare class InvalidGoogleTokenError extends KoolbaseAuthError {
|
|
132
224
|
constructor();
|
|
133
225
|
}
|
package/dist/esm/auth-errors.js
CHANGED
|
@@ -34,8 +34,14 @@ export class UserDisabledError extends KoolbaseAuthError {
|
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
export class WeakPasswordError extends KoolbaseAuthError {
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
/**
|
|
38
|
+
* The message is optional so the server's own rule can be carried through.
|
|
39
|
+
* The SDK checks length before sending; a project may require more than
|
|
40
|
+
* that, and "must be at least 8 characters" would then be wrong as well as
|
|
41
|
+
* unhelpful.
|
|
42
|
+
*/
|
|
43
|
+
constructor(message) {
|
|
44
|
+
super(message ?? 'Password must be at least 8 characters', 'weak_password');
|
|
39
45
|
this.name = 'WeakPasswordError';
|
|
40
46
|
Object.setPrototypeOf(this, WeakPasswordError.prototype);
|
|
41
47
|
}
|
|
@@ -233,6 +239,144 @@ export class GoogleSignInNotConfiguredError extends KoolbaseAuthError {
|
|
|
233
239
|
Object.setPrototypeOf(this, GoogleSignInNotConfiguredError.prototype);
|
|
234
240
|
}
|
|
235
241
|
}
|
|
242
|
+
/**
|
|
243
|
+
* The server answered a sign-in as though it succeeded, without the tokens a
|
|
244
|
+
* session needs.
|
|
245
|
+
*
|
|
246
|
+
* Distinct from verification_required, which is a legitimate session-less
|
|
247
|
+
* success the SDK reports through SignUpResult. This is the other case: the
|
|
248
|
+
* response claims authentication and cannot support it. Inventing a session
|
|
249
|
+
* from it is what produced a signed-in user whose every request went out as
|
|
250
|
+
* `Bearer undefined`, so the SDK refuses instead.
|
|
251
|
+
*/
|
|
252
|
+
/**
|
|
253
|
+
* Sign-in refused because the account has not verified its email or phone.
|
|
254
|
+
*
|
|
255
|
+
* In a project with require_verified_contact on, this is the most common auth
|
|
256
|
+
* error there is — the user registered, did not click the link, and came back.
|
|
257
|
+
* It arrived untyped until 11.1.0, so apps showed "something went wrong" for
|
|
258
|
+
* the one case with an obvious remedy: offer to resend.
|
|
259
|
+
*/
|
|
260
|
+
export class ContactNotVerifiedError extends KoolbaseAuthError {
|
|
261
|
+
constructor(message) {
|
|
262
|
+
super(message ?? 'Verify your email or phone before signing in', 'contact_not_verified');
|
|
263
|
+
this.name = 'ContactNotVerifiedError';
|
|
264
|
+
Object.setPrototypeOf(this, ContactNotVerifiedError.prototype);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
/** The project has registration turned off. Not a credential problem. */
|
|
268
|
+
export class SignupsDisabledError extends KoolbaseAuthError {
|
|
269
|
+
constructor(message) {
|
|
270
|
+
super(message ?? 'Registration is disabled for this project', 'signups_disabled');
|
|
271
|
+
this.name = 'SignupsDisabledError';
|
|
272
|
+
Object.setPrototypeOf(this, SignupsDisabledError.prototype);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* A verification or reset link that has expired.
|
|
277
|
+
*
|
|
278
|
+
* Distinct from TokenAlreadyUsedError because the remedy differs: expired
|
|
279
|
+
* means request another, used means it already worked and they may simply be
|
|
280
|
+
* clicking an old email.
|
|
281
|
+
*/
|
|
282
|
+
export class TokenExpiredError extends KoolbaseAuthError {
|
|
283
|
+
constructor(message) {
|
|
284
|
+
super(message ?? 'This link has expired — request a new one', 'token_expired');
|
|
285
|
+
this.name = 'TokenExpiredError';
|
|
286
|
+
Object.setPrototypeOf(this, TokenExpiredError.prototype);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
/** A one-shot link clicked twice. Often means it already succeeded. */
|
|
290
|
+
export class TokenAlreadyUsedError extends KoolbaseAuthError {
|
|
291
|
+
constructor(message) {
|
|
292
|
+
super(message ?? 'This link has already been used', 'token_used');
|
|
293
|
+
this.name = 'TokenAlreadyUsedError';
|
|
294
|
+
Object.setPrototypeOf(this, TokenAlreadyUsedError.prototype);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* The current password given for a password change was wrong.
|
|
299
|
+
*
|
|
300
|
+
* Named for the operation rather than the code: the server calls this
|
|
301
|
+
* `invalid_password`, which reads like a rejected new password and is not.
|
|
302
|
+
*/
|
|
303
|
+
export class CurrentPasswordIncorrectError extends KoolbaseAuthError {
|
|
304
|
+
constructor(message) {
|
|
305
|
+
super(message ?? 'Current password is incorrect', 'invalid_password');
|
|
306
|
+
this.name = 'CurrentPasswordIncorrectError';
|
|
307
|
+
Object.setPrototypeOf(this, CurrentPasswordIncorrectError.prototype);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* An OAuth sign-in for an email that already has an account by another
|
|
312
|
+
* method. The remedy is to sign in the original way and connect the provider
|
|
313
|
+
* afterwards, which the server's message says.
|
|
314
|
+
*/
|
|
315
|
+
export class AccountExistsError extends KoolbaseAuthError {
|
|
316
|
+
constructor(message) {
|
|
317
|
+
super(message ?? 'An account with this email already exists — sign in with your existing method first', 'account_exists');
|
|
318
|
+
this.name = 'AccountExistsError';
|
|
319
|
+
Object.setPrototypeOf(this, AccountExistsError.prototype);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
/** A password attempt against an account that only has Google or Apple. */
|
|
323
|
+
export class OAuthOnlyAccountError extends KoolbaseAuthError {
|
|
324
|
+
constructor(message) {
|
|
325
|
+
super(message ?? 'This account uses Google or Apple sign-in and has no password', 'oauth_only_account');
|
|
326
|
+
this.name = 'OAuthOnlyAccountError';
|
|
327
|
+
Object.setPrototypeOf(this, OAuthOnlyAccountError.prototype);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
/** A provider the project has not enabled. */
|
|
331
|
+
export class UnsupportedOAuthProviderError extends KoolbaseAuthError {
|
|
332
|
+
constructor(message) {
|
|
333
|
+
super(message ?? 'That sign-in provider is not supported', 'unsupported_oauth_provider');
|
|
334
|
+
this.name = 'UnsupportedOAuthProviderError';
|
|
335
|
+
Object.setPrototypeOf(this, UnsupportedOAuthProviderError.prototype);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
/** The call needs a signed-in user and did not have one. */
|
|
339
|
+
export class SessionRequiredError extends KoolbaseAuthError {
|
|
340
|
+
constructor(message) {
|
|
341
|
+
super(message ?? 'This action requires a signed-in user', 'session_required');
|
|
342
|
+
this.name = 'SessionRequiredError';
|
|
343
|
+
Object.setPrototypeOf(this, SessionRequiredError.prototype);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
/** The caller is authenticated but not permitted to do this. */
|
|
347
|
+
export class InsufficientAuthorityError extends KoolbaseAuthError {
|
|
348
|
+
constructor(message) {
|
|
349
|
+
super(message ?? 'You do not have permission to do that', 'insufficient_authority');
|
|
350
|
+
this.name = 'InsufficientAuthorityError';
|
|
351
|
+
Object.setPrototypeOf(this, InsufficientAuthorityError.prototype);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Refused because it would remove the account's last way of signing in —
|
|
356
|
+
* unlinking the only provider, or clearing the only password.
|
|
357
|
+
*/
|
|
358
|
+
export class LastCredentialError extends KoolbaseAuthError {
|
|
359
|
+
constructor(message) {
|
|
360
|
+
super(message ?? 'This is the account\'s only sign-in method and cannot be removed', 'last_credential');
|
|
361
|
+
this.name = 'LastCredentialError';
|
|
362
|
+
Object.setPrototypeOf(this, LastCredentialError.prototype);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
/** Hiding account existence needs verification on; the project has it off. */
|
|
366
|
+
export class HideRequiresVerificationError extends KoolbaseAuthError {
|
|
367
|
+
constructor(message) {
|
|
368
|
+
super(message ?? 'Hiding account existence requires verified contact to be enabled', 'hide_requires_verification');
|
|
369
|
+
this.name = 'HideRequiresVerificationError';
|
|
370
|
+
Object.setPrototypeOf(this, HideRequiresVerificationError.prototype);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
export class MalformedSessionResponseError extends KoolbaseAuthError {
|
|
374
|
+
constructor(missing) {
|
|
375
|
+
super(`The server returned a session without ${missing}. This is a protocol error, not a credential problem.`, 'malformed_session_response');
|
|
376
|
+
this.name = 'MalformedSessionResponseError';
|
|
377
|
+
Object.setPrototypeOf(this, MalformedSessionResponseError.prototype);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
236
380
|
export class InvalidGoogleTokenError extends KoolbaseAuthError {
|
|
237
381
|
constructor() {
|
|
238
382
|
super('Invalid Google identity token', 'invalid_google_token');
|
package/dist/esm/auth.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AuthStateListener, KoolbaseConfig, KoolbaseSession, KoolbaseUser, LinkPhoneParams, LoginParams, OtpSendResult, PhoneVerifyResult, RegisterParams, RestoreResult, ResendVerificationResult, SendOtpParams, SignInWithAppleParams, VerifyOtpParams } from './types.js';
|
|
1
|
+
import { AuthStateListener, KoolbaseConfig, KoolbaseSession, KoolbaseUser, LinkPhoneParams, LoginParams, OtpSendResult, PhoneVerifyResult, RegisterParams, RestoreResult, ResendVerificationResult, SendOtpParams, SignUpResult, SignInWithAppleParams, VerifyOtpParams } from './types.js';
|
|
2
2
|
import type { SignInWithGoogleParams } from './types.js';
|
|
3
3
|
export declare class KoolbaseAuth {
|
|
4
4
|
private config;
|
|
@@ -72,7 +72,19 @@ export declare class KoolbaseAuth {
|
|
|
72
72
|
clearStoredSession(): Promise<void>;
|
|
73
73
|
private clearSessionInternal;
|
|
74
74
|
restoreSession(): Promise<RestoreResult>;
|
|
75
|
-
|
|
75
|
+
/**
|
|
76
|
+
* Create an account.
|
|
77
|
+
*
|
|
78
|
+
* Two outcomes, and the caller must tell them apart. With
|
|
79
|
+
* require_verified_contact off, the account is created and signed in.
|
|
80
|
+
* With it on, the account is created and NO session is issued — the user
|
|
81
|
+
* verifies their email before their first sign-in. Both are successes.
|
|
82
|
+
*
|
|
83
|
+
* Switch on `status` rather than checking the session for null: the point
|
|
84
|
+
* of the union is that there is no path where an app reads the user and
|
|
85
|
+
* assumes it is signed in.
|
|
86
|
+
*/
|
|
87
|
+
register(params: RegisterParams): Promise<SignUpResult>;
|
|
76
88
|
login(params: LoginParams): Promise<KoolbaseSession>;
|
|
77
89
|
/**
|
|
78
90
|
* Sign in with Apple using a credential obtained from a native Apple
|
|
@@ -212,6 +224,18 @@ export declare class KoolbaseAuth {
|
|
|
212
224
|
* affects how a bare 401 (no code, older server) is interpreted.
|
|
213
225
|
*/
|
|
214
226
|
private parseSessionResponse;
|
|
227
|
+
/**
|
|
228
|
+
* A session, or a refusal — never a session-shaped object with nothing in
|
|
229
|
+
* it.
|
|
230
|
+
*
|
|
231
|
+
* This used to read the fields straight off the body, so a response with no
|
|
232
|
+
* tokens produced a session whose accessToken was undefined. It persisted,
|
|
233
|
+
* currentUser returned a user, and every authenticated request went out as
|
|
234
|
+
* `Bearer undefined` and came back 401 — signed in as far as the app could
|
|
235
|
+
* tell, and unable to do anything. A user object without tokens is not a
|
|
236
|
+
* session, and refusing is the only honest answer.
|
|
237
|
+
*/
|
|
238
|
+
private sessionFromBody;
|
|
215
239
|
private checkResponse;
|
|
216
240
|
/**
|
|
217
241
|
* Map a non-2xx credential/session response to a typed error.
|
package/dist/esm/auth.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { RestoreResult, } from './types.js';
|
|
2
|
-
import { AccountLockedError, EmailAlreadyInUseError, InvalidCredentialsError, InvalidPhoneNumberError, KoolbaseAuthError, OtpExpiredError, OtpInvalidError, OtpMaxAttemptsError, OtpRateLimitError, PhoneAlreadyLinkedError, RateLimitError, VerificationResendCooldownError, VerificationResendDailyCapError, SessionExpiredError, SmsConfigMissingError, TokenRevokedError, UnlockTokenInvalidError, UserDisabledError, WeakPasswordError, AppleEmailRequiredError, AppleSignInNotConfiguredError, InvalidAppleTokenError, OAuthEmailConflictError, GoogleEmailRequiredError, GoogleSignInNotConfiguredError, InvalidGoogleTokenError, } from './auth-errors.js';
|
|
2
|
+
import { AccountLockedError, EmailAlreadyInUseError, InvalidCredentialsError, InvalidPhoneNumberError, KoolbaseAuthError, OtpExpiredError, OtpInvalidError, OtpMaxAttemptsError, OtpRateLimitError, PhoneAlreadyLinkedError, MalformedSessionResponseError, AccountExistsError, ContactNotVerifiedError, CurrentPasswordIncorrectError, HideRequiresVerificationError, InsufficientAuthorityError, LastCredentialError, OAuthOnlyAccountError, SessionRequiredError, SignupsDisabledError, TokenAlreadyUsedError, TokenExpiredError, UnsupportedOAuthProviderError, RateLimitError, VerificationResendCooldownError, VerificationResendDailyCapError, SessionExpiredError, SmsConfigMissingError, TokenRevokedError, UnlockTokenInvalidError, UserDisabledError, WeakPasswordError, AppleEmailRequiredError, AppleSignInNotConfiguredError, InvalidAppleTokenError, OAuthEmailConflictError, GoogleEmailRequiredError, GoogleSignInNotConfiguredError, InvalidGoogleTokenError, } from './auth-errors.js';
|
|
3
3
|
import { getPlatform } from './platform.js';
|
|
4
4
|
import { DeviceMetadata } from './device-metadata.js';
|
|
5
5
|
export class KoolbaseAuth {
|
|
@@ -205,6 +205,18 @@ export class KoolbaseAuth {
|
|
|
205
205
|
}
|
|
206
206
|
}
|
|
207
207
|
// ─── Public auth API ────────────────────────────────────────────────────
|
|
208
|
+
/**
|
|
209
|
+
* Create an account.
|
|
210
|
+
*
|
|
211
|
+
* Two outcomes, and the caller must tell them apart. With
|
|
212
|
+
* require_verified_contact off, the account is created and signed in.
|
|
213
|
+
* With it on, the account is created and NO session is issued — the user
|
|
214
|
+
* verifies their email before their first sign-in. Both are successes.
|
|
215
|
+
*
|
|
216
|
+
* Switch on `status` rather than checking the session for null: the point
|
|
217
|
+
* of the union is that there is no path where an app reads the user and
|
|
218
|
+
* assumes it is signed in.
|
|
219
|
+
*/
|
|
208
220
|
async register(params) {
|
|
209
221
|
if (params.password.length < 8)
|
|
210
222
|
throw new WeakPasswordError();
|
|
@@ -212,9 +224,25 @@ export class KoolbaseAuth {
|
|
|
212
224
|
method: 'POST',
|
|
213
225
|
body: params,
|
|
214
226
|
});
|
|
215
|
-
|
|
227
|
+
if (!res.ok)
|
|
228
|
+
await this.throwTypedError(res); // never returns
|
|
229
|
+
const data = await res.json();
|
|
230
|
+
// The server's own discriminator. It sends this deliberately — a 201
|
|
231
|
+
// meaning "created, not signed in" — and the SDK ignored it until now,
|
|
232
|
+
// building a session out of a body with no tokens in it.
|
|
233
|
+
if (data.verification_required === true) {
|
|
234
|
+
// Nothing is persisted and no listener fires: a pending signup is not
|
|
235
|
+
// an authentication event, and an existing session on this device
|
|
236
|
+
// belongs to whoever was already signed in.
|
|
237
|
+
return {
|
|
238
|
+
status: 'verification_required',
|
|
239
|
+
user: this.mapUser(data.user),
|
|
240
|
+
session: null,
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
const session = this.sessionFromBody(data);
|
|
216
244
|
await this.setSessionInternal(session);
|
|
217
|
-
return session.user;
|
|
245
|
+
return { status: 'authenticated', user: session.user, session };
|
|
218
246
|
}
|
|
219
247
|
async login(params) {
|
|
220
248
|
const res = await this.authRequest('/v1/sdk/auth/login', {
|
|
@@ -321,13 +349,7 @@ export class KoolbaseAuth {
|
|
|
321
349
|
*/
|
|
322
350
|
async parseGoogleSessionResponse(res) {
|
|
323
351
|
if (res.status === 200) {
|
|
324
|
-
|
|
325
|
-
return {
|
|
326
|
-
accessToken: data.access_token,
|
|
327
|
-
refreshToken: data.refresh_token,
|
|
328
|
-
expiresAt: data.expires_at,
|
|
329
|
-
user: this.mapUser(data.user),
|
|
330
|
-
};
|
|
352
|
+
return this.sessionFromBody(await res.json());
|
|
331
353
|
}
|
|
332
354
|
let body = {};
|
|
333
355
|
try {
|
|
@@ -381,13 +403,7 @@ export class KoolbaseAuth {
|
|
|
381
403
|
*/
|
|
382
404
|
async parseAppleSessionResponse(res) {
|
|
383
405
|
if (res.status === 200) {
|
|
384
|
-
|
|
385
|
-
return {
|
|
386
|
-
accessToken: data.access_token,
|
|
387
|
-
refreshToken: data.refresh_token,
|
|
388
|
-
expiresAt: data.expires_at,
|
|
389
|
-
user: this.mapUser(data.user),
|
|
390
|
-
};
|
|
406
|
+
return this.sessionFromBody(await res.json());
|
|
391
407
|
}
|
|
392
408
|
let body = {};
|
|
393
409
|
try {
|
|
@@ -710,7 +726,24 @@ export class KoolbaseAuth {
|
|
|
710
726
|
async parseSessionResponse(res, isRefresh) {
|
|
711
727
|
if (!res.ok)
|
|
712
728
|
await this.throwTypedError(res, isRefresh); // never returns
|
|
713
|
-
|
|
729
|
+
return this.sessionFromBody(await res.json());
|
|
730
|
+
}
|
|
731
|
+
/**
|
|
732
|
+
* A session, or a refusal — never a session-shaped object with nothing in
|
|
733
|
+
* it.
|
|
734
|
+
*
|
|
735
|
+
* This used to read the fields straight off the body, so a response with no
|
|
736
|
+
* tokens produced a session whose accessToken was undefined. It persisted,
|
|
737
|
+
* currentUser returned a user, and every authenticated request went out as
|
|
738
|
+
* `Bearer undefined` and came back 401 — signed in as far as the app could
|
|
739
|
+
* tell, and unable to do anything. A user object without tokens is not a
|
|
740
|
+
* session, and refusing is the only honest answer.
|
|
741
|
+
*/
|
|
742
|
+
sessionFromBody(data) {
|
|
743
|
+
if (!data?.access_token)
|
|
744
|
+
throw new MalformedSessionResponseError('an access token');
|
|
745
|
+
if (!data?.refresh_token)
|
|
746
|
+
throw new MalformedSessionResponseError('a refresh token');
|
|
714
747
|
return {
|
|
715
748
|
accessToken: data.access_token,
|
|
716
749
|
refreshToken: data.refresh_token,
|
|
@@ -761,6 +794,49 @@ export class KoolbaseAuth {
|
|
|
761
794
|
throw new UnlockTokenInvalidError();
|
|
762
795
|
case 'rate_limit':
|
|
763
796
|
throw new RateLimitError(msg || undefined);
|
|
797
|
+
// Verification state. Two codes, one situation — projectauth and the
|
|
798
|
+
// dashboard's auth package name it differently and an app should not
|
|
799
|
+
// have to know which spoke.
|
|
800
|
+
// Verification state. Two codes, one situation — projectauth and the
|
|
801
|
+
// dashboard's auth package name it differently and an app should not
|
|
802
|
+
// have to know which spoke.
|
|
803
|
+
case 'contact_not_verified':
|
|
804
|
+
case 'email_not_verified':
|
|
805
|
+
throw new ContactNotVerifiedError(msg || undefined);
|
|
806
|
+
// Registration
|
|
807
|
+
case 'signups_disabled':
|
|
808
|
+
throw new SignupsDisabledError(msg || undefined);
|
|
809
|
+
case 'weak_password':
|
|
810
|
+
// The class existed and was only ever thrown client-side for length.
|
|
811
|
+
// The server has its own rules, and a password that passes ours and
|
|
812
|
+
// fails theirs deserves the same error, not a generic one.
|
|
813
|
+
throw new WeakPasswordError(msg || undefined);
|
|
814
|
+
case 'account_exists':
|
|
815
|
+
throw new AccountExistsError(msg || undefined);
|
|
816
|
+
// Link tokens — verification and password reset
|
|
817
|
+
case 'token_expired':
|
|
818
|
+
throw new TokenExpiredError(msg || undefined);
|
|
819
|
+
case 'token_used':
|
|
820
|
+
throw new TokenAlreadyUsedError(msg || undefined);
|
|
821
|
+
case 'invalid_token':
|
|
822
|
+
throw new UnlockTokenInvalidError();
|
|
823
|
+
// Password change
|
|
824
|
+
case 'invalid_password':
|
|
825
|
+
throw new CurrentPasswordIncorrectError(msg || undefined);
|
|
826
|
+
// OAuth
|
|
827
|
+
case 'oauth_only_account':
|
|
828
|
+
throw new OAuthOnlyAccountError(msg || undefined);
|
|
829
|
+
case 'unsupported_oauth_provider':
|
|
830
|
+
throw new UnsupportedOAuthProviderError(msg || undefined);
|
|
831
|
+
// Authority
|
|
832
|
+
case 'session_required':
|
|
833
|
+
throw new SessionRequiredError(msg || undefined);
|
|
834
|
+
case 'insufficient_authority':
|
|
835
|
+
throw new InsufficientAuthorityError(msg || undefined);
|
|
836
|
+
case 'last_credential':
|
|
837
|
+
throw new LastCredentialError(msg || undefined);
|
|
838
|
+
case 'hide_requires_verification':
|
|
839
|
+
throw new HideRequiresVerificationError(msg || undefined);
|
|
764
840
|
case 'resend_cooldown':
|
|
765
841
|
throw new VerificationResendCooldownError(body.cooldown_until ? new Date(body.cooldown_until) : null, msg || undefined);
|
|
766
842
|
case 'resend_daily_cap':
|
package/dist/esm/types.d.ts
CHANGED
|
@@ -63,6 +63,31 @@ export interface KoolbaseSession {
|
|
|
63
63
|
* cooldownUntil is when the next send becomes possible. The server throttles
|
|
64
64
|
* sends, and a countdown is a better answer to a user than a bare refusal.
|
|
65
65
|
*/
|
|
66
|
+
/**
|
|
67
|
+
* What register() answers with.
|
|
68
|
+
*
|
|
69
|
+
* A discriminated union rather than a nullable session, because registration
|
|
70
|
+
* succeeding and authentication succeeding are different outcomes and an app
|
|
71
|
+
* must handle both. A project with require_verified_contact on creates the
|
|
72
|
+
* account and issues no session — the server returns 201 with
|
|
73
|
+
* verification_required, deliberately not an error, since reporting failure
|
|
74
|
+
* for a signup that worked is worse than either alternative.
|
|
75
|
+
*
|
|
76
|
+
* Until 10.x this was typed as the user alone, and the SDK built a session
|
|
77
|
+
* from a response that had no tokens: currentUser returned someone whose
|
|
78
|
+
* every request went out as `Bearer undefined`. A nullable session would
|
|
79
|
+
* have let an app read result.user and reproduce that at one remove, so the
|
|
80
|
+
* status is the only way in.
|
|
81
|
+
*/
|
|
82
|
+
export type SignUpResult = {
|
|
83
|
+
status: 'authenticated';
|
|
84
|
+
user: KoolbaseUser;
|
|
85
|
+
session: KoolbaseSession;
|
|
86
|
+
} | {
|
|
87
|
+
status: 'verification_required';
|
|
88
|
+
user: KoolbaseUser;
|
|
89
|
+
session: null;
|
|
90
|
+
};
|
|
66
91
|
export interface ResendVerificationResult {
|
|
67
92
|
alreadyVerified: boolean;
|
|
68
93
|
/** When the link in the email stops working. Null when nothing was sent. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@koolbase/core",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "11.1.0",
|
|
4
4
|
"description": "Koolbase SDK core \u2014 shared behaviour behind @koolbase/react-native and @koolbase/js. Install one of those, not this.",
|
|
5
5
|
"main": "./dist/cjs/index.js",
|
|
6
6
|
"types": "./dist/esm/index.d.ts",
|