@koolbase/core 10.3.0 → 11.0.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 +32 -0
- package/dist/cjs/auth-errors.js +48 -1
- package/dist/cjs/auth.d.ts +47 -2
- package/dist/cjs/auth.js +93 -17
- package/dist/cjs/index.d.ts +1 -1
- package/dist/cjs/types.d.ts +42 -0
- package/dist/esm/auth-errors.d.ts +32 -0
- package/dist/esm/auth-errors.js +44 -0
- package/dist/esm/auth.d.ts +47 -2
- package/dist/esm/auth.js +94 -18
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/types.d.ts +42 -0
- package/package.json +1 -1
|
@@ -64,6 +64,25 @@ export declare class UnlockTokenInvalidError extends KoolbaseAuthError {
|
|
|
64
64
|
export declare class RateLimitError extends KoolbaseAuthError {
|
|
65
65
|
constructor(message?: string);
|
|
66
66
|
}
|
|
67
|
+
/**
|
|
68
|
+
* The verification email was refused because one was sent too recently.
|
|
69
|
+
*
|
|
70
|
+
* Carries when the next send becomes possible, so an app can show a countdown
|
|
71
|
+
* rather than a bare refusal — the server sends the timestamp and throwing it
|
|
72
|
+
* away would waste the only thing that makes this error actionable.
|
|
73
|
+
*/
|
|
74
|
+
export declare class VerificationResendCooldownError extends KoolbaseAuthError {
|
|
75
|
+
readonly cooldownUntil: Date | null;
|
|
76
|
+
constructor(cooldownUntil: Date | null, message?: string);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* The day's verification-email allowance is spent. Distinct from the cooldown
|
|
80
|
+
* because the remedy differs: waiting seconds versus waiting until tomorrow,
|
|
81
|
+
* and an app should say which.
|
|
82
|
+
*/
|
|
83
|
+
export declare class VerificationResendDailyCapError extends KoolbaseAuthError {
|
|
84
|
+
constructor(message?: string);
|
|
85
|
+
}
|
|
67
86
|
/**
|
|
68
87
|
* Generic network error. The SDK does NOT throw this directly — fetch
|
|
69
88
|
* failures (DNS, no connection, timeout) propagate as native TypeErrors.
|
|
@@ -109,6 +128,19 @@ export declare class OAuthEmailConflictError extends KoolbaseAuthError {
|
|
|
109
128
|
export declare class GoogleSignInNotConfiguredError extends KoolbaseAuthError {
|
|
110
129
|
constructor();
|
|
111
130
|
}
|
|
131
|
+
/**
|
|
132
|
+
* The server answered a sign-in as though it succeeded, without the tokens a
|
|
133
|
+
* session needs.
|
|
134
|
+
*
|
|
135
|
+
* Distinct from verification_required, which is a legitimate session-less
|
|
136
|
+
* success the SDK reports through SignUpResult. This is the other case: the
|
|
137
|
+
* response claims authentication and cannot support it. Inventing a session
|
|
138
|
+
* from it is what produced a signed-in user whose every request went out as
|
|
139
|
+
* `Bearer undefined`, so the SDK refuses instead.
|
|
140
|
+
*/
|
|
141
|
+
export declare class MalformedSessionResponseError extends KoolbaseAuthError {
|
|
142
|
+
constructor(missing: string);
|
|
143
|
+
}
|
|
112
144
|
export declare class InvalidGoogleTokenError extends KoolbaseAuthError {
|
|
113
145
|
constructor();
|
|
114
146
|
}
|
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.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.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
|
|
@@ -120,6 +120,35 @@ class RateLimitError extends KoolbaseAuthError {
|
|
|
120
120
|
}
|
|
121
121
|
}
|
|
122
122
|
exports.RateLimitError = RateLimitError;
|
|
123
|
+
/**
|
|
124
|
+
* The verification email was refused because one was sent too recently.
|
|
125
|
+
*
|
|
126
|
+
* Carries when the next send becomes possible, so an app can show a countdown
|
|
127
|
+
* rather than a bare refusal — the server sends the timestamp and throwing it
|
|
128
|
+
* away would waste the only thing that makes this error actionable.
|
|
129
|
+
*/
|
|
130
|
+
class VerificationResendCooldownError extends KoolbaseAuthError {
|
|
131
|
+
constructor(cooldownUntil, message) {
|
|
132
|
+
super(message ?? 'Please wait before requesting another verification email', 'resend_cooldown');
|
|
133
|
+
this.cooldownUntil = cooldownUntil;
|
|
134
|
+
this.name = 'VerificationResendCooldownError';
|
|
135
|
+
Object.setPrototypeOf(this, VerificationResendCooldownError.prototype);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
exports.VerificationResendCooldownError = VerificationResendCooldownError;
|
|
139
|
+
/**
|
|
140
|
+
* The day's verification-email allowance is spent. Distinct from the cooldown
|
|
141
|
+
* because the remedy differs: waiting seconds versus waiting until tomorrow,
|
|
142
|
+
* and an app should say which.
|
|
143
|
+
*/
|
|
144
|
+
class VerificationResendDailyCapError extends KoolbaseAuthError {
|
|
145
|
+
constructor(message) {
|
|
146
|
+
super(message ?? 'Daily verification-email limit reached; try again tomorrow', 'resend_daily_cap');
|
|
147
|
+
this.name = 'VerificationResendDailyCapError';
|
|
148
|
+
Object.setPrototypeOf(this, VerificationResendDailyCapError.prototype);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
exports.VerificationResendDailyCapError = VerificationResendDailyCapError;
|
|
123
152
|
// ─── Network ───────────────────────────────────────────────────────────────
|
|
124
153
|
/**
|
|
125
154
|
* Generic network error. The SDK does NOT throw this directly — fetch
|
|
@@ -232,6 +261,24 @@ class GoogleSignInNotConfiguredError extends KoolbaseAuthError {
|
|
|
232
261
|
}
|
|
233
262
|
}
|
|
234
263
|
exports.GoogleSignInNotConfiguredError = GoogleSignInNotConfiguredError;
|
|
264
|
+
/**
|
|
265
|
+
* The server answered a sign-in as though it succeeded, without the tokens a
|
|
266
|
+
* session needs.
|
|
267
|
+
*
|
|
268
|
+
* Distinct from verification_required, which is a legitimate session-less
|
|
269
|
+
* success the SDK reports through SignUpResult. This is the other case: the
|
|
270
|
+
* response claims authentication and cannot support it. Inventing a session
|
|
271
|
+
* from it is what produced a signed-in user whose every request went out as
|
|
272
|
+
* `Bearer undefined`, so the SDK refuses instead.
|
|
273
|
+
*/
|
|
274
|
+
class MalformedSessionResponseError extends KoolbaseAuthError {
|
|
275
|
+
constructor(missing) {
|
|
276
|
+
super(`The server returned a session without ${missing}. This is a protocol error, not a credential problem.`, 'malformed_session_response');
|
|
277
|
+
this.name = 'MalformedSessionResponseError';
|
|
278
|
+
Object.setPrototypeOf(this, MalformedSessionResponseError.prototype);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
exports.MalformedSessionResponseError = MalformedSessionResponseError;
|
|
235
282
|
class InvalidGoogleTokenError extends KoolbaseAuthError {
|
|
236
283
|
constructor() {
|
|
237
284
|
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, 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
|
|
@@ -143,6 +155,27 @@ export declare class KoolbaseAuth {
|
|
|
143
155
|
logout(): Promise<boolean>;
|
|
144
156
|
forgotPassword(email: string): Promise<void>;
|
|
145
157
|
resetPassword(token: string, password: string): Promise<void>;
|
|
158
|
+
/**
|
|
159
|
+
* Complete email verification with a token from a verification link.
|
|
160
|
+
*
|
|
161
|
+
* The token comes from wherever your verification URL template pointed —
|
|
162
|
+
* your own page reads it from the query string and passes it here. Throws
|
|
163
|
+
* if the token is invalid, expired or already used.
|
|
164
|
+
*/
|
|
165
|
+
verifyEmail(token: string): Promise<void>;
|
|
166
|
+
/**
|
|
167
|
+
* Re-send the verification email to the signed-in but unverified user.
|
|
168
|
+
*
|
|
169
|
+
* Safe to call when already verified: nothing is sent and
|
|
170
|
+
* `alreadyVerified` comes back true. The server throttles this — a short
|
|
171
|
+
* cooldown between sends and a daily cap — and the refusal arrives as
|
|
172
|
+
* VerificationResendCooldownError carrying when to retry, or
|
|
173
|
+
* VerificationResendDailyCapError when the day's allowance is spent.
|
|
174
|
+
*
|
|
175
|
+
* Requires a session, since the server verifies the caller rather than
|
|
176
|
+
* taking an email address — otherwise this would be an open mail relay.
|
|
177
|
+
*/
|
|
178
|
+
resendVerificationEmail(): Promise<ResendVerificationResult>;
|
|
146
179
|
unlock(token: string): Promise<void>;
|
|
147
180
|
get currentUser(): KoolbaseUser | null;
|
|
148
181
|
get accessToken(): string | null;
|
|
@@ -191,6 +224,18 @@ export declare class KoolbaseAuth {
|
|
|
191
224
|
* affects how a bare 401 (no code, older server) is interpreted.
|
|
192
225
|
*/
|
|
193
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;
|
|
194
239
|
private checkResponse;
|
|
195
240
|
/**
|
|
196
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 {
|
|
@@ -503,6 +519,45 @@ class KoolbaseAuth {
|
|
|
503
519
|
});
|
|
504
520
|
await this.checkResponse(res);
|
|
505
521
|
}
|
|
522
|
+
/**
|
|
523
|
+
* Complete email verification with a token from a verification link.
|
|
524
|
+
*
|
|
525
|
+
* The token comes from wherever your verification URL template pointed —
|
|
526
|
+
* your own page reads it from the query string and passes it here. Throws
|
|
527
|
+
* if the token is invalid, expired or already used.
|
|
528
|
+
*/
|
|
529
|
+
async verifyEmail(token) {
|
|
530
|
+
const res = await this.authRequest('/v1/sdk/auth/verify-email', {
|
|
531
|
+
method: 'POST',
|
|
532
|
+
body: { token },
|
|
533
|
+
});
|
|
534
|
+
await this.checkResponse(res);
|
|
535
|
+
}
|
|
536
|
+
/**
|
|
537
|
+
* Re-send the verification email to the signed-in but unverified user.
|
|
538
|
+
*
|
|
539
|
+
* Safe to call when already verified: nothing is sent and
|
|
540
|
+
* `alreadyVerified` comes back true. The server throttles this — a short
|
|
541
|
+
* cooldown between sends and a daily cap — and the refusal arrives as
|
|
542
|
+
* VerificationResendCooldownError carrying when to retry, or
|
|
543
|
+
* VerificationResendDailyCapError when the day's allowance is spent.
|
|
544
|
+
*
|
|
545
|
+
* Requires a session, since the server verifies the caller rather than
|
|
546
|
+
* taking an email address — otherwise this would be an open mail relay.
|
|
547
|
+
*/
|
|
548
|
+
async resendVerificationEmail() {
|
|
549
|
+
const res = await this.authRequest('/v1/sdk/auth/resend-verification', {
|
|
550
|
+
method: 'POST',
|
|
551
|
+
includeAuth: true,
|
|
552
|
+
});
|
|
553
|
+
await this.checkResponse(res);
|
|
554
|
+
const body = (await res.json().catch(() => ({})));
|
|
555
|
+
return {
|
|
556
|
+
alreadyVerified: body.already_verified ?? false,
|
|
557
|
+
expiresAt: body.expires_at ? new Date(body.expires_at) : null,
|
|
558
|
+
cooldownUntil: body.cooldown_until ? new Date(body.cooldown_until) : null,
|
|
559
|
+
};
|
|
560
|
+
}
|
|
506
561
|
async unlock(token) {
|
|
507
562
|
const res = await this.authRequest('/v1/sdk/auth/unlock', {
|
|
508
563
|
method: 'POST',
|
|
@@ -674,7 +729,24 @@ class KoolbaseAuth {
|
|
|
674
729
|
async parseSessionResponse(res, isRefresh) {
|
|
675
730
|
if (!res.ok)
|
|
676
731
|
await this.throwTypedError(res, isRefresh); // never returns
|
|
677
|
-
|
|
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');
|
|
678
750
|
return {
|
|
679
751
|
accessToken: data.access_token,
|
|
680
752
|
refreshToken: data.refresh_token,
|
|
@@ -725,6 +797,10 @@ class KoolbaseAuth {
|
|
|
725
797
|
throw new auth_errors_js_1.UnlockTokenInvalidError();
|
|
726
798
|
case 'rate_limit':
|
|
727
799
|
throw new auth_errors_js_1.RateLimitError(msg || undefined);
|
|
800
|
+
case 'resend_cooldown':
|
|
801
|
+
throw new auth_errors_js_1.VerificationResendCooldownError(body.cooldown_until ? new Date(body.cooldown_until) : null, msg || undefined);
|
|
802
|
+
case 'resend_daily_cap':
|
|
803
|
+
throw new auth_errors_js_1.VerificationResendDailyCapError(msg || undefined);
|
|
728
804
|
}
|
|
729
805
|
// ─── status fallback (pre-code servers) ───
|
|
730
806
|
if (res.status === 409)
|
package/dist/cjs/index.d.ts
CHANGED
|
@@ -18,5 +18,5 @@ export type { RegisterTokenOptions } from './messaging.js';
|
|
|
18
18
|
export { getOrCreateDeviceId } from './device-id.js';
|
|
19
19
|
export { koolbaseSdkVersion } from './device-metadata.js';
|
|
20
20
|
export { RestoreResult } from './types.js';
|
|
21
|
-
export type { AuthStateListener, FetchLike, KoolbaseAuthStorage } from './types.js';
|
|
21
|
+
export type { AuthStateListener, FetchLike, KoolbaseAuthStorage, ResendVerificationResult } from './types.js';
|
|
22
22
|
export { setPlatform, getPlatform, memoryPlatform, type PlatformAdapter, type PlatformStorage, type PlatformNetwork, type PlatformLifecycle, type PlatformLocks, type PlatformInfo, } from './platform.js';
|
package/dist/cjs/types.d.ts
CHANGED
|
@@ -53,6 +53,48 @@ export interface KoolbaseSession {
|
|
|
53
53
|
* unavailable), compliant encryption layers, or in-memory test mocks —
|
|
54
54
|
* can implement this interface and inject it via KoolbaseConfig.authStorage.
|
|
55
55
|
*/
|
|
56
|
+
/**
|
|
57
|
+
* What resendVerificationEmail() answers with.
|
|
58
|
+
*
|
|
59
|
+
* alreadyVerified is not an error: calling it for a verified account is a
|
|
60
|
+
* no-op that says so, which is what a "resend" button needs when the user
|
|
61
|
+
* verified in another tab.
|
|
62
|
+
*
|
|
63
|
+
* cooldownUntil is when the next send becomes possible. The server throttles
|
|
64
|
+
* sends, and a countdown is a better answer to a user than a bare refusal.
|
|
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
|
+
};
|
|
91
|
+
export interface ResendVerificationResult {
|
|
92
|
+
alreadyVerified: boolean;
|
|
93
|
+
/** When the link in the email stops working. Null when nothing was sent. */
|
|
94
|
+
expiresAt: Date | null;
|
|
95
|
+
/** When another send becomes possible. Null when nothing was sent. */
|
|
96
|
+
cooldownUntil: Date | null;
|
|
97
|
+
}
|
|
56
98
|
export interface KoolbaseAuthStorage {
|
|
57
99
|
saveSession(session: KoolbaseSession): Promise<void>;
|
|
58
100
|
readSession(): Promise<KoolbaseSession | null>;
|
|
@@ -64,6 +64,25 @@ export declare class UnlockTokenInvalidError extends KoolbaseAuthError {
|
|
|
64
64
|
export declare class RateLimitError extends KoolbaseAuthError {
|
|
65
65
|
constructor(message?: string);
|
|
66
66
|
}
|
|
67
|
+
/**
|
|
68
|
+
* The verification email was refused because one was sent too recently.
|
|
69
|
+
*
|
|
70
|
+
* Carries when the next send becomes possible, so an app can show a countdown
|
|
71
|
+
* rather than a bare refusal — the server sends the timestamp and throwing it
|
|
72
|
+
* away would waste the only thing that makes this error actionable.
|
|
73
|
+
*/
|
|
74
|
+
export declare class VerificationResendCooldownError extends KoolbaseAuthError {
|
|
75
|
+
readonly cooldownUntil: Date | null;
|
|
76
|
+
constructor(cooldownUntil: Date | null, message?: string);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* The day's verification-email allowance is spent. Distinct from the cooldown
|
|
80
|
+
* because the remedy differs: waiting seconds versus waiting until tomorrow,
|
|
81
|
+
* and an app should say which.
|
|
82
|
+
*/
|
|
83
|
+
export declare class VerificationResendDailyCapError extends KoolbaseAuthError {
|
|
84
|
+
constructor(message?: string);
|
|
85
|
+
}
|
|
67
86
|
/**
|
|
68
87
|
* Generic network error. The SDK does NOT throw this directly — fetch
|
|
69
88
|
* failures (DNS, no connection, timeout) propagate as native TypeErrors.
|
|
@@ -109,6 +128,19 @@ export declare class OAuthEmailConflictError extends KoolbaseAuthError {
|
|
|
109
128
|
export declare class GoogleSignInNotConfiguredError extends KoolbaseAuthError {
|
|
110
129
|
constructor();
|
|
111
130
|
}
|
|
131
|
+
/**
|
|
132
|
+
* The server answered a sign-in as though it succeeded, without the tokens a
|
|
133
|
+
* session needs.
|
|
134
|
+
*
|
|
135
|
+
* Distinct from verification_required, which is a legitimate session-less
|
|
136
|
+
* success the SDK reports through SignUpResult. This is the other case: the
|
|
137
|
+
* response claims authentication and cannot support it. Inventing a session
|
|
138
|
+
* from it is what produced a signed-in user whose every request went out as
|
|
139
|
+
* `Bearer undefined`, so the SDK refuses instead.
|
|
140
|
+
*/
|
|
141
|
+
export declare class MalformedSessionResponseError extends KoolbaseAuthError {
|
|
142
|
+
constructor(missing: string);
|
|
143
|
+
}
|
|
112
144
|
export declare class InvalidGoogleTokenError extends KoolbaseAuthError {
|
|
113
145
|
constructor();
|
|
114
146
|
}
|
package/dist/esm/auth-errors.js
CHANGED
|
@@ -107,6 +107,33 @@ export class RateLimitError extends KoolbaseAuthError {
|
|
|
107
107
|
Object.setPrototypeOf(this, RateLimitError.prototype);
|
|
108
108
|
}
|
|
109
109
|
}
|
|
110
|
+
/**
|
|
111
|
+
* The verification email was refused because one was sent too recently.
|
|
112
|
+
*
|
|
113
|
+
* Carries when the next send becomes possible, so an app can show a countdown
|
|
114
|
+
* rather than a bare refusal — the server sends the timestamp and throwing it
|
|
115
|
+
* away would waste the only thing that makes this error actionable.
|
|
116
|
+
*/
|
|
117
|
+
export class VerificationResendCooldownError extends KoolbaseAuthError {
|
|
118
|
+
constructor(cooldownUntil, message) {
|
|
119
|
+
super(message ?? 'Please wait before requesting another verification email', 'resend_cooldown');
|
|
120
|
+
this.cooldownUntil = cooldownUntil;
|
|
121
|
+
this.name = 'VerificationResendCooldownError';
|
|
122
|
+
Object.setPrototypeOf(this, VerificationResendCooldownError.prototype);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* The day's verification-email allowance is spent. Distinct from the cooldown
|
|
127
|
+
* because the remedy differs: waiting seconds versus waiting until tomorrow,
|
|
128
|
+
* and an app should say which.
|
|
129
|
+
*/
|
|
130
|
+
export class VerificationResendDailyCapError extends KoolbaseAuthError {
|
|
131
|
+
constructor(message) {
|
|
132
|
+
super(message ?? 'Daily verification-email limit reached; try again tomorrow', 'resend_daily_cap');
|
|
133
|
+
this.name = 'VerificationResendDailyCapError';
|
|
134
|
+
Object.setPrototypeOf(this, VerificationResendDailyCapError.prototype);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
110
137
|
// ─── Network ───────────────────────────────────────────────────────────────
|
|
111
138
|
/**
|
|
112
139
|
* Generic network error. The SDK does NOT throw this directly — fetch
|
|
@@ -206,6 +233,23 @@ export class GoogleSignInNotConfiguredError extends KoolbaseAuthError {
|
|
|
206
233
|
Object.setPrototypeOf(this, GoogleSignInNotConfiguredError.prototype);
|
|
207
234
|
}
|
|
208
235
|
}
|
|
236
|
+
/**
|
|
237
|
+
* The server answered a sign-in as though it succeeded, without the tokens a
|
|
238
|
+
* session needs.
|
|
239
|
+
*
|
|
240
|
+
* Distinct from verification_required, which is a legitimate session-less
|
|
241
|
+
* success the SDK reports through SignUpResult. This is the other case: the
|
|
242
|
+
* response claims authentication and cannot support it. Inventing a session
|
|
243
|
+
* from it is what produced a signed-in user whose every request went out as
|
|
244
|
+
* `Bearer undefined`, so the SDK refuses instead.
|
|
245
|
+
*/
|
|
246
|
+
export class MalformedSessionResponseError extends KoolbaseAuthError {
|
|
247
|
+
constructor(missing) {
|
|
248
|
+
super(`The server returned a session without ${missing}. This is a protocol error, not a credential problem.`, 'malformed_session_response');
|
|
249
|
+
this.name = 'MalformedSessionResponseError';
|
|
250
|
+
Object.setPrototypeOf(this, MalformedSessionResponseError.prototype);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
209
253
|
export class InvalidGoogleTokenError extends KoolbaseAuthError {
|
|
210
254
|
constructor() {
|
|
211
255
|
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, 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
|
|
@@ -143,6 +155,27 @@ export declare class KoolbaseAuth {
|
|
|
143
155
|
logout(): Promise<boolean>;
|
|
144
156
|
forgotPassword(email: string): Promise<void>;
|
|
145
157
|
resetPassword(token: string, password: string): Promise<void>;
|
|
158
|
+
/**
|
|
159
|
+
* Complete email verification with a token from a verification link.
|
|
160
|
+
*
|
|
161
|
+
* The token comes from wherever your verification URL template pointed —
|
|
162
|
+
* your own page reads it from the query string and passes it here. Throws
|
|
163
|
+
* if the token is invalid, expired or already used.
|
|
164
|
+
*/
|
|
165
|
+
verifyEmail(token: string): Promise<void>;
|
|
166
|
+
/**
|
|
167
|
+
* Re-send the verification email to the signed-in but unverified user.
|
|
168
|
+
*
|
|
169
|
+
* Safe to call when already verified: nothing is sent and
|
|
170
|
+
* `alreadyVerified` comes back true. The server throttles this — a short
|
|
171
|
+
* cooldown between sends and a daily cap — and the refusal arrives as
|
|
172
|
+
* VerificationResendCooldownError carrying when to retry, or
|
|
173
|
+
* VerificationResendDailyCapError when the day's allowance is spent.
|
|
174
|
+
*
|
|
175
|
+
* Requires a session, since the server verifies the caller rather than
|
|
176
|
+
* taking an email address — otherwise this would be an open mail relay.
|
|
177
|
+
*/
|
|
178
|
+
resendVerificationEmail(): Promise<ResendVerificationResult>;
|
|
146
179
|
unlock(token: string): Promise<void>;
|
|
147
180
|
get currentUser(): KoolbaseUser | null;
|
|
148
181
|
get accessToken(): string | null;
|
|
@@ -191,6 +224,18 @@ export declare class KoolbaseAuth {
|
|
|
191
224
|
* affects how a bare 401 (no code, older server) is interpreted.
|
|
192
225
|
*/
|
|
193
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;
|
|
194
239
|
private checkResponse;
|
|
195
240
|
/**
|
|
196
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, 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, 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 {
|
|
@@ -500,6 +516,45 @@ export class KoolbaseAuth {
|
|
|
500
516
|
});
|
|
501
517
|
await this.checkResponse(res);
|
|
502
518
|
}
|
|
519
|
+
/**
|
|
520
|
+
* Complete email verification with a token from a verification link.
|
|
521
|
+
*
|
|
522
|
+
* The token comes from wherever your verification URL template pointed —
|
|
523
|
+
* your own page reads it from the query string and passes it here. Throws
|
|
524
|
+
* if the token is invalid, expired or already used.
|
|
525
|
+
*/
|
|
526
|
+
async verifyEmail(token) {
|
|
527
|
+
const res = await this.authRequest('/v1/sdk/auth/verify-email', {
|
|
528
|
+
method: 'POST',
|
|
529
|
+
body: { token },
|
|
530
|
+
});
|
|
531
|
+
await this.checkResponse(res);
|
|
532
|
+
}
|
|
533
|
+
/**
|
|
534
|
+
* Re-send the verification email to the signed-in but unverified user.
|
|
535
|
+
*
|
|
536
|
+
* Safe to call when already verified: nothing is sent and
|
|
537
|
+
* `alreadyVerified` comes back true. The server throttles this — a short
|
|
538
|
+
* cooldown between sends and a daily cap — and the refusal arrives as
|
|
539
|
+
* VerificationResendCooldownError carrying when to retry, or
|
|
540
|
+
* VerificationResendDailyCapError when the day's allowance is spent.
|
|
541
|
+
*
|
|
542
|
+
* Requires a session, since the server verifies the caller rather than
|
|
543
|
+
* taking an email address — otherwise this would be an open mail relay.
|
|
544
|
+
*/
|
|
545
|
+
async resendVerificationEmail() {
|
|
546
|
+
const res = await this.authRequest('/v1/sdk/auth/resend-verification', {
|
|
547
|
+
method: 'POST',
|
|
548
|
+
includeAuth: true,
|
|
549
|
+
});
|
|
550
|
+
await this.checkResponse(res);
|
|
551
|
+
const body = (await res.json().catch(() => ({})));
|
|
552
|
+
return {
|
|
553
|
+
alreadyVerified: body.already_verified ?? false,
|
|
554
|
+
expiresAt: body.expires_at ? new Date(body.expires_at) : null,
|
|
555
|
+
cooldownUntil: body.cooldown_until ? new Date(body.cooldown_until) : null,
|
|
556
|
+
};
|
|
557
|
+
}
|
|
503
558
|
async unlock(token) {
|
|
504
559
|
const res = await this.authRequest('/v1/sdk/auth/unlock', {
|
|
505
560
|
method: 'POST',
|
|
@@ -671,7 +726,24 @@ export class KoolbaseAuth {
|
|
|
671
726
|
async parseSessionResponse(res, isRefresh) {
|
|
672
727
|
if (!res.ok)
|
|
673
728
|
await this.throwTypedError(res, isRefresh); // never returns
|
|
674
|
-
|
|
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');
|
|
675
747
|
return {
|
|
676
748
|
accessToken: data.access_token,
|
|
677
749
|
refreshToken: data.refresh_token,
|
|
@@ -722,6 +794,10 @@ export class KoolbaseAuth {
|
|
|
722
794
|
throw new UnlockTokenInvalidError();
|
|
723
795
|
case 'rate_limit':
|
|
724
796
|
throw new RateLimitError(msg || undefined);
|
|
797
|
+
case 'resend_cooldown':
|
|
798
|
+
throw new VerificationResendCooldownError(body.cooldown_until ? new Date(body.cooldown_until) : null, msg || undefined);
|
|
799
|
+
case 'resend_daily_cap':
|
|
800
|
+
throw new VerificationResendDailyCapError(msg || undefined);
|
|
725
801
|
}
|
|
726
802
|
// ─── status fallback (pre-code servers) ───
|
|
727
803
|
if (res.status === 409)
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -18,5 +18,5 @@ export type { RegisterTokenOptions } from './messaging.js';
|
|
|
18
18
|
export { getOrCreateDeviceId } from './device-id.js';
|
|
19
19
|
export { koolbaseSdkVersion } from './device-metadata.js';
|
|
20
20
|
export { RestoreResult } from './types.js';
|
|
21
|
-
export type { AuthStateListener, FetchLike, KoolbaseAuthStorage } from './types.js';
|
|
21
|
+
export type { AuthStateListener, FetchLike, KoolbaseAuthStorage, ResendVerificationResult } from './types.js';
|
|
22
22
|
export { setPlatform, getPlatform, memoryPlatform, type PlatformAdapter, type PlatformStorage, type PlatformNetwork, type PlatformLifecycle, type PlatformLocks, type PlatformInfo, } from './platform.js';
|
package/dist/esm/types.d.ts
CHANGED
|
@@ -53,6 +53,48 @@ export interface KoolbaseSession {
|
|
|
53
53
|
* unavailable), compliant encryption layers, or in-memory test mocks —
|
|
54
54
|
* can implement this interface and inject it via KoolbaseConfig.authStorage.
|
|
55
55
|
*/
|
|
56
|
+
/**
|
|
57
|
+
* What resendVerificationEmail() answers with.
|
|
58
|
+
*
|
|
59
|
+
* alreadyVerified is not an error: calling it for a verified account is a
|
|
60
|
+
* no-op that says so, which is what a "resend" button needs when the user
|
|
61
|
+
* verified in another tab.
|
|
62
|
+
*
|
|
63
|
+
* cooldownUntil is when the next send becomes possible. The server throttles
|
|
64
|
+
* sends, and a countdown is a better answer to a user than a bare refusal.
|
|
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
|
+
};
|
|
91
|
+
export interface ResendVerificationResult {
|
|
92
|
+
alreadyVerified: boolean;
|
|
93
|
+
/** When the link in the email stops working. Null when nothing was sent. */
|
|
94
|
+
expiresAt: Date | null;
|
|
95
|
+
/** When another send becomes possible. Null when nothing was sent. */
|
|
96
|
+
cooldownUntil: Date | null;
|
|
97
|
+
}
|
|
56
98
|
export interface KoolbaseAuthStorage {
|
|
57
99
|
saveSession(session: KoolbaseSession): Promise<void>;
|
|
58
100
|
readSession(): Promise<KoolbaseSession | null>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@koolbase/core",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "11.0.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",
|