@techfinityedge/koolbase-react-native 1.7.0 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +114 -60
- package/dist/auth-errors.d.ts +71 -0
- package/dist/auth-errors.js +127 -1
- package/dist/auth-storage.d.ts +26 -0
- package/dist/auth-storage.js +105 -0
- package/dist/auth.d.ts +83 -8
- package/dist/auth.js +397 -67
- package/dist/device-metadata.d.ts +36 -0
- package/dist/device-metadata.js +102 -0
- package/dist/functions.d.ts +2 -1
- package/dist/functions.js +11 -5
- package/dist/index.d.ts +5 -0
- package/dist/index.js +28 -2
- package/dist/types.d.ts +61 -0
- package/dist/types.js +16 -1
- package/package.json +11 -4
package/README.md
CHANGED
|
@@ -1,18 +1,6 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @techfinityedge/koolbase-react-native
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
This package is deprecated.
|
|
6
|
-
|
|
7
|
-
👉 Install the official version:
|
|
8
|
-
|
|
9
|
-
```bash
|
|
10
|
-
npm install @techfinityedge/koolbase-react-native
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
---
|
|
14
|
-
|
|
15
|
-
[](https://www.npmjs.com/package/koolbase-react-native)
|
|
3
|
+
[](https://www.npmjs.com/package/@techfinityedge/koolbase-react-native)
|
|
16
4
|
[](https://opensource.org/licenses/MIT)
|
|
17
5
|
|
|
18
6
|
React Native SDK for [Koolbase](https://koolbase.com) — Backend as a Service built for mobile developers.
|
|
@@ -30,15 +18,15 @@ Auth, database, storage, realtime, functions, feature flags, remote config, vers
|
|
|
30
18
|
3. Add the SDK:
|
|
31
19
|
|
|
32
20
|
```bash
|
|
33
|
-
npm install koolbase-react-native
|
|
21
|
+
npm install @techfinityedge/koolbase-react-native@^1.8.0
|
|
34
22
|
# or
|
|
35
|
-
yarn add koolbase-react-native
|
|
23
|
+
yarn add @techfinityedge/koolbase-react-native@^1.8.0
|
|
36
24
|
```
|
|
37
25
|
|
|
38
26
|
**4. Initialize at app startup:**
|
|
39
27
|
|
|
40
28
|
```typescript
|
|
41
|
-
import { Koolbase } from 'koolbase-react-native';
|
|
29
|
+
import { Koolbase } from '@techfinityedge/koolbase-react-native';
|
|
42
30
|
|
|
43
31
|
await Koolbase.initialize({
|
|
44
32
|
publicKey: 'pk_live_xxxx',
|
|
@@ -52,6 +40,8 @@ That's it. Every feature below is now available via `Koolbase.*`.
|
|
|
52
40
|
|
|
53
41
|
## Authentication
|
|
54
42
|
|
|
43
|
+
Email + password, Google, Apple, and phone + OTP — out of the box.
|
|
44
|
+
|
|
55
45
|
```typescript
|
|
56
46
|
// Register
|
|
57
47
|
await Koolbase.auth.register({ email: 'user@example.com', password: 'password' });
|
|
@@ -69,6 +59,50 @@ await Koolbase.auth.logout();
|
|
|
69
59
|
await Koolbase.auth.forgotPassword('user@example.com');
|
|
70
60
|
```
|
|
71
61
|
|
|
62
|
+
### OAuth — Google & Apple
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
// Google
|
|
66
|
+
await Koolbase.auth.signInWithGoogle({ idToken: googleIdToken });
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Apple uses the native authentication flow via `@invertase/react-native-apple-authentication` as a peer dependency:
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
import { KoolbaseAppleAuth } from '@techfinityedge/koolbase-react-native';
|
|
73
|
+
import { appleAuth } from '@invertase/react-native-apple-authentication';
|
|
74
|
+
|
|
75
|
+
const session = await KoolbaseAppleAuth.signIn(async () => {
|
|
76
|
+
return await appleAuth.performRequest({
|
|
77
|
+
requestedOperation: appleAuth.Operation.LOGIN,
|
|
78
|
+
requestedScopes: [appleAuth.Scope.EMAIL, appleAuth.Scope.FULL_NAME],
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Full setup guide at [docs.koolbase.com/auth/oauth](https://docs.koolbase.com/auth/oauth).
|
|
84
|
+
|
|
85
|
+
### Phone + OTP
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
// Send a one-time code
|
|
89
|
+
await Koolbase.auth.sendOtp({ phoneE164: '+233200000000' });
|
|
90
|
+
|
|
91
|
+
// Verify and sign in
|
|
92
|
+
await Koolbase.auth.verifyOtp({
|
|
93
|
+
phoneE164: '+233200000000',
|
|
94
|
+
code: '123456',
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// Or link a phone to an existing account
|
|
98
|
+
await Koolbase.auth.linkPhone({
|
|
99
|
+
phoneE164: '+233200000000',
|
|
100
|
+
code: '123456',
|
|
101
|
+
});
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Configure your SMS provider (Twilio, Africa's Talking, or Hubtel) in the dashboard under Phone Auth.
|
|
105
|
+
|
|
72
106
|
---
|
|
73
107
|
|
|
74
108
|
## Database
|
|
@@ -86,7 +120,7 @@ const { records } = await Koolbase.db.query('posts', {
|
|
|
86
120
|
});
|
|
87
121
|
|
|
88
122
|
// Populate related records
|
|
89
|
-
const { records } = await Koolbase.db.query('posts', {
|
|
123
|
+
const { records: postsWithAuthor } = await Koolbase.db.query('posts', {
|
|
90
124
|
populate: ['author_id:users'],
|
|
91
125
|
});
|
|
92
126
|
|
|
@@ -134,10 +168,40 @@ unsubscribe();
|
|
|
134
168
|
|
|
135
169
|
---
|
|
136
170
|
|
|
171
|
+
## Functions
|
|
172
|
+
|
|
173
|
+
Invoke deployed serverless functions. When a user is signed in via `Koolbase.auth`, their access token is automatically forwarded — the function receives the caller's identity via `ctx.auth`. No token handling on the client side.
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
// Invoke a deployed function
|
|
177
|
+
const result = await Koolbase.functions.invoke('send-welcome-email', {
|
|
178
|
+
userId: '123',
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
if (result.success) console.log(result.data);
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Inside the function (Deno runtime), read the caller:
|
|
185
|
+
|
|
186
|
+
```typescript
|
|
187
|
+
export async function handler(ctx) {
|
|
188
|
+
const userId = ctx.auth?.user_id;
|
|
189
|
+
if (!userId) {
|
|
190
|
+
return { error: { code: 'AUTH_REQUIRED' }, status: 401 };
|
|
191
|
+
}
|
|
192
|
+
// Authenticated logic here
|
|
193
|
+
return { ok: true };
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Token refresh is transparent — the SDK reads the current token fresh on every invoke. Full docs at [docs.koolbase.com/functions/authentication](https://docs.koolbase.com/functions/authentication).
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
137
201
|
## Feature Flags & Remote Config
|
|
138
202
|
|
|
139
203
|
```typescript
|
|
140
|
-
if (Koolbase.isEnabled('new_checkout')) { ... }
|
|
204
|
+
if (Koolbase.isEnabled('new_checkout')) { /* ... */ }
|
|
141
205
|
|
|
142
206
|
const timeout = Koolbase.configNumber('timeout_seconds', 30);
|
|
143
207
|
const apiUrl = Koolbase.configString('api_url', 'https://api.myapp.com');
|
|
@@ -159,6 +223,8 @@ if (result.status === 'force_update') {
|
|
|
159
223
|
|
|
160
224
|
## Code Push
|
|
161
225
|
|
|
226
|
+
Push config overrides, feature flag overrides, and directive-driven behaviour without a store release.
|
|
227
|
+
|
|
162
228
|
```typescript
|
|
163
229
|
await Koolbase.initialize({
|
|
164
230
|
publicKey: 'pk_live_xxxx',
|
|
@@ -180,10 +246,13 @@ Koolbase.codePush.applyDirectives();
|
|
|
180
246
|
|
|
181
247
|
## Logic Engine
|
|
182
248
|
|
|
249
|
+
Define conditional app behavior as data in your Runtime Bundle — no code changes required.
|
|
250
|
+
|
|
183
251
|
```typescript
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
252
|
+
const result = Koolbase.executeFlow('on_checkout_tap', {
|
|
253
|
+
plan: user.plan,
|
|
254
|
+
usage: user.usage,
|
|
255
|
+
});
|
|
187
256
|
|
|
188
257
|
if (result.hasEvent) {
|
|
189
258
|
switch (result.eventName) {
|
|
@@ -193,10 +262,16 @@ if (result.hasEvent) {
|
|
|
193
262
|
}
|
|
194
263
|
```
|
|
195
264
|
|
|
265
|
+
**v2 operators:** `eq`, `neq`, `gt`, `gte`, `lt`, `lte`, `contains`, `starts_with`, `ends_with`, `in_list`, `not_in_list`, `between`, `is_true`, `is_false`, `exists`, `not_exists`, `and`, `or`
|
|
266
|
+
|
|
267
|
+
Full docs at [docs.koolbase.com/sdk/logic-engine](https://docs.koolbase.com/sdk/logic-engine).
|
|
268
|
+
|
|
196
269
|
---
|
|
197
270
|
|
|
198
271
|
## Analytics
|
|
199
272
|
|
|
273
|
+
Track screen views, custom events, and user behaviour. View DAU, WAU, MAU, funnels, and retention in the Koolbase dashboard.
|
|
274
|
+
|
|
200
275
|
```typescript
|
|
201
276
|
await Koolbase.initialize({
|
|
202
277
|
publicKey: 'pk_live_xxxx',
|
|
@@ -248,43 +323,22 @@ await Koolbase.messaging.send({
|
|
|
248
323
|
|
|
249
324
|
---
|
|
250
325
|
|
|
251
|
-
##
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
**v2 operators:** `eq`, `neq`, `gt`, `gte`, `lt`, `lte`, `contains`, `starts_with`, `ends_with`, `in_list`, `not_in_list`, `between`, `is_true`, `is_false`, `exists`, `not_exists`, `and`, `or`
|
|
268
|
-
|
|
269
|
-
Full docs at [docs.koolbase.com/sdk/logic-engine](https://docs.koolbase.com/sdk/logic-engine).
|
|
270
|
-
|
|
271
|
-
---
|
|
272
|
-
|
|
273
|
-
## Sign in with Apple
|
|
274
|
-
|
|
275
|
-
```typescript
|
|
276
|
-
import { KoolbaseAppleAuth } from 'koolbase-react-native';
|
|
277
|
-
import { appleAuth } from '@invertase/react-native-apple-authentication';
|
|
278
|
-
|
|
279
|
-
const session = await KoolbaseAppleAuth.signIn(async () => {
|
|
280
|
-
return await appleAuth.performRequest({
|
|
281
|
-
requestedOperation: appleAuth.Operation.LOGIN,
|
|
282
|
-
requestedScopes: [appleAuth.Scope.EMAIL, appleAuth.Scope.FULL_NAME],
|
|
283
|
-
});
|
|
284
|
-
});
|
|
285
|
-
```
|
|
286
|
-
|
|
287
|
-
Install `@invertase/react-native-apple-authentication` as a peer dependency. Full setup guide at [docs.koolbase.com/auth/oauth](https://docs.koolbase.com/auth/oauth).
|
|
326
|
+
## What's included
|
|
327
|
+
|
|
328
|
+
| Feature | Koolbase | Firebase | Supabase |
|
|
329
|
+
| --- | --- | --- | --- |
|
|
330
|
+
| TypeScript SDK | Yes | Yes | Yes |
|
|
331
|
+
| Feature flags | Yes | — | — |
|
|
332
|
+
| Remote config | Yes | Yes | — |
|
|
333
|
+
| Version enforcement | Yes | — | — |
|
|
334
|
+
| Offline-first database | Yes | Yes | — |
|
|
335
|
+
| Code push | Yes | — | — |
|
|
336
|
+
| Logic engine (flows OTA) | Yes | — | — |
|
|
337
|
+
| Analytics | Yes | Yes | — |
|
|
338
|
+
| Cloud Messaging | Yes | Yes | — |
|
|
339
|
+
| Sign in with Apple | Yes | Yes | Yes |
|
|
340
|
+
| Phone + OTP | Yes | Yes | Yes |
|
|
341
|
+
| Authenticated functions (`ctx.auth`) | Yes | Yes | Yes |
|
|
288
342
|
|
|
289
343
|
---
|
|
290
344
|
|
|
@@ -300,7 +354,7 @@ Manage your projects at [app.koolbase.com](https://app.koolbase.com)
|
|
|
300
354
|
|
|
301
355
|
- [GitHub Issues](https://github.com/kennedyowusu/koolbase-react-native/issues)
|
|
302
356
|
- [docs.koolbase.com](https://docs.koolbase.com)
|
|
303
|
-
- Email: hello@koolbase.com
|
|
357
|
+
- Email: <hello@koolbase.com>
|
|
304
358
|
|
|
305
359
|
## License
|
|
306
360
|
|
package/dist/auth-errors.d.ts
CHANGED
|
@@ -1,7 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base error type for all Koolbase auth errors. Catchable via
|
|
3
|
+
* `instanceof KoolbaseAuthError` to handle any auth-related failure
|
|
4
|
+
* generically; subclasses let you handle specific cases.
|
|
5
|
+
*/
|
|
1
6
|
export declare class KoolbaseAuthError extends Error {
|
|
2
7
|
code?: string;
|
|
3
8
|
constructor(message: string, code?: string);
|
|
4
9
|
}
|
|
10
|
+
export declare class InvalidCredentialsError extends KoolbaseAuthError {
|
|
11
|
+
constructor();
|
|
12
|
+
}
|
|
13
|
+
export declare class EmailAlreadyInUseError extends KoolbaseAuthError {
|
|
14
|
+
constructor();
|
|
15
|
+
}
|
|
16
|
+
export declare class UserDisabledError extends KoolbaseAuthError {
|
|
17
|
+
constructor();
|
|
18
|
+
}
|
|
19
|
+
export declare class WeakPasswordError extends KoolbaseAuthError {
|
|
20
|
+
constructor();
|
|
21
|
+
}
|
|
22
|
+
export declare class SessionExpiredError extends KoolbaseAuthError {
|
|
23
|
+
constructor();
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Thrown when the access token references a session that has been revoked
|
|
27
|
+
* centrally — either by the user (sessions endpoint) or an administrator.
|
|
28
|
+
* Distinct from {@link SessionExpiredError} which indicates the access
|
|
29
|
+
* token TTL elapsed without a successful refresh.
|
|
30
|
+
*
|
|
31
|
+
* Forward-compatible: matches multiple server message patterns so it stays
|
|
32
|
+
* accurate as the server's revocation signaling evolves.
|
|
33
|
+
*/
|
|
34
|
+
export declare class TokenRevokedError extends KoolbaseAuthError {
|
|
35
|
+
constructor();
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Thrown when the account is temporarily locked due to too many failed
|
|
39
|
+
* login attempts. The server uses progressive 5/10/20-attempt lockouts;
|
|
40
|
+
* if an unlock email was issued (level 2+), the user can clear the lock
|
|
41
|
+
* by passing that token to {@link KoolbaseAuth.unlock}.
|
|
42
|
+
*
|
|
43
|
+
* [lockedUntil] is currently null — the server returns a generic 429 but
|
|
44
|
+
* does not yet include the unlock timestamp in the response body. Field
|
|
45
|
+
* is forward-compatible for when the server adds it.
|
|
46
|
+
*/
|
|
47
|
+
export declare class AccountLockedError extends KoolbaseAuthError {
|
|
48
|
+
lockedUntil?: Date;
|
|
49
|
+
constructor(lockedUntil?: Date);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Thrown when the unlock token from a brute-force unlock email is
|
|
53
|
+
* invalid, expired, or already consumed. Unlock tokens are one-shot.
|
|
54
|
+
*/
|
|
55
|
+
export declare class UnlockTokenInvalidError extends KoolbaseAuthError {
|
|
56
|
+
constructor();
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Thrown when the server rate-limits a non-phone authentication endpoint
|
|
60
|
+
* (HTTP 429 without the "account temporarily locked" marker). Phone OTP
|
|
61
|
+
* endpoints throw {@link OtpRateLimitError} instead — they hit a
|
|
62
|
+
* separate server-side rate-limiter.
|
|
63
|
+
*/
|
|
64
|
+
export declare class RateLimitError extends KoolbaseAuthError {
|
|
65
|
+
constructor(message?: string);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Generic network error. The SDK does NOT throw this directly — fetch
|
|
69
|
+
* failures (DNS, no connection, timeout) propagate as native TypeErrors.
|
|
70
|
+
* This class exists for consumer code that wants to construct or
|
|
71
|
+
* `instanceof`-check a typed network error from their own retry logic.
|
|
72
|
+
*/
|
|
73
|
+
export declare class NetworkError extends KoolbaseAuthError {
|
|
74
|
+
constructor();
|
|
75
|
+
}
|
|
5
76
|
export declare class InvalidPhoneNumberError extends KoolbaseAuthError {
|
|
6
77
|
constructor();
|
|
7
78
|
}
|
package/dist/auth-errors.js
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.SmsConfigMissingError = exports.PhoneAlreadyLinkedError = exports.OtpRateLimitError = exports.OtpMaxAttemptsError = exports.OtpInvalidError = exports.OtpExpiredError = exports.InvalidPhoneNumberError = exports.KoolbaseAuthError = void 0;
|
|
3
|
+
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;
|
|
4
|
+
/**
|
|
5
|
+
* Base error type for all Koolbase auth errors. Catchable via
|
|
6
|
+
* `instanceof KoolbaseAuthError` to handle any auth-related failure
|
|
7
|
+
* generically; subclasses let you handle specific cases.
|
|
8
|
+
*/
|
|
4
9
|
class KoolbaseAuthError extends Error {
|
|
5
10
|
constructor(message, code) {
|
|
6
11
|
super(message);
|
|
@@ -10,6 +15,127 @@ class KoolbaseAuthError extends Error {
|
|
|
10
15
|
}
|
|
11
16
|
}
|
|
12
17
|
exports.KoolbaseAuthError = KoolbaseAuthError;
|
|
18
|
+
// ─── Credentials / Registration ────────────────────────────────────────────
|
|
19
|
+
class InvalidCredentialsError extends KoolbaseAuthError {
|
|
20
|
+
constructor() {
|
|
21
|
+
super('Invalid email or password', 'invalid_credentials');
|
|
22
|
+
this.name = 'InvalidCredentialsError';
|
|
23
|
+
Object.setPrototypeOf(this, InvalidCredentialsError.prototype);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
exports.InvalidCredentialsError = InvalidCredentialsError;
|
|
27
|
+
class EmailAlreadyInUseError extends KoolbaseAuthError {
|
|
28
|
+
constructor() {
|
|
29
|
+
super('Email is already in use', 'email_taken');
|
|
30
|
+
this.name = 'EmailAlreadyInUseError';
|
|
31
|
+
Object.setPrototypeOf(this, EmailAlreadyInUseError.prototype);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
exports.EmailAlreadyInUseError = EmailAlreadyInUseError;
|
|
35
|
+
class UserDisabledError extends KoolbaseAuthError {
|
|
36
|
+
constructor() {
|
|
37
|
+
super('This account has been disabled', 'user_disabled');
|
|
38
|
+
this.name = 'UserDisabledError';
|
|
39
|
+
Object.setPrototypeOf(this, UserDisabledError.prototype);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
exports.UserDisabledError = UserDisabledError;
|
|
43
|
+
class WeakPasswordError extends KoolbaseAuthError {
|
|
44
|
+
constructor() {
|
|
45
|
+
super('Password must be at least 8 characters', 'weak_password');
|
|
46
|
+
this.name = 'WeakPasswordError';
|
|
47
|
+
Object.setPrototypeOf(this, WeakPasswordError.prototype);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
exports.WeakPasswordError = WeakPasswordError;
|
|
51
|
+
// ─── Session lifecycle ─────────────────────────────────────────────────────
|
|
52
|
+
class SessionExpiredError extends KoolbaseAuthError {
|
|
53
|
+
constructor() {
|
|
54
|
+
super('Session expired, please log in again', 'session_expired');
|
|
55
|
+
this.name = 'SessionExpiredError';
|
|
56
|
+
Object.setPrototypeOf(this, SessionExpiredError.prototype);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
exports.SessionExpiredError = SessionExpiredError;
|
|
60
|
+
/**
|
|
61
|
+
* Thrown when the access token references a session that has been revoked
|
|
62
|
+
* centrally — either by the user (sessions endpoint) or an administrator.
|
|
63
|
+
* Distinct from {@link SessionExpiredError} which indicates the access
|
|
64
|
+
* token TTL elapsed without a successful refresh.
|
|
65
|
+
*
|
|
66
|
+
* Forward-compatible: matches multiple server message patterns so it stays
|
|
67
|
+
* accurate as the server's revocation signaling evolves.
|
|
68
|
+
*/
|
|
69
|
+
class TokenRevokedError extends KoolbaseAuthError {
|
|
70
|
+
constructor() {
|
|
71
|
+
super('Session has been revoked, please log in again', 'token_revoked');
|
|
72
|
+
this.name = 'TokenRevokedError';
|
|
73
|
+
Object.setPrototypeOf(this, TokenRevokedError.prototype);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
exports.TokenRevokedError = TokenRevokedError;
|
|
77
|
+
// ─── Brute-force protection ────────────────────────────────────────────────
|
|
78
|
+
/**
|
|
79
|
+
* Thrown when the account is temporarily locked due to too many failed
|
|
80
|
+
* login attempts. The server uses progressive 5/10/20-attempt lockouts;
|
|
81
|
+
* if an unlock email was issued (level 2+), the user can clear the lock
|
|
82
|
+
* by passing that token to {@link KoolbaseAuth.unlock}.
|
|
83
|
+
*
|
|
84
|
+
* [lockedUntil] is currently null — the server returns a generic 429 but
|
|
85
|
+
* does not yet include the unlock timestamp in the response body. Field
|
|
86
|
+
* is forward-compatible for when the server adds it.
|
|
87
|
+
*/
|
|
88
|
+
class AccountLockedError extends KoolbaseAuthError {
|
|
89
|
+
constructor(lockedUntil) {
|
|
90
|
+
super('Account temporarily locked due to too many failed attempts', 'account_locked');
|
|
91
|
+
this.lockedUntil = lockedUntil;
|
|
92
|
+
this.name = 'AccountLockedError';
|
|
93
|
+
Object.setPrototypeOf(this, AccountLockedError.prototype);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
exports.AccountLockedError = AccountLockedError;
|
|
97
|
+
/**
|
|
98
|
+
* Thrown when the unlock token from a brute-force unlock email is
|
|
99
|
+
* invalid, expired, or already consumed. Unlock tokens are one-shot.
|
|
100
|
+
*/
|
|
101
|
+
class UnlockTokenInvalidError extends KoolbaseAuthError {
|
|
102
|
+
constructor() {
|
|
103
|
+
super('Unlock link is invalid or has expired', 'unlock_token_invalid');
|
|
104
|
+
this.name = 'UnlockTokenInvalidError';
|
|
105
|
+
Object.setPrototypeOf(this, UnlockTokenInvalidError.prototype);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
exports.UnlockTokenInvalidError = UnlockTokenInvalidError;
|
|
109
|
+
/**
|
|
110
|
+
* Thrown when the server rate-limits a non-phone authentication endpoint
|
|
111
|
+
* (HTTP 429 without the "account temporarily locked" marker). Phone OTP
|
|
112
|
+
* endpoints throw {@link OtpRateLimitError} instead — they hit a
|
|
113
|
+
* separate server-side rate-limiter.
|
|
114
|
+
*/
|
|
115
|
+
class RateLimitError extends KoolbaseAuthError {
|
|
116
|
+
constructor(message) {
|
|
117
|
+
super(message ?? 'Too many requests, please wait before trying again', 'rate_limit');
|
|
118
|
+
this.name = 'RateLimitError';
|
|
119
|
+
Object.setPrototypeOf(this, RateLimitError.prototype);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
exports.RateLimitError = RateLimitError;
|
|
123
|
+
// ─── Network ───────────────────────────────────────────────────────────────
|
|
124
|
+
/**
|
|
125
|
+
* Generic network error. The SDK does NOT throw this directly — fetch
|
|
126
|
+
* failures (DNS, no connection, timeout) propagate as native TypeErrors.
|
|
127
|
+
* This class exists for consumer code that wants to construct or
|
|
128
|
+
* `instanceof`-check a typed network error from their own retry logic.
|
|
129
|
+
*/
|
|
130
|
+
class NetworkError extends KoolbaseAuthError {
|
|
131
|
+
constructor() {
|
|
132
|
+
super('Network error, please check your connection', 'network_error');
|
|
133
|
+
this.name = 'NetworkError';
|
|
134
|
+
Object.setPrototypeOf(this, NetworkError.prototype);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
exports.NetworkError = NetworkError;
|
|
138
|
+
// ─── Phone OTP (unchanged from earlier releases) ───────────────────────────
|
|
13
139
|
class InvalidPhoneNumberError extends KoolbaseAuthError {
|
|
14
140
|
constructor() {
|
|
15
141
|
super('Phone number must be in E.164 format (e.g. +233XXXXXXXXX)', 'invalid_phone');
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { KoolbaseAuthStorage, KoolbaseSession } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Probe whether react-native-keychain is installed without throwing.
|
|
4
|
+
* Used by KoolbaseAuth to decide whether to instantiate a default
|
|
5
|
+
* SecureAuthStorage or proceed without persistence.
|
|
6
|
+
*/
|
|
7
|
+
export declare function isKeychainAvailable(): boolean;
|
|
8
|
+
/**
|
|
9
|
+
* Default secure storage implementation backed by `react-native-keychain`.
|
|
10
|
+
*
|
|
11
|
+
* - iOS: Keychain (encrypted, never synced via iCloud by default)
|
|
12
|
+
* - Android: Android Keystore-backed encryption
|
|
13
|
+
*
|
|
14
|
+
* Requires `react-native-keychain` as a peer dependency. Apps that prefer a
|
|
15
|
+
* different secure backend (e.g. `expo-secure-store`,
|
|
16
|
+
* `react-native-encrypted-storage`, an in-memory mock for testing, or a
|
|
17
|
+
* compliance-grade encryption layer) should implement the
|
|
18
|
+
* {@link KoolbaseAuthStorage} interface and pass it via
|
|
19
|
+
* `KoolbaseConfig.authStorage`.
|
|
20
|
+
*/
|
|
21
|
+
export declare class SecureAuthStorage implements KoolbaseAuthStorage {
|
|
22
|
+
private static readonly SERVICE;
|
|
23
|
+
saveSession(session: KoolbaseSession): Promise<void>;
|
|
24
|
+
readSession(): Promise<KoolbaseSession | null>;
|
|
25
|
+
clear(): Promise<void>;
|
|
26
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SecureAuthStorage = void 0;
|
|
4
|
+
exports.isKeychainAvailable = isKeychainAvailable;
|
|
5
|
+
// Lazy-load react-native-keychain so apps without it installed (e.g. Expo Go,
|
|
6
|
+
// or those providing a custom adapter) can still import this module without
|
|
7
|
+
// crashing. The default SecureAuthStorage will throw a clear error on first
|
|
8
|
+
// use if the peer dependency is missing.
|
|
9
|
+
let _keychain = null;
|
|
10
|
+
let _keychainAttempted = false;
|
|
11
|
+
function loadKeychain() {
|
|
12
|
+
if (!_keychainAttempted) {
|
|
13
|
+
_keychainAttempted = true;
|
|
14
|
+
try {
|
|
15
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
16
|
+
_keychain = require('react-native-keychain');
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
_keychain = null;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
if (!_keychain) {
|
|
23
|
+
throw new Error('[Koolbase] SecureAuthStorage requires react-native-keychain. ' +
|
|
24
|
+
'Install it with:\n npm install react-native-keychain\n' +
|
|
25
|
+
'Or provide your own storage adapter via ' +
|
|
26
|
+
'KoolbaseConfig.authStorage. For Expo Go (where ' +
|
|
27
|
+
'react-native-keychain is unavailable), implement KoolbaseAuthStorage ' +
|
|
28
|
+
'with expo-secure-store and pass it via authStorage.');
|
|
29
|
+
}
|
|
30
|
+
return _keychain;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Probe whether react-native-keychain is installed without throwing.
|
|
34
|
+
* Used by KoolbaseAuth to decide whether to instantiate a default
|
|
35
|
+
* SecureAuthStorage or proceed without persistence.
|
|
36
|
+
*/
|
|
37
|
+
function isKeychainAvailable() {
|
|
38
|
+
if (!_keychainAttempted) {
|
|
39
|
+
_keychainAttempted = true;
|
|
40
|
+
try {
|
|
41
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
42
|
+
_keychain = require('react-native-keychain');
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
_keychain = null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return _keychain !== null;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Default secure storage implementation backed by `react-native-keychain`.
|
|
52
|
+
*
|
|
53
|
+
* - iOS: Keychain (encrypted, never synced via iCloud by default)
|
|
54
|
+
* - Android: Android Keystore-backed encryption
|
|
55
|
+
*
|
|
56
|
+
* Requires `react-native-keychain` as a peer dependency. Apps that prefer a
|
|
57
|
+
* different secure backend (e.g. `expo-secure-store`,
|
|
58
|
+
* `react-native-encrypted-storage`, an in-memory mock for testing, or a
|
|
59
|
+
* compliance-grade encryption layer) should implement the
|
|
60
|
+
* {@link KoolbaseAuthStorage} interface and pass it via
|
|
61
|
+
* `KoolbaseConfig.authStorage`.
|
|
62
|
+
*/
|
|
63
|
+
class SecureAuthStorage {
|
|
64
|
+
async saveSession(session) {
|
|
65
|
+
const Keychain = loadKeychain();
|
|
66
|
+
await Keychain.setGenericPassword('session', JSON.stringify(session), { service: SecureAuthStorage.SERVICE });
|
|
67
|
+
}
|
|
68
|
+
async readSession() {
|
|
69
|
+
let Keychain;
|
|
70
|
+
try {
|
|
71
|
+
Keychain = loadKeychain();
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
// Peer dep missing — caller will fall back to no persistence.
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
const credentials = await Keychain.getGenericPassword({
|
|
79
|
+
service: SecureAuthStorage.SERVICE,
|
|
80
|
+
});
|
|
81
|
+
if (!credentials || !credentials.password)
|
|
82
|
+
return null;
|
|
83
|
+
return JSON.parse(credentials.password);
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
// Corrupt data, schema mismatch, or platform-level keychain error.
|
|
87
|
+
// Treat as no session — caller will trigger fresh login.
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
async clear() {
|
|
92
|
+
let Keychain;
|
|
93
|
+
try {
|
|
94
|
+
Keychain = loadKeychain();
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
return; // No-op if peer dep missing.
|
|
98
|
+
}
|
|
99
|
+
await Keychain.resetGenericPassword({
|
|
100
|
+
service: SecureAuthStorage.SERVICE,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
exports.SecureAuthStorage = SecureAuthStorage;
|
|
105
|
+
SecureAuthStorage.SERVICE = 'koolbase_session_v1';
|