@witsauth/react-native-client 1.0.0 → 1.0.1
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/core/AuthClient.d.ts +8 -4
- package/dist/core/pkce.d.ts +2 -0
- package/dist/core/storage.d.ts +2 -0
- package/dist/index.es.js +179 -87
- package/dist/index.umd.js +1 -1
- package/dist/models/types.d.ts +27 -0
- package/dist/react/AuthContext.d.ts +5 -3
- package/package.json +7 -8
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { OAuth2NativeConfig, TokenInfo } from '../models/types';
|
|
1
|
+
import { AuthorizeOptions, LogoutOptions, OAuth2NativeConfig, TokenInfo, UserProfile } from '../models/types';
|
|
2
2
|
import { NativeTokenStorage } from './storage';
|
|
3
3
|
export declare class AuthClient {
|
|
4
4
|
private config;
|
|
@@ -14,12 +14,12 @@ export declare class AuthClient {
|
|
|
14
14
|
/**
|
|
15
15
|
* Builds the OAuth 2.0 + PKCE authorization URL and persists `code_verifier`, `state`, and `nonce`.
|
|
16
16
|
*/
|
|
17
|
-
buildAuthorizationUrl(): Promise<string>;
|
|
17
|
+
buildAuthorizationUrl(options?: AuthorizeOptions): Promise<string>;
|
|
18
18
|
/**
|
|
19
19
|
* Launches the system authentication browser (`ASWebAuthenticationSession` on iOS / Chrome Custom Tabs on Android),
|
|
20
20
|
* intercepts the deep-link callback (`redirectUri`), validates `state`, and exchanges `code` + `code_verifier` for tokens.
|
|
21
21
|
*/
|
|
22
|
-
authorize(): Promise<TokenInfo>;
|
|
22
|
+
authorize(options?: AuthorizeOptions): Promise<TokenInfo>;
|
|
23
23
|
/**
|
|
24
24
|
* Parses a redirect callback URL (`myapp://auth/callback?code=...&state=...`) and exchanges the code for tokens.
|
|
25
25
|
*/
|
|
@@ -28,6 +28,8 @@ export declare class AuthClient {
|
|
|
28
28
|
refreshAccessToken(): Promise<TokenInfo>;
|
|
29
29
|
private saveTokenInfo;
|
|
30
30
|
getAccessToken(): string | null;
|
|
31
|
+
getIdToken(): string | null;
|
|
32
|
+
getUserProfile<T extends UserProfile = UserProfile>(): T | null;
|
|
31
33
|
getRefreshToken(): string | null;
|
|
32
34
|
getExpiresAt(): number | null;
|
|
33
35
|
isAuthenticated(): boolean;
|
|
@@ -39,6 +41,8 @@ export declare class AuthClient {
|
|
|
39
41
|
navigateToAccountManagement(): Promise<void>;
|
|
40
42
|
/**
|
|
41
43
|
* Clears tokens from secure storage and revokes both refresh_token and access_token at `revokeEndpoint`.
|
|
44
|
+
* If `options.clearBrowserSession` is true and `logoutEndpoint` + `openAuthSession` are configured,
|
|
45
|
+
* launches the system browser to terminate the WitsAuth web session cookie.
|
|
42
46
|
*/
|
|
43
|
-
logout(): Promise<void>;
|
|
47
|
+
logout(options?: LogoutOptions): Promise<void>;
|
|
44
48
|
}
|
package/dist/core/pkce.d.ts
CHANGED
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
*/
|
|
10
10
|
export declare function sha256Bytes(asciiOrUtf8: string): Uint8Array;
|
|
11
11
|
export declare function base64UrlEncode(bytes: Uint8Array): string;
|
|
12
|
+
export declare function getRandomBytes(length: number): Uint8Array;
|
|
13
|
+
export declare function decodeJwtPayload<T = Record<string, unknown>>(token: string | null | undefined): T | null;
|
|
12
14
|
export declare function generateRandomString(length?: number): string;
|
|
13
15
|
export declare function generateCodeVerifier(): string;
|
|
14
16
|
export declare function generateState(): string;
|
package/dist/core/storage.d.ts
CHANGED
|
@@ -22,6 +22,8 @@ export declare class NativeTokenStorage {
|
|
|
22
22
|
export declare function createMemoryStorageAdapter(): OAuth2AsyncStorage;
|
|
23
23
|
/**
|
|
24
24
|
* Helper to create a storage adapter backed by `expo-secure-store`.
|
|
25
|
+
* Automatically splits values exceeding 2000 bytes across indexed chunk keys
|
|
26
|
+
* to prevent Expo SecureStore's 2048-byte limit errors on large JWTs.
|
|
25
27
|
* Pass the imported `SecureStore` module from `expo-secure-store`.
|
|
26
28
|
*/
|
|
27
29
|
export declare function createExpoSecureStoreAdapter(secureStoreModule: {
|
package/dist/index.es.js
CHANGED
|
@@ -122,21 +122,56 @@ function f(e) {
|
|
|
122
122
|
function p(e) {
|
|
123
123
|
let t = new Uint8Array(e), n = typeof globalThis < "u" ? globalThis : void 0;
|
|
124
124
|
if (n?.crypto && typeof n.crypto.getRandomValues == "function") return n.crypto.getRandomValues(t), t;
|
|
125
|
-
|
|
126
|
-
|
|
125
|
+
try {
|
|
126
|
+
let r = typeof n?.require == "function" ? n.require : void 0;
|
|
127
|
+
if (r) {
|
|
128
|
+
let n = r("expo-crypto");
|
|
129
|
+
if (n && typeof n.getRandomValues == "function") return n.getRandomValues(t), t;
|
|
130
|
+
if (n && typeof n.getRandomBytes == "function") {
|
|
131
|
+
let r = n.getRandomBytes(e);
|
|
132
|
+
return t.set(r.subarray(0, e)), t;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
} catch {}
|
|
136
|
+
throw Error("Cryptographically secure random number generator (CSPRNG) is unavailable. Please install and import \"react-native-get-random-values\" or \"expo-crypto\".");
|
|
137
|
+
}
|
|
138
|
+
function m(e) {
|
|
139
|
+
let t = e.replace(/-/g, "+").replace(/_/g, "/"), n = t + "=".repeat((4 - t.length % 4) % 4);
|
|
140
|
+
if (typeof atob == "function") {
|
|
141
|
+
let e = atob(n), t = new Uint8Array(e.length);
|
|
142
|
+
for (let n = 0; n < e.length; n++) t[n] = e.charCodeAt(n);
|
|
143
|
+
if (typeof TextDecoder < "u") return new TextDecoder().decode(t);
|
|
144
|
+
let r = "";
|
|
145
|
+
for (let e = 0; e < t.length; e++) r += String.fromCharCode(t[e]);
|
|
146
|
+
return decodeURIComponent(escape(r));
|
|
147
|
+
}
|
|
148
|
+
let r = typeof globalThis < "u" ? globalThis : void 0;
|
|
149
|
+
if (r?.Buffer) return r.Buffer.from(n, "base64").toString("utf8");
|
|
150
|
+
throw Error("Unable to decode base64url string.");
|
|
151
|
+
}
|
|
152
|
+
function h(e) {
|
|
153
|
+
if (!e || typeof e != "string") return null;
|
|
154
|
+
let t = e.split(".");
|
|
155
|
+
if (t.length < 2) return null;
|
|
156
|
+
try {
|
|
157
|
+
let e = m(t[1]);
|
|
158
|
+
return JSON.parse(e);
|
|
159
|
+
} catch {
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
127
162
|
}
|
|
128
|
-
function
|
|
163
|
+
function g(e = 64) {
|
|
129
164
|
let t = p(e), n = "";
|
|
130
165
|
for (let r = 0; r < e; r++) n += "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~"[t[r] % 66];
|
|
131
166
|
return n;
|
|
132
167
|
}
|
|
133
|
-
function
|
|
134
|
-
return
|
|
168
|
+
function _() {
|
|
169
|
+
return g(64);
|
|
135
170
|
}
|
|
136
|
-
function
|
|
137
|
-
return
|
|
171
|
+
function v() {
|
|
172
|
+
return g(32);
|
|
138
173
|
}
|
|
139
|
-
async function
|
|
174
|
+
async function y(e) {
|
|
140
175
|
let t = typeof globalThis < "u" ? globalThis : void 0;
|
|
141
176
|
if (t?.crypto?.subtle && typeof t.crypto.subtle.digest == "function") try {
|
|
142
177
|
let n = u(e), r = await t.crypto.subtle.digest("SHA-256", n);
|
|
@@ -145,17 +180,17 @@ async function _(e) {
|
|
|
145
180
|
return f(d(e));
|
|
146
181
|
}
|
|
147
182
|
// src/core/storage.ts
|
|
148
|
-
var
|
|
183
|
+
var b = Object.values(o), x = class {
|
|
149
184
|
backend;
|
|
150
185
|
cache = /* @__PURE__ */ new Map();
|
|
151
186
|
hydrated = !1;
|
|
152
187
|
hydratePromise = null;
|
|
153
188
|
constructor(e) {
|
|
154
|
-
this.backend = e ||
|
|
189
|
+
this.backend = e || S();
|
|
155
190
|
}
|
|
156
191
|
async hydrate() {
|
|
157
192
|
if (!this.hydrated) return this.hydratePromise ||= (async () => {
|
|
158
|
-
await Promise.all(
|
|
193
|
+
await Promise.all(b.map(async (e) => {
|
|
159
194
|
try {
|
|
160
195
|
let t = await this.backend.getItem(e);
|
|
161
196
|
t != null && this.cache.set(e, t);
|
|
@@ -197,7 +232,7 @@ var v = Object.values(o), y = class {
|
|
|
197
232
|
]);
|
|
198
233
|
}
|
|
199
234
|
};
|
|
200
|
-
function
|
|
235
|
+
function S() {
|
|
201
236
|
let e = /* @__PURE__ */ new Map();
|
|
202
237
|
return {
|
|
203
238
|
getItem: async (t) => e.get(t) ?? null,
|
|
@@ -209,14 +244,51 @@ function b() {
|
|
|
209
244
|
}
|
|
210
245
|
};
|
|
211
246
|
}
|
|
212
|
-
|
|
247
|
+
var C = 2e3, w = "__witsauth_chunked__:";
|
|
248
|
+
function T(e) {
|
|
249
|
+
let t = async (t) => {
|
|
250
|
+
try {
|
|
251
|
+
let n = await e.getItemAsync(t);
|
|
252
|
+
if (n && n.startsWith(w)) {
|
|
253
|
+
let r = parseInt(n.slice(21), 10);
|
|
254
|
+
if (Number.isFinite(r) && r > 0) {
|
|
255
|
+
let n = [];
|
|
256
|
+
for (let i = 0; i < r; i++) n.push(e.deleteItemAsync(`${t}__chunk_${i}`));
|
|
257
|
+
await Promise.all(n);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
} catch {}
|
|
261
|
+
};
|
|
213
262
|
return {
|
|
214
|
-
getItem: (t) =>
|
|
215
|
-
|
|
216
|
-
|
|
263
|
+
getItem: async (t) => {
|
|
264
|
+
let n = await e.getItemAsync(t);
|
|
265
|
+
if (!n) return null;
|
|
266
|
+
if (n.startsWith(w)) {
|
|
267
|
+
let r = parseInt(n.slice(21), 10);
|
|
268
|
+
if (!Number.isFinite(r) || r <= 0) return null;
|
|
269
|
+
let i = [];
|
|
270
|
+
for (let n = 0; n < r; n++) i.push(e.getItemAsync(`${t}__chunk_${n}`));
|
|
271
|
+
let a = await Promise.all(i);
|
|
272
|
+
return a.some((e) => e == null) ? null : a.join("");
|
|
273
|
+
}
|
|
274
|
+
return n;
|
|
275
|
+
},
|
|
276
|
+
setItem: async (n, r) => {
|
|
277
|
+
if (r.length <= C) {
|
|
278
|
+
await t(n), await e.setItemAsync(n, r);
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
await t(n);
|
|
282
|
+
let i = [];
|
|
283
|
+
for (let e = 0; e < r.length; e += C) i.push(r.slice(e, e + C));
|
|
284
|
+
await Promise.all(i.map((t, r) => e.setItemAsync(`${n}__chunk_${r}`, t))), await e.setItemAsync(n, `${w}${i.length}`);
|
|
285
|
+
},
|
|
286
|
+
removeItem: async (n) => {
|
|
287
|
+
await t(n), await e.deleteItemAsync(n);
|
|
288
|
+
}
|
|
217
289
|
};
|
|
218
290
|
}
|
|
219
|
-
function
|
|
291
|
+
function E(e, t = "com.witsauth.tokens") {
|
|
220
292
|
return {
|
|
221
293
|
getItem: async (n) => {
|
|
222
294
|
let r = await e.getGenericPassword({ service: `${t}.${n}` });
|
|
@@ -231,7 +303,7 @@ function S(e, t = "com.witsauth.tokens") {
|
|
|
231
303
|
};
|
|
232
304
|
}
|
|
233
305
|
// src/core/browser.ts
|
|
234
|
-
function
|
|
306
|
+
function D(e) {
|
|
235
307
|
return async (t, n) => {
|
|
236
308
|
let r = await e.openAuthSessionAsync(t, n);
|
|
237
309
|
return r.type === "success" && r.url ? {
|
|
@@ -240,7 +312,7 @@ function C(e) {
|
|
|
240
312
|
} : r.type === "cancel" ? { type: "cancel" } : { type: "dismiss" };
|
|
241
313
|
};
|
|
242
314
|
}
|
|
243
|
-
function
|
|
315
|
+
function O(e) {
|
|
244
316
|
return async (t, n) => {
|
|
245
317
|
if (!await e.isAvailable()) throw Error("InAppBrowser is not available on this device.");
|
|
246
318
|
let r = await e.openAuth(t, n, {
|
|
@@ -255,7 +327,7 @@ function w(e) {
|
|
|
255
327
|
} : r.type === "cancel" ? { type: "cancel" } : { type: "dismiss" };
|
|
256
328
|
};
|
|
257
329
|
}
|
|
258
|
-
function
|
|
330
|
+
function k(e) {
|
|
259
331
|
let t = e.indexOf("?");
|
|
260
332
|
if (t === -1) return {
|
|
261
333
|
code: null,
|
|
@@ -272,12 +344,12 @@ function T(e) {
|
|
|
272
344
|
};
|
|
273
345
|
}
|
|
274
346
|
// src/core/AuthClient.ts
|
|
275
|
-
var
|
|
347
|
+
var A = class {
|
|
276
348
|
config;
|
|
277
349
|
storage;
|
|
278
350
|
refreshPromise = null;
|
|
279
351
|
constructor(e, t) {
|
|
280
|
-
this.config = e, this.storage = t || new
|
|
352
|
+
this.config = e, this.storage = t || new x(e.storage);
|
|
281
353
|
}
|
|
282
354
|
async initialize() {
|
|
283
355
|
await this.storage.hydrate();
|
|
@@ -292,33 +364,34 @@ var E = class {
|
|
|
292
364
|
}, r = n[this.config.logLevel || "warn"] || 0, i = n[e] || 0;
|
|
293
365
|
i > 0 && r >= i && console[e](...t);
|
|
294
366
|
}
|
|
295
|
-
async buildAuthorizationUrl() {
|
|
367
|
+
async buildAuthorizationUrl(e) {
|
|
296
368
|
await this.storage.hydrate();
|
|
297
|
-
let
|
|
298
|
-
await this.storage.setItem(o.CODE_VERIFIER,
|
|
299
|
-
let
|
|
369
|
+
let t = _(), n = await y(t), r = v(), i = this.config.nonce || v();
|
|
370
|
+
await this.storage.setItem(o.CODE_VERIFIER, t), await this.storage.setItem(o.STATE, r), await this.storage.setItem(o.NONCE, i);
|
|
371
|
+
let a = new URLSearchParams({
|
|
300
372
|
client_id: this.config.clientId,
|
|
301
373
|
redirect_uri: this.config.redirectUri,
|
|
302
374
|
response_type: this.config.responseType || "code",
|
|
303
375
|
code_challenge_method: this.config.codeChallengeMethod || "S256",
|
|
304
|
-
code_challenge:
|
|
305
|
-
state:
|
|
306
|
-
nonce:
|
|
376
|
+
code_challenge: n,
|
|
377
|
+
state: r,
|
|
378
|
+
nonce: i
|
|
307
379
|
});
|
|
308
380
|
if (this.config.theme) {
|
|
309
381
|
let e = typeof this.config.theme == "function" ? this.config.theme() : this.config.theme;
|
|
310
|
-
e &&
|
|
382
|
+
e && a.append("theme", e);
|
|
311
383
|
}
|
|
312
|
-
|
|
384
|
+
let s = e?.scope ?? this.config.scope, c = e?.audience ?? this.config.audience;
|
|
385
|
+
return s && a.append("scope", s), c && a.append("audience", c), e?.prompt && a.append("prompt", e.prompt), `${this.config.authorizationEndpoint}?${a.toString()}`;
|
|
313
386
|
}
|
|
314
|
-
async authorize() {
|
|
387
|
+
async authorize(e) {
|
|
315
388
|
if (!this.config.openAuthSession) throw Error("No openAuthSession launcher configured. Pass createExpoWebBrowserLauncher(WebBrowser) or createInAppBrowserLauncher(InAppBrowser) in OAuth2NativeConfig.");
|
|
316
|
-
let
|
|
317
|
-
if (
|
|
318
|
-
return this.handleCallbackUrl(
|
|
389
|
+
let t = await this.buildAuthorizationUrl(e), n = await this.config.openAuthSession(t, this.config.redirectUri);
|
|
390
|
+
if (n.type !== "success" || !n.url) throw await this.storage.clearTemporary(), Error(`Authentication ${n.type}`);
|
|
391
|
+
return this.handleCallbackUrl(n.url);
|
|
319
392
|
}
|
|
320
393
|
async handleCallbackUrl(e) {
|
|
321
|
-
let { code: t, state: n, error: r, errorDescription: i } =
|
|
394
|
+
let { code: t, state: n, error: r, errorDescription: i } = k(e);
|
|
322
395
|
if (r) throw await this.storage.clearTemporary(), Error(i ? `${r}: ${i}` : r);
|
|
323
396
|
if (!t || !n) throw await this.storage.clearTemporary(), Error("Callback URL is missing required code or state parameters.");
|
|
324
397
|
return this.exchangeCodeForTokens(t, n);
|
|
@@ -403,6 +476,12 @@ var E = class {
|
|
|
403
476
|
getAccessToken() {
|
|
404
477
|
return this.storage.getItemSync(o.ACCESS_TOKEN);
|
|
405
478
|
}
|
|
479
|
+
getIdToken() {
|
|
480
|
+
return this.storage.getItemSync(o.ID_TOKEN);
|
|
481
|
+
}
|
|
482
|
+
getUserProfile() {
|
|
483
|
+
return h(this.getIdToken() || this.getAccessToken());
|
|
484
|
+
}
|
|
406
485
|
getRefreshToken() {
|
|
407
486
|
return this.storage.getItemSync(o.REFRESH_TOKEN);
|
|
408
487
|
}
|
|
@@ -436,24 +515,24 @@ var E = class {
|
|
|
436
515
|
let i = `${t}/account?ticket=${encodeURIComponent(r)}&return_to=${encodeURIComponent(this.config.redirectUri)}`;
|
|
437
516
|
await this.config.openAuthSession(i, this.config.redirectUri);
|
|
438
517
|
}
|
|
439
|
-
async logout() {
|
|
518
|
+
async logout(e) {
|
|
440
519
|
await this.storage.hydrate();
|
|
441
|
-
let
|
|
520
|
+
let t = this.getAccessToken(), n = await this.storage.getItem(o.REFRESH_TOKEN);
|
|
442
521
|
if (await this.storage.clearTokens(), await this.storage.clearTemporary(), this.config.revokeEndpoint) {
|
|
443
|
-
let
|
|
444
|
-
token:
|
|
522
|
+
let e = [...n ? [{
|
|
523
|
+
token: n,
|
|
445
524
|
token_type_hint: "refresh_token"
|
|
446
|
-
}] : [], ...
|
|
447
|
-
token:
|
|
525
|
+
}] : [], ...t ? [{
|
|
526
|
+
token: t,
|
|
448
527
|
token_type_hint: "access_token"
|
|
449
528
|
}] : []];
|
|
450
|
-
for (let
|
|
529
|
+
for (let t of e) try {
|
|
451
530
|
await fetch(this.config.revokeEndpoint, {
|
|
452
531
|
method: "POST",
|
|
453
532
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
454
533
|
body: new URLSearchParams({
|
|
455
|
-
token:
|
|
456
|
-
token_type_hint:
|
|
534
|
+
token: t.token,
|
|
535
|
+
token_type_hint: t.token_type_hint,
|
|
457
536
|
client_id: this.config.clientId
|
|
458
537
|
}).toString()
|
|
459
538
|
});
|
|
@@ -461,38 +540,49 @@ var E = class {
|
|
|
461
540
|
this.log("error", "Failed to revoke token during logout", e);
|
|
462
541
|
}
|
|
463
542
|
}
|
|
543
|
+
if (e?.clearBrowserSession && this.config.logoutEndpoint && this.config.openAuthSession) {
|
|
544
|
+
let e = new URLSearchParams({
|
|
545
|
+
client_id: this.config.clientId,
|
|
546
|
+
redirect_uri: this.config.redirectUri
|
|
547
|
+
}), t = this.config.logoutEndpoint.includes("?") ? "&" : "?", n = `${this.config.logoutEndpoint}${t}${e.toString()}`;
|
|
548
|
+
try {
|
|
549
|
+
await this.config.openAuthSession(n, this.config.redirectUri);
|
|
550
|
+
} catch (e) {
|
|
551
|
+
this.log("warn", "Browser logout session dismissed or failed", e);
|
|
552
|
+
}
|
|
553
|
+
}
|
|
464
554
|
}
|
|
465
|
-
},
|
|
555
|
+
}, j = e(null), M = ({ config: e, client: n, children: o, onLoginSuccess: s, onLoginError: c, subscribeAppState: l }) => {
|
|
466
556
|
let [u] = i(() => {
|
|
467
557
|
if (n) return n;
|
|
468
|
-
if (e) return new
|
|
558
|
+
if (e) return new A(e);
|
|
469
559
|
throw Error("AuthProvider requires either a `client` or `config` prop.");
|
|
470
|
-
}), [d, f] = i(!1), [p, m] = i(null), [h, g] = i(
|
|
471
|
-
if (await u.initialize(), u.isAuthenticated()) f(!0), m(u.getAccessToken());
|
|
560
|
+
}), [d, f] = i(!1), [p, m] = i(null), [h, g] = i(null), [_, v] = i(null), [y, b] = i(!0), [x, S] = i(null), C = t(async () => {
|
|
561
|
+
if (await u.initialize(), u.isAuthenticated()) f(!0), m(u.getAccessToken()), g(u.getIdToken()), v(u.getUserProfile());
|
|
472
562
|
else {
|
|
473
563
|
if (u.getRefreshToken()) try {
|
|
474
564
|
let e = await u.refreshAccessToken();
|
|
475
|
-
f(!0), m(e.accessToken);
|
|
565
|
+
f(!0), m(e.accessToken), g(u.getIdToken()), v(u.getUserProfile());
|
|
476
566
|
return;
|
|
477
567
|
} catch {
|
|
478
|
-
f(!1), m(null);
|
|
568
|
+
f(!1), m(null), g(null), v(null);
|
|
479
569
|
return;
|
|
480
570
|
}
|
|
481
|
-
f(!1), m(null);
|
|
571
|
+
f(!1), m(null), g(null), v(null);
|
|
482
572
|
}
|
|
483
573
|
}, [u]);
|
|
484
574
|
r(() => {
|
|
485
575
|
let e = !0;
|
|
486
576
|
return (async () => {
|
|
487
577
|
try {
|
|
488
|
-
await
|
|
578
|
+
await C();
|
|
489
579
|
} finally {
|
|
490
|
-
e &&
|
|
580
|
+
e && b(!1);
|
|
491
581
|
}
|
|
492
582
|
})(), () => {
|
|
493
583
|
e = !1;
|
|
494
584
|
};
|
|
495
|
-
}, [
|
|
585
|
+
}, [C]), r(() => {
|
|
496
586
|
if (!d) return;
|
|
497
587
|
let e, t = () => {
|
|
498
588
|
let n = u.getExpiresAt();
|
|
@@ -501,15 +591,15 @@ var E = class {
|
|
|
501
591
|
e = setTimeout(async () => {
|
|
502
592
|
try {
|
|
503
593
|
let e = await u.refreshAccessToken();
|
|
504
|
-
m(e.accessToken), f(!0), t();
|
|
594
|
+
m(e.accessToken), g(u.getIdToken()), v(u.getUserProfile()), f(!0), t();
|
|
505
595
|
} catch (n) {
|
|
506
|
-
u.log("error", "Background token refresh failed:", n), u.getAccessToken() ? e = setTimeout(t, 3e4) : (f(!1), m(null));
|
|
596
|
+
u.log("error", "Background token refresh failed:", n), u.getAccessToken() ? e = setTimeout(t, 3e4) : (f(!1), m(null), g(null), v(null));
|
|
507
597
|
}
|
|
508
598
|
}, Math.max(0, r));
|
|
509
599
|
};
|
|
510
600
|
t();
|
|
511
601
|
let n = l ? l((e) => {
|
|
512
|
-
e === "active" &&
|
|
602
|
+
e === "active" && C().catch(() => {});
|
|
513
603
|
}) : void 0;
|
|
514
604
|
return () => {
|
|
515
605
|
e && clearTimeout(e), n && n();
|
|
@@ -518,64 +608,66 @@ var E = class {
|
|
|
518
608
|
d,
|
|
519
609
|
u,
|
|
520
610
|
l,
|
|
521
|
-
|
|
611
|
+
C
|
|
522
612
|
]);
|
|
523
|
-
let
|
|
524
|
-
|
|
613
|
+
let w = t(async (e) => {
|
|
614
|
+
b(!0), S(null);
|
|
525
615
|
try {
|
|
526
|
-
let
|
|
527
|
-
m(
|
|
616
|
+
let t = await u.authorize(e);
|
|
617
|
+
m(t.accessToken), g(u.getIdToken()), v(u.getUserProfile()), f(!0), s?.();
|
|
528
618
|
} catch (e) {
|
|
529
619
|
let t = e instanceof Error ? e : /* @__PURE__ */ Error("Authentication failed");
|
|
530
|
-
|
|
620
|
+
S(t), c?.(t);
|
|
531
621
|
} finally {
|
|
532
|
-
|
|
622
|
+
b(!1);
|
|
533
623
|
}
|
|
534
624
|
}, [
|
|
535
625
|
u,
|
|
536
626
|
s,
|
|
537
627
|
c
|
|
538
|
-
]),
|
|
539
|
-
|
|
628
|
+
]), T = t(async (e) => {
|
|
629
|
+
b(!0);
|
|
540
630
|
try {
|
|
541
|
-
await u.logout(), f(!1), m(null);
|
|
631
|
+
await u.logout(e), f(!1), m(null), g(null), v(null);
|
|
542
632
|
} catch (e) {
|
|
543
|
-
|
|
633
|
+
S(e instanceof Error ? e : /* @__PURE__ */ Error("Logout failed"));
|
|
544
634
|
} finally {
|
|
545
|
-
|
|
635
|
+
b(!1);
|
|
546
636
|
}
|
|
547
|
-
}, [u]),
|
|
637
|
+
}, [u]), E = t(async () => {
|
|
548
638
|
if (await u.initialize(), u.isAuthenticated()) return u.getAccessToken();
|
|
549
639
|
try {
|
|
550
640
|
let e = await u.refreshAccessToken();
|
|
551
|
-
return m(e.accessToken), f(!0), e.accessToken;
|
|
641
|
+
return m(e.accessToken), g(u.getIdToken()), v(u.getUserProfile()), f(!0), e.accessToken;
|
|
552
642
|
} catch {
|
|
553
|
-
return f(!1), m(null), null;
|
|
643
|
+
return f(!1), m(null), g(null), v(null), null;
|
|
554
644
|
}
|
|
555
|
-
}, [u]),
|
|
556
|
-
await u.navigateToAccountManagement(), await
|
|
557
|
-
}, [u,
|
|
558
|
-
return /* @__PURE__ */ a(
|
|
645
|
+
}, [u]), D = t(async () => {
|
|
646
|
+
await u.navigateToAccountManagement(), await C();
|
|
647
|
+
}, [u, C]);
|
|
648
|
+
return /* @__PURE__ */ a(j.Provider, {
|
|
559
649
|
value: {
|
|
560
650
|
isAuthenticated: d,
|
|
561
|
-
isLoading:
|
|
651
|
+
isLoading: y,
|
|
562
652
|
accessToken: p,
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
653
|
+
idToken: h,
|
|
654
|
+
user: _,
|
|
655
|
+
error: x,
|
|
656
|
+
login: w,
|
|
657
|
+
logout: T,
|
|
658
|
+
getToken: E,
|
|
659
|
+
navigateToAccountManagement: D,
|
|
568
660
|
authClient: u
|
|
569
661
|
},
|
|
570
662
|
children: o
|
|
571
663
|
});
|
|
572
|
-
},
|
|
573
|
-
let e = n(
|
|
664
|
+
}, N = () => {
|
|
665
|
+
let e = n(j);
|
|
574
666
|
if (!e) throw Error("useAuth must be used within an AuthProvider");
|
|
575
667
|
return e;
|
|
576
668
|
};
|
|
577
669
|
// src/axios/interceptor.ts
|
|
578
|
-
function
|
|
670
|
+
function P(e, t, n = {}) {
|
|
579
671
|
let r = !1, i = [], a = (e, t = null) => {
|
|
580
672
|
i.forEach((n) => {
|
|
581
673
|
e ? n.reject(e) : n.resolve(t);
|
|
@@ -616,4 +708,4 @@ function A(e, t, n = {}) {
|
|
|
616
708
|
e.interceptors.request.eject(s), e.interceptors.response.eject(c);
|
|
617
709
|
};
|
|
618
710
|
}
|
|
619
|
-
export {
|
|
711
|
+
export { A as AuthClient, M as AuthProvider, x as NativeTokenStorage, o as STORAGE_KEYS, f as base64UrlEncode, y as createCodeChallenge, T as createExpoSecureStoreAdapter, D as createExpoWebBrowserLauncher, O as createInAppBrowserLauncher, E as createKeychainStorageAdapter, S as createMemoryStorageAdapter, h as decodeJwtPayload, _ as generateCodeVerifier, g as generateRandomString, v as generateState, p as getRandomBytes, k as parseCallbackUrl, P as setupAxiosInterceptors, d as sha256Bytes, N as useAuth };
|
package/dist/index.umd.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(e,t){typeof exports==`object`&&typeof module<`u`?t(exports,require("react"),require("react/jsx-runtime")):typeof define==`function`&&define.amd?define([`exports`,`react`,`react/jsx-runtime`],t):(e=typeof globalThis<`u`?globalThis:e||self,t(e.WitsAuthReactNativeClient={},e.React,e.jsxRuntime))})(this,function(e,t,n){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var r=Object.create,i=Object.defineProperty,a=Object.getOwnPropertyDescriptor,o=Object.getOwnPropertyNames,s=Object.getPrototypeOf,c=Object.prototype.hasOwnProperty,l=(e,t,n,r)=>{if(t&&typeof t==`object`||typeof t==`function`)for(var s=o(t),l=0,u=s.length,d;l<u;l++)d=s[l],!c.call(e,d)&&d!==n&&i(e,d,{get:(e=>t[e]).bind(null,d),enumerable:!(r=a(t,d))||r.enumerable});return e};t=((e,t,n)=>(n=e==null?{}:r(s(e)),l(i(n,`default`,{value:e,enumerable:!0}),e)))(t,1);var u={ACCESS_TOKEN:`witsauth_access_token`,REFRESH_TOKEN:`witsauth_refresh_token`,TOKEN_EXPIRES_AT:`witsauth_expires_at`,TOKEN_TYPE:`witsauth_token_type`,SCOPE:`witsauth_scope`,ID_TOKEN:`witsauth_id_token`,CODE_VERIFIER:`witsauth_code_verifier`,STATE:`witsauth_state`,NONCE:`witsauth_nonce`},d=`ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_`,f=new Uint32Array([1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298]);function p(e,t){return t>>>e|t<<32-e}function m(e){if(typeof TextEncoder<`u`)return new TextEncoder().encode(e);let t=[];for(let n=0;n<e.length;n++){let r=e.charCodeAt(n);r<128?t.push(r):r<2048?t.push(192|r>>6,128|r&63):r<55296||r>=57344?t.push(224|r>>12,128|r>>6&63,128|r&63):(n++,r=65536+((r&1023)<<10|e.charCodeAt(n)&1023),t.push(240|r>>18,128|r>>12&63,128|r>>6&63,128|r&63))}return new Uint8Array(t)}function h(e){let t=m(e),n=t.length*8,r=t.length+9+63>>6<<6,i=new Uint8Array(r);i.set(t),i[t.length]=128;let a=new DataView(i.buffer);a.setUint32(r-8,Math.floor(n/4294967296),!1),a.setUint32(r-4,n>>>0,!1);let o=1779033703,s=3144134277,c=1013904242,l=2773480762,u=1359893119,d=2600822924,h=528734635,g=1541459225,_=new Uint32Array(64);for(let e=0;e<r;e+=64){for(let t=0;t<16;t++)_[t]=a.getUint32(e+t*4,!1);for(let e=16;e<64;e++){let t=p(7,_[e-15])^p(18,_[e-15])^_[e-15]>>>3,n=p(17,_[e-2])^p(19,_[e-2])^_[e-2]>>>10;_[e]=_[e-16]+t+_[e-7]+n>>>0}let t=o,n=s,r=c,i=l,m=u,v=d,y=h,b=g;for(let e=0;e<64;e++){let a=p(6,m)^p(11,m)^p(25,m),o=m&v^~m&y,s=b+a+o+f[e]+_[e]>>>0,c=(p(2,t)^p(13,t)^p(22,t))+(t&n^t&r^n&r)>>>0;b=y,y=v,v=m,m=i+s>>>0,i=r,r=n,n=t,t=s+c>>>0}o=o+t>>>0,s=s+n>>>0,c=c+r>>>0,l=l+i>>>0,u=u+m>>>0,d=d+v>>>0,h=h+y>>>0,g=g+b>>>0}let v=new Uint8Array(32),y=new DataView(v.buffer);return y.setUint32(0,o,!1),y.setUint32(4,s,!1),y.setUint32(8,c,!1),y.setUint32(12,l,!1),y.setUint32(16,u,!1),y.setUint32(20,d,!1),y.setUint32(24,h,!1),y.setUint32(28,g,!1),v}function g(e){let t=``,n=e.length;for(let r=0;r<n;r+=3){let i=e[r],a=r+1<n?e[r+1]:0,o=r+2<n?e[r+2]:0;t+=d[i>>2],t+=d[(i&3)<<4|a>>4],r+1<n&&(t+=d[(a&15)<<2|o>>6]),r+2<n&&(t+=d[o&63])}return t}function _(e){let t=new Uint8Array(e),n=typeof globalThis<`u`?globalThis:void 0;if(n?.crypto&&typeof n.crypto.getRandomValues==`function`)return n.crypto.getRandomValues(t),t;for(let n=0;n<e;n++)t[n]=Math.floor(Math.random()*256);return t}function v(e=64){let t=_(e),n=``;for(let r=0;r<e;r++)n+=`ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~`[t[r]%66];return n}function y(){return v(64)}function b(){return v(32)}async function x(e){let t=typeof globalThis<`u`?globalThis:void 0;if(t?.crypto?.subtle&&typeof t.crypto.subtle.digest==`function`)try{let n=m(e),r=await t.crypto.subtle.digest(`SHA-256`,n);return g(new Uint8Array(r))}catch{}return g(h(e))}var S=Object.values(u),C=class{backend;cache=new Map;hydrated=!1;hydratePromise=null;constructor(e){this.backend=e||w()}async hydrate(){if(!this.hydrated)return this.hydratePromise||=(async()=>{await Promise.all(S.map(async e=>{try{let t=await this.backend.getItem(e);t!=null&&this.cache.set(e,t)}catch{}})),this.hydrated=!0})().finally(()=>{this.hydratePromise=null}),this.hydratePromise}getItemSync(e){return this.cache.get(e)??null}async getItem(e){if(this.cache.has(e))return this.cache.get(e);let t=await this.backend.getItem(e);return t==null?null:(this.cache.set(e,t),t)}async setItem(e,t){this.cache.set(e,t),await this.backend.setItem(e,t)}async removeItem(e){this.cache.delete(e),await this.backend.removeItem(e)}async clearTemporary(){await Promise.all([this.removeItem(u.CODE_VERIFIER),this.removeItem(u.STATE),this.removeItem(u.NONCE)])}async clearTokens(){await Promise.all([this.removeItem(u.ACCESS_TOKEN),this.removeItem(u.REFRESH_TOKEN),this.removeItem(u.TOKEN_EXPIRES_AT),this.removeItem(u.TOKEN_TYPE),this.removeItem(u.SCOPE),this.removeItem(u.ID_TOKEN)])}};function w(){let e=new Map;return{getItem:async t=>e.get(t)??null,setItem:async(t,n)=>{e.set(t,n)},removeItem:async t=>{e.delete(t)}}}function T(e){return{getItem:t=>e.getItemAsync(t),setItem:(t,n)=>e.setItemAsync(t,n),removeItem:t=>e.deleteItemAsync(t)}}function E(e,t=`com.witsauth.tokens`){return{getItem:async n=>{let r=await e.getGenericPassword({service:`${t}.${n}`});return r?r.password:null},setItem:async(n,r)=>{await e.setGenericPassword(n,r,{service:`${t}.${n}`})},removeItem:async n=>{await e.resetGenericPassword({service:`${t}.${n}`})}}}function D(e){return async(t,n)=>{let r=await e.openAuthSessionAsync(t,n);return r.type===`success`&&r.url?{type:`success`,url:r.url}:r.type===`cancel`?{type:`cancel`}:{type:`dismiss`}}}function O(e){return async(t,n)=>{if(!await e.isAvailable())throw Error(`InAppBrowser is not available on this device.`);let r=await e.openAuth(t,n,{ephemeralWebSession:!1,showTitle:!1,enableUrlBarHiding:!0,enableDefaultShare:!1});return r.type===`success`&&r.url?{type:`success`,url:r.url}:r.type===`cancel`?{type:`cancel`}:{type:`dismiss`}}}function k(e){let t=e.indexOf(`?`);if(t===-1)return{code:null,state:null,error:null,errorDescription:null};let n=e.indexOf(`#`,t),r=n===-1?e.slice(t+1):e.slice(t+1,n),i=new URLSearchParams(r);return{code:i.get(`code`),state:i.get(`state`),error:i.get(`error`),errorDescription:i.get(`error_description`)}}var A=class{config;storage;refreshPromise=null;constructor(e,t){this.config=e,this.storage=t||new C(e.storage)}async initialize(){await this.storage.hydrate()}log(e,...t){let n={none:0,error:1,warn:2,info:3,debug:4},r=n[this.config.logLevel||`warn`]||0,i=n[e]||0;i>0&&r>=i&&console[e](...t)}async buildAuthorizationUrl(){await this.storage.hydrate();let e=y(),t=await x(e),n=b(),r=this.config.nonce||b();await this.storage.setItem(u.CODE_VERIFIER,e),await this.storage.setItem(u.STATE,n),await this.storage.setItem(u.NONCE,r);let i=new URLSearchParams({client_id:this.config.clientId,redirect_uri:this.config.redirectUri,response_type:this.config.responseType||`code`,code_challenge_method:this.config.codeChallengeMethod||`S256`,code_challenge:t,state:n,nonce:r});if(this.config.theme){let e=typeof this.config.theme==`function`?this.config.theme():this.config.theme;e&&i.append(`theme`,e)}return this.config.scope&&i.append(`scope`,this.config.scope),this.config.audience&&i.append(`audience`,this.config.audience),`${this.config.authorizationEndpoint}?${i.toString()}`}async authorize(){if(!this.config.openAuthSession)throw Error(`No openAuthSession launcher configured. Pass createExpoWebBrowserLauncher(WebBrowser) or createInAppBrowserLauncher(InAppBrowser) in OAuth2NativeConfig.`);let e=await this.buildAuthorizationUrl(),t=await this.config.openAuthSession(e,this.config.redirectUri);if(t.type!==`success`||!t.url)throw await this.storage.clearTemporary(),Error(`Authentication ${t.type}`);return this.handleCallbackUrl(t.url)}async handleCallbackUrl(e){let{code:t,state:n,error:r,errorDescription:i}=k(e);if(r)throw await this.storage.clearTemporary(),Error(i?`${r}: ${i}`:r);if(!t||!n)throw await this.storage.clearTemporary(),Error(`Callback URL is missing required code or state parameters.`);return this.exchangeCodeForTokens(t,n)}async exchangeCodeForTokens(e,t){await this.storage.hydrate();let n=await this.storage.getItem(u.STATE),r=await this.storage.getItem(u.CODE_VERIFIER);if(!n||t!==n)throw await this.storage.clearTemporary(),Error(`Invalid state parameter. Possible CSRF attack.`);if(!r)throw await this.storage.clearTemporary(),Error(`Code verifier missing from storage.`);let i=new URLSearchParams({grant_type:`authorization_code`,client_id:this.config.clientId,redirect_uri:this.config.redirectUri,code:e,code_verifier:r}),a=await fetch(this.config.tokenEndpoint,{method:`POST`,headers:{"Content-Type":`application/x-www-form-urlencoded`},body:i.toString()});if(!a.ok)throw Error(`Failed to exchange code for tokens`);let o=await a.json(),s={accessToken:o.access_token,refreshToken:o.refresh_token,tokenType:o.token_type||`Bearer`,expiresAt:Date.now()+o.expires_in*1e3,scope:o.scope,idToken:o.id_token};return await this.saveTokenInfo(s),await this.storage.clearTemporary(),s}async refreshAccessToken(){return this.refreshPromise||=(async()=>{await this.storage.hydrate();let e=await this.storage.getItem(u.REFRESH_TOKEN);if(!e)throw Error(`No refresh token available`);let t=new URLSearchParams({grant_type:`refresh_token`,client_id:this.config.clientId,refresh_token:e}),n;try{n=await fetch(this.config.tokenEndpoint,{method:`POST`,headers:{"Content-Type":`application/x-www-form-urlencoded`},body:t.toString()})}catch(e){throw this.log(`error`,`Token refresh network request failed:`,e),e}if(!n.ok){let e={};try{e=await n.json()}catch{try{e={error:await n.text()}}catch{e={}}}this.log(`error`,`Token refresh failed with status:`,n.status,e),(n.status===400||n.status===401)&&await this.storage.clearTokens();let t=e.error_description||e.error||n.statusText||`Failed to refresh token`;throw Error(`Failed to refresh token (${n.status}): ${t}`)}let r=await n.json(),i={accessToken:r.access_token,refreshToken:r.refresh_token||e,tokenType:r.token_type||`Bearer`,expiresAt:Date.now()+r.expires_in*1e3,scope:r.scope,idToken:r.id_token};return await this.saveTokenInfo(i),i})().finally(()=>{this.refreshPromise=null}),this.refreshPromise}async saveTokenInfo(e){await this.storage.setItem(u.ACCESS_TOKEN,e.accessToken),await this.storage.setItem(u.TOKEN_EXPIRES_AT,e.expiresAt.toString()),await this.storage.setItem(u.TOKEN_TYPE,e.tokenType),e.refreshToken&&await this.storage.setItem(u.REFRESH_TOKEN,e.refreshToken),e.scope&&await this.storage.setItem(u.SCOPE,e.scope),e.idToken&&await this.storage.setItem(u.ID_TOKEN,e.idToken)}getAccessToken(){return this.storage.getItemSync(u.ACCESS_TOKEN)}getRefreshToken(){return this.storage.getItemSync(u.REFRESH_TOKEN)}getExpiresAt(){let e=this.storage.getItemSync(u.TOKEN_EXPIRES_AT);if(!e)return null;let t=Number(e);return Number.isFinite(t)?t:null}isAuthenticated(){let e=this.getExpiresAt();return e!==null&&Date.now()<e-3e4&&!!this.getAccessToken()}async navigateToAccountManagement(){await this.storage.hydrate();let e=await this.storage.getItem(u.REFRESH_TOKEN);if(!e)throw Error(`No refresh token available to access account management.`);if(!this.config.openAuthSession)throw Error(`No openAuthSession launcher configured. Pass createExpoWebBrowserLauncher(WebBrowser) or createInAppBrowserLauncher(InAppBrowser) in OAuth2NativeConfig.`);let t=this.config.authorizationEndpoint.replace(/\/authorize\/?$/,``),n=await fetch(`${t}/account/ticket`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({refreshToken:e,clientId:this.config.clientId,redirectUri:this.config.redirectUri})});if(!n.ok)throw Error(`Failed to request account management ticket (${n.status})`);let{ticket:r}=await n.json();if(!r)throw Error(`Account management ticket missing from server response.`);let i=`${t}/account?ticket=${encodeURIComponent(r)}&return_to=${encodeURIComponent(this.config.redirectUri)}`;await this.config.openAuthSession(i,this.config.redirectUri)}async logout(){await this.storage.hydrate();let e=this.getAccessToken(),t=await this.storage.getItem(u.REFRESH_TOKEN);if(await this.storage.clearTokens(),await this.storage.clearTemporary(),this.config.revokeEndpoint){let n=[...t?[{token:t,token_type_hint:`refresh_token`}]:[],...e?[{token:e,token_type_hint:`access_token`}]:[]];for(let e of n)try{await fetch(this.config.revokeEndpoint,{method:`POST`,headers:{"Content-Type":`application/x-www-form-urlencoded`},body:new URLSearchParams({token:e.token,token_type_hint:e.token_type_hint,client_id:this.config.clientId}).toString()})}catch(e){this.log(`error`,`Failed to revoke token during logout`,e)}}}},j=(0,t.createContext)(null),M=({config:e,client:r,children:i,onLoginSuccess:a,onLoginError:o,subscribeAppState:s})=>{let[c]=(0,t.useState)(()=>{if(r)return r;if(e)return new A(e);throw Error("AuthProvider requires either a `client` or `config` prop.")}),[l,u]=(0,t.useState)(!1),[d,f]=(0,t.useState)(null),[p,m]=(0,t.useState)(!0),[h,g]=(0,t.useState)(null),_=(0,t.useCallback)(async()=>{if(await c.initialize(),c.isAuthenticated())u(!0),f(c.getAccessToken());else{if(c.getRefreshToken())try{let e=await c.refreshAccessToken();u(!0),f(e.accessToken);return}catch{u(!1),f(null);return}u(!1),f(null)}},[c]);(0,t.useEffect)(()=>{let e=!0;return(async()=>{try{await _()}finally{e&&m(!1)}})(),()=>{e=!1}},[_]),(0,t.useEffect)(()=>{if(!l)return;let e,t=()=>{let n=c.getExpiresAt();if(!n)return;let r=n-Date.now()-6e4;e=setTimeout(async()=>{try{let e=await c.refreshAccessToken();f(e.accessToken),u(!0),t()}catch(n){c.log(`error`,`Background token refresh failed:`,n),c.getAccessToken()?e=setTimeout(t,3e4):(u(!1),f(null))}},Math.max(0,r))};t();let n=s?s(e=>{e===`active`&&_().catch(()=>{})}):void 0;return()=>{e&&clearTimeout(e),n&&n()}},[l,c,s,_]);let v=(0,t.useCallback)(async()=>{m(!0),g(null);try{let e=await c.authorize();f(e.accessToken),u(!0),a?.()}catch(e){let t=e instanceof Error?e:Error(`Authentication failed`);g(t),o?.(t)}finally{m(!1)}},[c,a,o]),y=(0,t.useCallback)(async()=>{m(!0);try{await c.logout(),u(!1),f(null)}catch(e){g(e instanceof Error?e:Error(`Logout failed`))}finally{m(!1)}},[c]),b=(0,t.useCallback)(async()=>{if(await c.initialize(),c.isAuthenticated())return c.getAccessToken();try{let e=await c.refreshAccessToken();return f(e.accessToken),u(!0),e.accessToken}catch{return u(!1),f(null),null}},[c]),x=(0,t.useCallback)(async()=>{await c.navigateToAccountManagement(),await _()},[c,_]);return(0,n.jsx)(j.Provider,{value:{isAuthenticated:l,isLoading:p,accessToken:d,error:h,login:v,logout:y,getToken:b,navigateToAccountManagement:x,authClient:c},children:i})},N=()=>{let e=(0,t.useContext)(j);if(!e)throw Error(`useAuth must be used within an AuthProvider`);return e};function P(e,t,n={}){let r=!1,i=[],a=(e,t=null)=>{i.forEach(n=>{e?n.reject(e):n.resolve(t)}),i=[]},o=e=>!e||!n.excludeUrls?!1:n.excludeUrls.some(t=>typeof t==`string`?e.includes(t):t.test(e)),s=e.interceptors.request.use(async e=>{if(o(e.url))return e;await t.initialize();let n=t.getAccessToken();if(!t.isAuthenticated()&&n)try{n=(await t.refreshAccessToken()).accessToken}catch{n=null}return n&&e.headers.set(`Authorization`,`Bearer ${n}`),e},e=>Promise.reject(e)),c=e.interceptors.response.use(e=>e,async s=>{let c=s.config;if(!c||o(c.url))return Promise.reject(s);if(s.response?.status===401&&!c._retry){if(r)return new Promise((e,t)=>{i.push({resolve:e,reject:t})}).then(t=>(t&&c.headers.set(`Authorization`,`Bearer ${t}`),e(c))).catch(e=>Promise.reject(e));c._retry=!0,r=!0;try{let n=(await t.refreshAccessToken()).accessToken;return a(null,n),c.headers.set(`Authorization`,`Bearer ${n}`),e(c)}catch(e){return a(e,null),await t.logout(),n.onUnauthorized&&n.onUnauthorized(),Promise.reject(e)}finally{r=!1}}return Promise.reject(s)});return()=>{e.interceptors.request.eject(s),e.interceptors.response.eject(c)}}e.AuthClient=A,e.AuthProvider=M,e.NativeTokenStorage=C,e.STORAGE_KEYS=u,e.base64UrlEncode=g,e.createCodeChallenge=x,e.createExpoSecureStoreAdapter=T,e.createExpoWebBrowserLauncher=D,e.createInAppBrowserLauncher=O,e.createKeychainStorageAdapter=E,e.createMemoryStorageAdapter=w,e.generateCodeVerifier=y,e.generateRandomString=v,e.generateState=b,e.parseCallbackUrl=k,e.setupAxiosInterceptors=P,e.sha256Bytes=h,e.useAuth=N});
|
|
1
|
+
(function(e,t){typeof exports==`object`&&typeof module<`u`?t(exports,require("react"),require("react/jsx-runtime")):typeof define==`function`&&define.amd?define([`exports`,`react`,`react/jsx-runtime`],t):(e=typeof globalThis<`u`?globalThis:e||self,t(e.WitsAuthReactNativeClient={},e.React,e.jsxRuntime))})(this,function(e,t,n){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var r=Object.create,i=Object.defineProperty,a=Object.getOwnPropertyDescriptor,o=Object.getOwnPropertyNames,s=Object.getPrototypeOf,c=Object.prototype.hasOwnProperty,l=(e,t,n,r)=>{if(t&&typeof t==`object`||typeof t==`function`)for(var s=o(t),l=0,u=s.length,d;l<u;l++)d=s[l],!c.call(e,d)&&d!==n&&i(e,d,{get:(e=>t[e]).bind(null,d),enumerable:!(r=a(t,d))||r.enumerable});return e};t=((e,t,n)=>(n=e==null?{}:r(s(e)),l(i(n,`default`,{value:e,enumerable:!0}),e)))(t,1);var u={ACCESS_TOKEN:`witsauth_access_token`,REFRESH_TOKEN:`witsauth_refresh_token`,TOKEN_EXPIRES_AT:`witsauth_expires_at`,TOKEN_TYPE:`witsauth_token_type`,SCOPE:`witsauth_scope`,ID_TOKEN:`witsauth_id_token`,CODE_VERIFIER:`witsauth_code_verifier`,STATE:`witsauth_state`,NONCE:`witsauth_nonce`},d=`ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_`,f=new Uint32Array([1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298]);function p(e,t){return t>>>e|t<<32-e}function m(e){if(typeof TextEncoder<`u`)return new TextEncoder().encode(e);let t=[];for(let n=0;n<e.length;n++){let r=e.charCodeAt(n);r<128?t.push(r):r<2048?t.push(192|r>>6,128|r&63):r<55296||r>=57344?t.push(224|r>>12,128|r>>6&63,128|r&63):(n++,r=65536+((r&1023)<<10|e.charCodeAt(n)&1023),t.push(240|r>>18,128|r>>12&63,128|r>>6&63,128|r&63))}return new Uint8Array(t)}function h(e){let t=m(e),n=t.length*8,r=t.length+9+63>>6<<6,i=new Uint8Array(r);i.set(t),i[t.length]=128;let a=new DataView(i.buffer);a.setUint32(r-8,Math.floor(n/4294967296),!1),a.setUint32(r-4,n>>>0,!1);let o=1779033703,s=3144134277,c=1013904242,l=2773480762,u=1359893119,d=2600822924,h=528734635,g=1541459225,_=new Uint32Array(64);for(let e=0;e<r;e+=64){for(let t=0;t<16;t++)_[t]=a.getUint32(e+t*4,!1);for(let e=16;e<64;e++){let t=p(7,_[e-15])^p(18,_[e-15])^_[e-15]>>>3,n=p(17,_[e-2])^p(19,_[e-2])^_[e-2]>>>10;_[e]=_[e-16]+t+_[e-7]+n>>>0}let t=o,n=s,r=c,i=l,m=u,v=d,y=h,b=g;for(let e=0;e<64;e++){let a=p(6,m)^p(11,m)^p(25,m),o=m&v^~m&y,s=b+a+o+f[e]+_[e]>>>0,c=(p(2,t)^p(13,t)^p(22,t))+(t&n^t&r^n&r)>>>0;b=y,y=v,v=m,m=i+s>>>0,i=r,r=n,n=t,t=s+c>>>0}o=o+t>>>0,s=s+n>>>0,c=c+r>>>0,l=l+i>>>0,u=u+m>>>0,d=d+v>>>0,h=h+y>>>0,g=g+b>>>0}let v=new Uint8Array(32),y=new DataView(v.buffer);return y.setUint32(0,o,!1),y.setUint32(4,s,!1),y.setUint32(8,c,!1),y.setUint32(12,l,!1),y.setUint32(16,u,!1),y.setUint32(20,d,!1),y.setUint32(24,h,!1),y.setUint32(28,g,!1),v}function g(e){let t=``,n=e.length;for(let r=0;r<n;r+=3){let i=e[r],a=r+1<n?e[r+1]:0,o=r+2<n?e[r+2]:0;t+=d[i>>2],t+=d[(i&3)<<4|a>>4],r+1<n&&(t+=d[(a&15)<<2|o>>6]),r+2<n&&(t+=d[o&63])}return t}function _(e){let t=new Uint8Array(e),n=typeof globalThis<`u`?globalThis:void 0;if(n?.crypto&&typeof n.crypto.getRandomValues==`function`)return n.crypto.getRandomValues(t),t;try{let r=typeof n?.require==`function`?n.require:void 0;if(r){let n=r(`expo-crypto`);if(n&&typeof n.getRandomValues==`function`)return n.getRandomValues(t),t;if(n&&typeof n.getRandomBytes==`function`){let r=n.getRandomBytes(e);return t.set(r.subarray(0,e)),t}}}catch{}throw Error(`Cryptographically secure random number generator (CSPRNG) is unavailable. Please install and import "react-native-get-random-values" or "expo-crypto".`)}function v(e){let t=e.replace(/-/g,`+`).replace(/_/g,`/`),n=t+`=`.repeat((4-t.length%4)%4);if(typeof atob==`function`){let e=atob(n),t=new Uint8Array(e.length);for(let n=0;n<e.length;n++)t[n]=e.charCodeAt(n);if(typeof TextDecoder<`u`)return new TextDecoder().decode(t);let r=``;for(let e=0;e<t.length;e++)r+=String.fromCharCode(t[e]);return decodeURIComponent(escape(r))}let r=typeof globalThis<`u`?globalThis:void 0;if(r?.Buffer)return r.Buffer.from(n,`base64`).toString(`utf8`);throw Error(`Unable to decode base64url string.`)}function y(e){if(!e||typeof e!=`string`)return null;let t=e.split(`.`);if(t.length<2)return null;try{let e=v(t[1]);return JSON.parse(e)}catch{return null}}function b(e=64){let t=_(e),n=``;for(let r=0;r<e;r++)n+=`ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~`[t[r]%66];return n}function x(){return b(64)}function S(){return b(32)}async function C(e){let t=typeof globalThis<`u`?globalThis:void 0;if(t?.crypto?.subtle&&typeof t.crypto.subtle.digest==`function`)try{let n=m(e),r=await t.crypto.subtle.digest(`SHA-256`,n);return g(new Uint8Array(r))}catch{}return g(h(e))}var w=Object.values(u),T=class{backend;cache=new Map;hydrated=!1;hydratePromise=null;constructor(e){this.backend=e||E()}async hydrate(){if(!this.hydrated)return this.hydratePromise||=(async()=>{await Promise.all(w.map(async e=>{try{let t=await this.backend.getItem(e);t!=null&&this.cache.set(e,t)}catch{}})),this.hydrated=!0})().finally(()=>{this.hydratePromise=null}),this.hydratePromise}getItemSync(e){return this.cache.get(e)??null}async getItem(e){if(this.cache.has(e))return this.cache.get(e);let t=await this.backend.getItem(e);return t==null?null:(this.cache.set(e,t),t)}async setItem(e,t){this.cache.set(e,t),await this.backend.setItem(e,t)}async removeItem(e){this.cache.delete(e),await this.backend.removeItem(e)}async clearTemporary(){await Promise.all([this.removeItem(u.CODE_VERIFIER),this.removeItem(u.STATE),this.removeItem(u.NONCE)])}async clearTokens(){await Promise.all([this.removeItem(u.ACCESS_TOKEN),this.removeItem(u.REFRESH_TOKEN),this.removeItem(u.TOKEN_EXPIRES_AT),this.removeItem(u.TOKEN_TYPE),this.removeItem(u.SCOPE),this.removeItem(u.ID_TOKEN)])}};function E(){let e=new Map;return{getItem:async t=>e.get(t)??null,setItem:async(t,n)=>{e.set(t,n)},removeItem:async t=>{e.delete(t)}}}var D=2e3,O=`__witsauth_chunked__:`;function k(e){let t=async t=>{try{let n=await e.getItemAsync(t);if(n&&n.startsWith(O)){let r=parseInt(n.slice(21),10);if(Number.isFinite(r)&&r>0){let n=[];for(let i=0;i<r;i++)n.push(e.deleteItemAsync(`${t}__chunk_${i}`));await Promise.all(n)}}}catch{}};return{getItem:async t=>{let n=await e.getItemAsync(t);if(!n)return null;if(n.startsWith(O)){let r=parseInt(n.slice(21),10);if(!Number.isFinite(r)||r<=0)return null;let i=[];for(let n=0;n<r;n++)i.push(e.getItemAsync(`${t}__chunk_${n}`));let a=await Promise.all(i);return a.some(e=>e==null)?null:a.join(``)}return n},setItem:async(n,r)=>{if(r.length<=D){await t(n),await e.setItemAsync(n,r);return}await t(n);let i=[];for(let e=0;e<r.length;e+=D)i.push(r.slice(e,e+D));await Promise.all(i.map((t,r)=>e.setItemAsync(`${n}__chunk_${r}`,t))),await e.setItemAsync(n,`${O}${i.length}`)},removeItem:async n=>{await t(n),await e.deleteItemAsync(n)}}}function A(e,t=`com.witsauth.tokens`){return{getItem:async n=>{let r=await e.getGenericPassword({service:`${t}.${n}`});return r?r.password:null},setItem:async(n,r)=>{await e.setGenericPassword(n,r,{service:`${t}.${n}`})},removeItem:async n=>{await e.resetGenericPassword({service:`${t}.${n}`})}}}function j(e){return async(t,n)=>{let r=await e.openAuthSessionAsync(t,n);return r.type===`success`&&r.url?{type:`success`,url:r.url}:r.type===`cancel`?{type:`cancel`}:{type:`dismiss`}}}function M(e){return async(t,n)=>{if(!await e.isAvailable())throw Error(`InAppBrowser is not available on this device.`);let r=await e.openAuth(t,n,{ephemeralWebSession:!1,showTitle:!1,enableUrlBarHiding:!0,enableDefaultShare:!1});return r.type===`success`&&r.url?{type:`success`,url:r.url}:r.type===`cancel`?{type:`cancel`}:{type:`dismiss`}}}function N(e){let t=e.indexOf(`?`);if(t===-1)return{code:null,state:null,error:null,errorDescription:null};let n=e.indexOf(`#`,t),r=n===-1?e.slice(t+1):e.slice(t+1,n),i=new URLSearchParams(r);return{code:i.get(`code`),state:i.get(`state`),error:i.get(`error`),errorDescription:i.get(`error_description`)}}var P=class{config;storage;refreshPromise=null;constructor(e,t){this.config=e,this.storage=t||new T(e.storage)}async initialize(){await this.storage.hydrate()}log(e,...t){let n={none:0,error:1,warn:2,info:3,debug:4},r=n[this.config.logLevel||`warn`]||0,i=n[e]||0;i>0&&r>=i&&console[e](...t)}async buildAuthorizationUrl(e){await this.storage.hydrate();let t=x(),n=await C(t),r=S(),i=this.config.nonce||S();await this.storage.setItem(u.CODE_VERIFIER,t),await this.storage.setItem(u.STATE,r),await this.storage.setItem(u.NONCE,i);let a=new URLSearchParams({client_id:this.config.clientId,redirect_uri:this.config.redirectUri,response_type:this.config.responseType||`code`,code_challenge_method:this.config.codeChallengeMethod||`S256`,code_challenge:n,state:r,nonce:i});if(this.config.theme){let e=typeof this.config.theme==`function`?this.config.theme():this.config.theme;e&&a.append(`theme`,e)}let o=e?.scope??this.config.scope,s=e?.audience??this.config.audience;return o&&a.append(`scope`,o),s&&a.append(`audience`,s),e?.prompt&&a.append(`prompt`,e.prompt),`${this.config.authorizationEndpoint}?${a.toString()}`}async authorize(e){if(!this.config.openAuthSession)throw Error(`No openAuthSession launcher configured. Pass createExpoWebBrowserLauncher(WebBrowser) or createInAppBrowserLauncher(InAppBrowser) in OAuth2NativeConfig.`);let t=await this.buildAuthorizationUrl(e),n=await this.config.openAuthSession(t,this.config.redirectUri);if(n.type!==`success`||!n.url)throw await this.storage.clearTemporary(),Error(`Authentication ${n.type}`);return this.handleCallbackUrl(n.url)}async handleCallbackUrl(e){let{code:t,state:n,error:r,errorDescription:i}=N(e);if(r)throw await this.storage.clearTemporary(),Error(i?`${r}: ${i}`:r);if(!t||!n)throw await this.storage.clearTemporary(),Error(`Callback URL is missing required code or state parameters.`);return this.exchangeCodeForTokens(t,n)}async exchangeCodeForTokens(e,t){await this.storage.hydrate();let n=await this.storage.getItem(u.STATE),r=await this.storage.getItem(u.CODE_VERIFIER);if(!n||t!==n)throw await this.storage.clearTemporary(),Error(`Invalid state parameter. Possible CSRF attack.`);if(!r)throw await this.storage.clearTemporary(),Error(`Code verifier missing from storage.`);let i=new URLSearchParams({grant_type:`authorization_code`,client_id:this.config.clientId,redirect_uri:this.config.redirectUri,code:e,code_verifier:r}),a=await fetch(this.config.tokenEndpoint,{method:`POST`,headers:{"Content-Type":`application/x-www-form-urlencoded`},body:i.toString()});if(!a.ok)throw Error(`Failed to exchange code for tokens`);let o=await a.json(),s={accessToken:o.access_token,refreshToken:o.refresh_token,tokenType:o.token_type||`Bearer`,expiresAt:Date.now()+o.expires_in*1e3,scope:o.scope,idToken:o.id_token};return await this.saveTokenInfo(s),await this.storage.clearTemporary(),s}async refreshAccessToken(){return this.refreshPromise||=(async()=>{await this.storage.hydrate();let e=await this.storage.getItem(u.REFRESH_TOKEN);if(!e)throw Error(`No refresh token available`);let t=new URLSearchParams({grant_type:`refresh_token`,client_id:this.config.clientId,refresh_token:e}),n;try{n=await fetch(this.config.tokenEndpoint,{method:`POST`,headers:{"Content-Type":`application/x-www-form-urlencoded`},body:t.toString()})}catch(e){throw this.log(`error`,`Token refresh network request failed:`,e),e}if(!n.ok){let e={};try{e=await n.json()}catch{try{e={error:await n.text()}}catch{e={}}}this.log(`error`,`Token refresh failed with status:`,n.status,e),(n.status===400||n.status===401)&&await this.storage.clearTokens();let t=e.error_description||e.error||n.statusText||`Failed to refresh token`;throw Error(`Failed to refresh token (${n.status}): ${t}`)}let r=await n.json(),i={accessToken:r.access_token,refreshToken:r.refresh_token||e,tokenType:r.token_type||`Bearer`,expiresAt:Date.now()+r.expires_in*1e3,scope:r.scope,idToken:r.id_token};return await this.saveTokenInfo(i),i})().finally(()=>{this.refreshPromise=null}),this.refreshPromise}async saveTokenInfo(e){await this.storage.setItem(u.ACCESS_TOKEN,e.accessToken),await this.storage.setItem(u.TOKEN_EXPIRES_AT,e.expiresAt.toString()),await this.storage.setItem(u.TOKEN_TYPE,e.tokenType),e.refreshToken&&await this.storage.setItem(u.REFRESH_TOKEN,e.refreshToken),e.scope&&await this.storage.setItem(u.SCOPE,e.scope),e.idToken&&await this.storage.setItem(u.ID_TOKEN,e.idToken)}getAccessToken(){return this.storage.getItemSync(u.ACCESS_TOKEN)}getIdToken(){return this.storage.getItemSync(u.ID_TOKEN)}getUserProfile(){return y(this.getIdToken()||this.getAccessToken())}getRefreshToken(){return this.storage.getItemSync(u.REFRESH_TOKEN)}getExpiresAt(){let e=this.storage.getItemSync(u.TOKEN_EXPIRES_AT);if(!e)return null;let t=Number(e);return Number.isFinite(t)?t:null}isAuthenticated(){let e=this.getExpiresAt();return e!==null&&Date.now()<e-3e4&&!!this.getAccessToken()}async navigateToAccountManagement(){await this.storage.hydrate();let e=await this.storage.getItem(u.REFRESH_TOKEN);if(!e)throw Error(`No refresh token available to access account management.`);if(!this.config.openAuthSession)throw Error(`No openAuthSession launcher configured. Pass createExpoWebBrowserLauncher(WebBrowser) or createInAppBrowserLauncher(InAppBrowser) in OAuth2NativeConfig.`);let t=this.config.authorizationEndpoint.replace(/\/authorize\/?$/,``),n=await fetch(`${t}/account/ticket`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({refreshToken:e,clientId:this.config.clientId,redirectUri:this.config.redirectUri})});if(!n.ok)throw Error(`Failed to request account management ticket (${n.status})`);let{ticket:r}=await n.json();if(!r)throw Error(`Account management ticket missing from server response.`);let i=`${t}/account?ticket=${encodeURIComponent(r)}&return_to=${encodeURIComponent(this.config.redirectUri)}`;await this.config.openAuthSession(i,this.config.redirectUri)}async logout(e){await this.storage.hydrate();let t=this.getAccessToken(),n=await this.storage.getItem(u.REFRESH_TOKEN);if(await this.storage.clearTokens(),await this.storage.clearTemporary(),this.config.revokeEndpoint){let e=[...n?[{token:n,token_type_hint:`refresh_token`}]:[],...t?[{token:t,token_type_hint:`access_token`}]:[]];for(let t of e)try{await fetch(this.config.revokeEndpoint,{method:`POST`,headers:{"Content-Type":`application/x-www-form-urlencoded`},body:new URLSearchParams({token:t.token,token_type_hint:t.token_type_hint,client_id:this.config.clientId}).toString()})}catch(e){this.log(`error`,`Failed to revoke token during logout`,e)}}if(e?.clearBrowserSession&&this.config.logoutEndpoint&&this.config.openAuthSession){let e=new URLSearchParams({client_id:this.config.clientId,redirect_uri:this.config.redirectUri}),t=this.config.logoutEndpoint.includes(`?`)?`&`:`?`,n=`${this.config.logoutEndpoint}${t}${e.toString()}`;try{await this.config.openAuthSession(n,this.config.redirectUri)}catch(e){this.log(`warn`,`Browser logout session dismissed or failed`,e)}}}},F=(0,t.createContext)(null),I=({config:e,client:r,children:i,onLoginSuccess:a,onLoginError:o,subscribeAppState:s})=>{let[c]=(0,t.useState)(()=>{if(r)return r;if(e)return new P(e);throw Error("AuthProvider requires either a `client` or `config` prop.")}),[l,u]=(0,t.useState)(!1),[d,f]=(0,t.useState)(null),[p,m]=(0,t.useState)(null),[h,g]=(0,t.useState)(null),[_,v]=(0,t.useState)(!0),[y,b]=(0,t.useState)(null),x=(0,t.useCallback)(async()=>{if(await c.initialize(),c.isAuthenticated())u(!0),f(c.getAccessToken()),m(c.getIdToken()),g(c.getUserProfile());else{if(c.getRefreshToken())try{let e=await c.refreshAccessToken();u(!0),f(e.accessToken),m(c.getIdToken()),g(c.getUserProfile());return}catch{u(!1),f(null),m(null),g(null);return}u(!1),f(null),m(null),g(null)}},[c]);(0,t.useEffect)(()=>{let e=!0;return(async()=>{try{await x()}finally{e&&v(!1)}})(),()=>{e=!1}},[x]),(0,t.useEffect)(()=>{if(!l)return;let e,t=()=>{let n=c.getExpiresAt();if(!n)return;let r=n-Date.now()-6e4;e=setTimeout(async()=>{try{let e=await c.refreshAccessToken();f(e.accessToken),m(c.getIdToken()),g(c.getUserProfile()),u(!0),t()}catch(n){c.log(`error`,`Background token refresh failed:`,n),c.getAccessToken()?e=setTimeout(t,3e4):(u(!1),f(null),m(null),g(null))}},Math.max(0,r))};t();let n=s?s(e=>{e===`active`&&x().catch(()=>{})}):void 0;return()=>{e&&clearTimeout(e),n&&n()}},[l,c,s,x]);let S=(0,t.useCallback)(async e=>{v(!0),b(null);try{let t=await c.authorize(e);f(t.accessToken),m(c.getIdToken()),g(c.getUserProfile()),u(!0),a?.()}catch(e){let t=e instanceof Error?e:Error(`Authentication failed`);b(t),o?.(t)}finally{v(!1)}},[c,a,o]),C=(0,t.useCallback)(async e=>{v(!0);try{await c.logout(e),u(!1),f(null),m(null),g(null)}catch(e){b(e instanceof Error?e:Error(`Logout failed`))}finally{v(!1)}},[c]),w=(0,t.useCallback)(async()=>{if(await c.initialize(),c.isAuthenticated())return c.getAccessToken();try{let e=await c.refreshAccessToken();return f(e.accessToken),m(c.getIdToken()),g(c.getUserProfile()),u(!0),e.accessToken}catch{return u(!1),f(null),m(null),g(null),null}},[c]),T=(0,t.useCallback)(async()=>{await c.navigateToAccountManagement(),await x()},[c,x]);return(0,n.jsx)(F.Provider,{value:{isAuthenticated:l,isLoading:_,accessToken:d,idToken:p,user:h,error:y,login:S,logout:C,getToken:w,navigateToAccountManagement:T,authClient:c},children:i})},L=()=>{let e=(0,t.useContext)(F);if(!e)throw Error(`useAuth must be used within an AuthProvider`);return e};function R(e,t,n={}){let r=!1,i=[],a=(e,t=null)=>{i.forEach(n=>{e?n.reject(e):n.resolve(t)}),i=[]},o=e=>!e||!n.excludeUrls?!1:n.excludeUrls.some(t=>typeof t==`string`?e.includes(t):t.test(e)),s=e.interceptors.request.use(async e=>{if(o(e.url))return e;await t.initialize();let n=t.getAccessToken();if(!t.isAuthenticated()&&n)try{n=(await t.refreshAccessToken()).accessToken}catch{n=null}return n&&e.headers.set(`Authorization`,`Bearer ${n}`),e},e=>Promise.reject(e)),c=e.interceptors.response.use(e=>e,async s=>{let c=s.config;if(!c||o(c.url))return Promise.reject(s);if(s.response?.status===401&&!c._retry){if(r)return new Promise((e,t)=>{i.push({resolve:e,reject:t})}).then(t=>(t&&c.headers.set(`Authorization`,`Bearer ${t}`),e(c))).catch(e=>Promise.reject(e));c._retry=!0,r=!0;try{let n=(await t.refreshAccessToken()).accessToken;return a(null,n),c.headers.set(`Authorization`,`Bearer ${n}`),e(c)}catch(e){return a(e,null),await t.logout(),n.onUnauthorized&&n.onUnauthorized(),Promise.reject(e)}finally{r=!1}}return Promise.reject(s)});return()=>{e.interceptors.request.eject(s),e.interceptors.response.eject(c)}}e.AuthClient=P,e.AuthProvider=I,e.NativeTokenStorage=T,e.STORAGE_KEYS=u,e.base64UrlEncode=g,e.createCodeChallenge=C,e.createExpoSecureStoreAdapter=k,e.createExpoWebBrowserLauncher=j,e.createInAppBrowserLauncher=M,e.createKeychainStorageAdapter=A,e.createMemoryStorageAdapter=E,e.decodeJwtPayload=y,e.generateCodeVerifier=x,e.generateRandomString=b,e.generateState=S,e.getRandomBytes=_,e.parseCallbackUrl=N,e.setupAxiosInterceptors=R,e.sha256Bytes=h,e.useAuth=L});
|
package/dist/models/types.d.ts
CHANGED
|
@@ -91,6 +91,33 @@ export interface TokenInfo {
|
|
|
91
91
|
scope?: string;
|
|
92
92
|
idToken?: string;
|
|
93
93
|
}
|
|
94
|
+
export interface UserProfile {
|
|
95
|
+
sub?: string;
|
|
96
|
+
name?: string;
|
|
97
|
+
given_name?: string;
|
|
98
|
+
family_name?: string;
|
|
99
|
+
email?: string;
|
|
100
|
+
email_verified?: boolean;
|
|
101
|
+
phone_number?: string;
|
|
102
|
+
phone_number_verified?: boolean;
|
|
103
|
+
roles?: string[];
|
|
104
|
+
permissions?: string[];
|
|
105
|
+
amr?: string[];
|
|
106
|
+
act?: {
|
|
107
|
+
sub: string;
|
|
108
|
+
ticket_ref?: string;
|
|
109
|
+
[key: string]: unknown;
|
|
110
|
+
};
|
|
111
|
+
[key: string]: unknown;
|
|
112
|
+
}
|
|
113
|
+
export interface AuthorizeOptions {
|
|
114
|
+
prompt?: 'login' | 'consent' | 'select_account';
|
|
115
|
+
scope?: string;
|
|
116
|
+
audience?: string;
|
|
117
|
+
}
|
|
118
|
+
export interface LogoutOptions {
|
|
119
|
+
clearBrowserSession?: boolean;
|
|
120
|
+
}
|
|
94
121
|
export declare const STORAGE_KEYS: {
|
|
95
122
|
readonly ACCESS_TOKEN: 'witsauth_access_token';
|
|
96
123
|
readonly REFRESH_TOKEN: 'witsauth_refresh_token';
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { AuthClient } from '../core/AuthClient';
|
|
3
|
-
import { OAuth2NativeConfig } from '../models/types';
|
|
3
|
+
import { AuthorizeOptions, LogoutOptions, OAuth2NativeConfig, UserProfile } from '../models/types';
|
|
4
4
|
export interface AuthContextType {
|
|
5
5
|
isAuthenticated: boolean;
|
|
6
6
|
isLoading: boolean;
|
|
7
7
|
accessToken: string | null;
|
|
8
|
+
idToken: string | null;
|
|
9
|
+
user: UserProfile | null;
|
|
8
10
|
error: Error | null;
|
|
9
|
-
login: () => Promise<void>;
|
|
10
|
-
logout: () => Promise<void>;
|
|
11
|
+
login: (options?: AuthorizeOptions) => Promise<void>;
|
|
12
|
+
logout: (options?: LogoutOptions) => Promise<void>;
|
|
11
13
|
getToken: () => Promise<string | null>;
|
|
12
14
|
navigateToAccountManagement: () => Promise<void>;
|
|
13
15
|
authClient: AuthClient;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@witsauth/react-native-client",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Official React Native client library (Expo and Bare React Native CLI) for integrating WitsAuth OAuth 2.0 + PKCE authentication on Android and iOS.",
|
|
5
5
|
"main": "./dist/index.umd.js",
|
|
6
6
|
"module": "./dist/index.es.js",
|
|
@@ -15,12 +15,6 @@
|
|
|
15
15
|
"files": [
|
|
16
16
|
"dist"
|
|
17
17
|
],
|
|
18
|
-
"scripts": {
|
|
19
|
-
"dev": "vite",
|
|
20
|
-
"build": "tsc && vite build && tsc -p tsconfig.build.json",
|
|
21
|
-
"test": "vitest run",
|
|
22
|
-
"prepublishOnly": "pnpm test && pnpm build"
|
|
23
|
-
},
|
|
24
18
|
"publishConfig": {
|
|
25
19
|
"access": "public"
|
|
26
20
|
},
|
|
@@ -61,5 +55,10 @@
|
|
|
61
55
|
"typescript": "^7.0.2",
|
|
62
56
|
"vite": "^8.1.5",
|
|
63
57
|
"vitest": "^4.1.10"
|
|
58
|
+
},
|
|
59
|
+
"scripts": {
|
|
60
|
+
"dev": "vite",
|
|
61
|
+
"build": "tsc && vite build && tsc -p tsconfig.build.json",
|
|
62
|
+
"test": "vitest run"
|
|
64
63
|
}
|
|
65
|
-
}
|
|
64
|
+
}
|