@layers/amba-react-native 4.2.1 → 4.2.2
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/index.cjs +93 -33
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +93 -33
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -249,8 +249,35 @@ function withSignal(init, signal) {
|
|
|
249
249
|
//#region src/http-client.ts
|
|
250
250
|
/** Default amba API endpoint. */
|
|
251
251
|
const DEFAULT_BASE_URL = "https://api.amba.dev";
|
|
252
|
-
/**
|
|
253
|
-
|
|
252
|
+
/**
|
|
253
|
+
* Server codes for a session token the server no longer accepts.
|
|
254
|
+
* `INVALID_SESSION` is the code the auth routes that verify the session
|
|
255
|
+
* themselves (`/identifiers`, `/link`, `/unlink`) answer with.
|
|
256
|
+
*/
|
|
257
|
+
const SESSION_REFUSED_CODES = new Set([
|
|
258
|
+
"SESSION_REVOKED",
|
|
259
|
+
"INVALID_SESSION_TOKEN",
|
|
260
|
+
"INVALID_SESSION"
|
|
261
|
+
]);
|
|
262
|
+
/** True for a server error that refused the session token a call sent. */
|
|
263
|
+
function isSessionRefusedError(err) {
|
|
264
|
+
return err instanceof AmbaApiError && SESSION_REFUSED_CODES.has(err.code);
|
|
265
|
+
}
|
|
266
|
+
const AUTH_PATH_PREFIX = "/v1/client/auth/";
|
|
267
|
+
/**
|
|
268
|
+
* The auth routes that read the session from the `Authorization` header and
|
|
269
|
+
* mint nothing: a refused session on them recovers like it does on any
|
|
270
|
+
* other authenticated call. The other auth routes stay out of the
|
|
271
|
+
* recovery: `/refresh` is the recovery itself, `/logout` ends the session,
|
|
272
|
+
* the sign-in, sign-up, OTP and magic-link routes take credentials (a 401
|
|
273
|
+
* there is a credential error), and `/link` and `/unlink` carry the session
|
|
274
|
+
* in their body, so their callers recover and rebuild the body themselves.
|
|
275
|
+
*/
|
|
276
|
+
const SESSION_HEADER_AUTH_PATHS = new Set([`${AUTH_PATH_PREFIX}me`, `${AUTH_PATH_PREFIX}identifiers`]);
|
|
277
|
+
/** Whether a refused session on `path` goes through `onSessionRefused`. */
|
|
278
|
+
function recoversRefusedSession(path) {
|
|
279
|
+
return !path.startsWith(AUTH_PATH_PREFIX) || SESSION_HEADER_AUTH_PATHS.has(path);
|
|
280
|
+
}
|
|
254
281
|
var AmbaHttpClient = class {
|
|
255
282
|
apiKey;
|
|
256
283
|
baseUrl;
|
|
@@ -386,7 +413,7 @@ var AmbaHttpClient = class {
|
|
|
386
413
|
try {
|
|
387
414
|
return parseResponse(response, text);
|
|
388
415
|
} catch (err) {
|
|
389
|
-
if (!resent && storedToken && this.onSessionRefused &&
|
|
416
|
+
if (!resent && storedToken && this.onSessionRefused && recoversRefusedSession(path) && isSessionRefusedError(err) && await this.recoverSession(storedToken, storedUserId)) return this.request(method, path, body, query, void 0, true);
|
|
390
417
|
throw err;
|
|
391
418
|
}
|
|
392
419
|
}
|
|
@@ -2747,7 +2774,7 @@ function delay(ms) {
|
|
|
2747
2774
|
//#endregion
|
|
2748
2775
|
//#region src/index.ts
|
|
2749
2776
|
/** SDK semver — burned in at build time. Bump alongside package.json. */
|
|
2750
|
-
const SDK_VERSION = "4.2.
|
|
2777
|
+
const SDK_VERSION = "4.2.2";
|
|
2751
2778
|
/**
|
|
2752
2779
|
* Default host that serves deployed functions. `Amba.functions.invoke(name)`
|
|
2753
2780
|
* targets `https://<projectId>.<DEFAULT_FUNCTIONS_HOST>/<name>` unless a
|
|
@@ -3033,7 +3060,11 @@ function trackLink(state_, link) {
|
|
|
3033
3060
|
}
|
|
3034
3061
|
/**
|
|
3035
3062
|
* After the server refused the stored session token on an authenticated
|
|
3036
|
-
* call (`SESSION_REVOKED`, `INVALID_SESSION_TOKEN`).
|
|
3063
|
+
* call (`SESSION_REVOKED`, `INVALID_SESSION_TOKEN`, `INVALID_SESSION`). The
|
|
3064
|
+
* HTTP client runs it for calls that send the session in the
|
|
3065
|
+
* `Authorization` header, `/v1/client/auth/me` and `/identifiers`
|
|
3066
|
+
* included; `withBodySession` runs it for the link and unlink calls, which
|
|
3067
|
+
* send the session in their body. True when a usable
|
|
3037
3068
|
* session for the user the call was sent for is stored afterwards, so the
|
|
3038
3069
|
* call is sent once more as that user:
|
|
3039
3070
|
* - a link in flight on this device stores the pair it returns, which is
|
|
@@ -3067,6 +3098,40 @@ async function recoverRefusedSession(state_, usedToken, usedUserId) {
|
|
|
3067
3098
|
return usableForCaller(state_.identity.getSessionToken());
|
|
3068
3099
|
}
|
|
3069
3100
|
/**
|
|
3101
|
+
* Send an auth call that carries the stored session token in its body
|
|
3102
|
+
* (`/link`, `/unlink`). `send` builds the request from the token it is
|
|
3103
|
+
* given. When the server refuses that token, the session recovers through
|
|
3104
|
+
* `recoverRefusedSession` (one refresh shared with every other refused
|
|
3105
|
+
* call) and the call is sent once more with the recovered token; a second
|
|
3106
|
+
* refusal reaches the app. A link waits here outside `trackLink`: its own
|
|
3107
|
+
* failed attempt has already left `linksInflight` (the removal is the first
|
|
3108
|
+
* reaction on that promise), so recovery never waits on itself.
|
|
3109
|
+
*/
|
|
3110
|
+
async function withBodySession(state_, send) {
|
|
3111
|
+
const usedToken = state_.identity.getSessionToken();
|
|
3112
|
+
if (!usedToken) throw new AmbaApiError("UNAUTHORIZED", "no active session — call signIn* first");
|
|
3113
|
+
const usedUserId = state_.identity.getAppUserId();
|
|
3114
|
+
try {
|
|
3115
|
+
return await send(usedToken);
|
|
3116
|
+
} catch (err) {
|
|
3117
|
+
if (!isSessionRefusedError(err)) throw err;
|
|
3118
|
+
if (!await recoverRefusedSession(state_, usedToken, usedUserId)) throw err;
|
|
3119
|
+
const recovered = state_.identity.getSessionToken();
|
|
3120
|
+
if (!recovered) throw err;
|
|
3121
|
+
return send(recovered);
|
|
3122
|
+
}
|
|
3123
|
+
}
|
|
3124
|
+
/**
|
|
3125
|
+
* Before a read of the current session (`me`, `getSession`,
|
|
3126
|
+
* `getCurrentIdentifiers`) while no session token is stored: wait for the
|
|
3127
|
+
* launch restore or sign-in in flight, so a check made at launch sees the
|
|
3128
|
+
* session it restores.
|
|
3129
|
+
*/
|
|
3130
|
+
async function settleSessionInFlight(state_) {
|
|
3131
|
+
if (state_.identity.getSessionToken() || !state_.anonSignInInflight) return;
|
|
3132
|
+
await state_.anonSignInInflight.catch(() => {});
|
|
3133
|
+
}
|
|
3134
|
+
/**
|
|
3070
3135
|
* Continue the stored session with its refresh token. True when it did.
|
|
3071
3136
|
* False when there is no refresh token, or the server rejected it: the
|
|
3072
3137
|
* session is then cleared (the anonymous id rotates unless the session was
|
|
@@ -3177,7 +3242,10 @@ async function refreshSessionOnce(state_) {
|
|
|
3177
3242
|
* account's rotated anonymous id is adopted.
|
|
3178
3243
|
*/
|
|
3179
3244
|
function postLink(state_, body) {
|
|
3180
|
-
return trackLink(state_, postLinkOnce(state_,
|
|
3245
|
+
return withBodySession(state_, (sessionToken) => trackLink(state_, postLinkOnce(state_, {
|
|
3246
|
+
...body,
|
|
3247
|
+
session_token: sessionToken
|
|
3248
|
+
})));
|
|
3181
3249
|
}
|
|
3182
3250
|
async function postLinkOnce(state_, body) {
|
|
3183
3251
|
const env = await state_.http.post(P.authLink, {
|
|
@@ -3213,12 +3281,10 @@ async function postUserNoRotate(state_, path, body) {
|
|
|
3213
3281
|
}
|
|
3214
3282
|
async function unlinkProvider(provider) {
|
|
3215
3283
|
const s = getState();
|
|
3216
|
-
|
|
3217
|
-
if (!sessionToken) throw new AmbaApiError("UNAUTHORIZED", "no active session — call signIn* first");
|
|
3218
|
-
return postUserNoRotate(s, P.authUnlink, {
|
|
3284
|
+
return withBodySession(s, (sessionToken) => postUserNoRotate(s, P.authUnlink, {
|
|
3219
3285
|
provider,
|
|
3220
3286
|
session_token: sessionToken
|
|
3221
|
-
});
|
|
3287
|
+
}));
|
|
3222
3288
|
}
|
|
3223
3289
|
function requireEmail(email) {
|
|
3224
3290
|
const trimmed = email.trim();
|
|
@@ -3732,52 +3798,38 @@ const Amba = {
|
|
|
3732
3798
|
if (providerTrimmed === "") throw new AmbaApiError("VALIDATION_ERROR", "provider must not be empty");
|
|
3733
3799
|
if (typeof credential !== "string" || credential.trim() === "") throw new AmbaApiError("VALIDATION_ERROR", "credential must not be empty");
|
|
3734
3800
|
const s = getState();
|
|
3735
|
-
|
|
3736
|
-
if (!sessionToken) throw new AmbaApiError("UNAUTHORIZED", "no active session — call signIn* first");
|
|
3737
|
-
return trackLink(s, postAuth(s, P.authLink, {
|
|
3801
|
+
return withBodySession(s, (sessionToken) => trackLink(s, postAuth(s, P.authLink, {
|
|
3738
3802
|
provider: providerTrimmed,
|
|
3739
3803
|
token: credential,
|
|
3740
3804
|
session_token: sessionToken,
|
|
3741
3805
|
...nonceField(options)
|
|
3742
|
-
}));
|
|
3806
|
+
})));
|
|
3743
3807
|
},
|
|
3744
3808
|
linkSmsOtp: async (phone, code) => {
|
|
3745
3809
|
requireE164Phone(phone);
|
|
3746
3810
|
requireOtpCode(code);
|
|
3747
|
-
|
|
3748
|
-
const sessionToken = s.identity.getSessionToken();
|
|
3749
|
-
if (!sessionToken) throw new AmbaApiError("UNAUTHORIZED", "no active session — call signIn* first");
|
|
3750
|
-
return postLink(s, {
|
|
3811
|
+
return postLink(getState(), {
|
|
3751
3812
|
provider: "sms_otp",
|
|
3752
3813
|
phone,
|
|
3753
|
-
code
|
|
3754
|
-
session_token: sessionToken
|
|
3814
|
+
code
|
|
3755
3815
|
});
|
|
3756
3816
|
},
|
|
3757
3817
|
linkEmailOtp: async (email, code) => {
|
|
3758
3818
|
requireEmail(email);
|
|
3759
3819
|
requireOtpCode(code);
|
|
3760
|
-
|
|
3761
|
-
const sessionToken = s.identity.getSessionToken();
|
|
3762
|
-
if (!sessionToken) throw new AmbaApiError("UNAUTHORIZED", "no active session — call signIn* first");
|
|
3763
|
-
return postLink(s, {
|
|
3820
|
+
return postLink(getState(), {
|
|
3764
3821
|
provider: "email_otp",
|
|
3765
3822
|
email,
|
|
3766
|
-
code
|
|
3767
|
-
session_token: sessionToken
|
|
3823
|
+
code
|
|
3768
3824
|
});
|
|
3769
3825
|
},
|
|
3770
3826
|
linkEmailPassword: async (email, password) => {
|
|
3771
3827
|
requireEmail(email);
|
|
3772
3828
|
requirePassword(password);
|
|
3773
|
-
|
|
3774
|
-
const sessionToken = s.identity.getSessionToken();
|
|
3775
|
-
if (!sessionToken) throw new AmbaApiError("UNAUTHORIZED", "no active session — call signIn* first");
|
|
3776
|
-
return postLink(s, {
|
|
3829
|
+
return postLink(getState(), {
|
|
3777
3830
|
provider: "email_password",
|
|
3778
3831
|
email,
|
|
3779
|
-
password
|
|
3780
|
-
session_token: sessionToken
|
|
3832
|
+
password
|
|
3781
3833
|
});
|
|
3782
3834
|
},
|
|
3783
3835
|
unlinkSmsOtp: async () => unlinkProvider("sms_otp"),
|
|
@@ -3786,6 +3838,7 @@ const Amba = {
|
|
|
3786
3838
|
unlinkGoogle: async () => unlinkProvider("google"),
|
|
3787
3839
|
getCurrentIdentifiers: async () => {
|
|
3788
3840
|
const s = getState();
|
|
3841
|
+
await settleSessionInFlight(s);
|
|
3789
3842
|
if (!s.identity.getSessionToken()) throw new AmbaApiError("UNAUTHORIZED", "no active session — call signIn* first");
|
|
3790
3843
|
return (await s.http.get(P.authIdentifiers)).data.identifiers;
|
|
3791
3844
|
},
|
|
@@ -3812,13 +3865,20 @@ const Amba = {
|
|
|
3812
3865
|
refresh: async () => refreshSession(getState()),
|
|
3813
3866
|
me: async () => {
|
|
3814
3867
|
const s = getState();
|
|
3868
|
+
await settleSessionInFlight(s);
|
|
3815
3869
|
if (!s.identity.getSessionToken()) throw new AmbaApiError("UNAUTHORIZED", "no active session — call signIn* first");
|
|
3816
3870
|
return normalizeUserAuthProviders((await s.http.get(P.authMe)).data);
|
|
3817
3871
|
},
|
|
3818
3872
|
getSession: async () => {
|
|
3819
3873
|
const s = getState();
|
|
3874
|
+
await settleSessionInFlight(s);
|
|
3820
3875
|
if (!s.identity.isAuthenticated()) return null;
|
|
3821
|
-
|
|
3876
|
+
try {
|
|
3877
|
+
return snapshotSession(normalizeUserAuthProviders((await s.http.get(P.authMe)).data));
|
|
3878
|
+
} catch (err) {
|
|
3879
|
+
if (isSessionRefusedError(err) && !s.identity.isAuthenticated()) return null;
|
|
3880
|
+
throw err;
|
|
3881
|
+
}
|
|
3822
3882
|
},
|
|
3823
3883
|
getAnonymousId: async () => {
|
|
3824
3884
|
return getState().identity.getAnonymousId();
|