@multiplatform.one/keycloak 6.0.3 → 6.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,717 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ AfterAuth: () => AfterAuth,
34
+ AuthConfigContext: () => AuthConfigContext,
35
+ AuthProvider: () => AuthProvider,
36
+ Authenticated: () => Authenticated,
37
+ BaseKeycloak: () => BaseKeycloak,
38
+ Keycloak: () => Keycloak,
39
+ KeycloakConfigContext: () => KeycloakConfigContext,
40
+ KeycloakProvider: () => KeycloakProvider,
41
+ defaultAuthConfig: () => defaultAuthConfig,
42
+ getAllWalletAddresses: () => getAllWalletAddresses,
43
+ getKeycloakBearerToken: () => getKeycloakBearerToken,
44
+ getSession: () => getSession,
45
+ getWalletAddresses: () => getWalletAddresses,
46
+ getWalletsFromToken: () => getWalletsFromToken,
47
+ hasWallets: () => hasWallets,
48
+ isTokenExpired: () => isTokenExpired,
49
+ setNativeSession: () => setNativeSession,
50
+ useAuthConfig: () => useAuthConfig,
51
+ useKeycloak: () => useKeycloak,
52
+ useSession: () => useSession,
53
+ useTokensFromQuery: () => useTokensFromQuery,
54
+ useTokensFromStore: () => useTokensFromStore,
55
+ validOrRefreshableToken: () => validOrRefreshableToken,
56
+ withAuthenticated: () => withAuthenticated
57
+ });
58
+ module.exports = __toCommonJS(index_exports);
59
+
60
+ // src/Authenticated.tsx
61
+ var import_platform3 = require("@multiplatform.one/platform");
62
+ var import_react7 = require("react");
63
+ var import_react_native = require("react-native");
64
+
65
+ // src/hooks/useAuthConfig.ts
66
+ var import_react2 = require("react");
67
+
68
+ // src/authConfig.ts
69
+ var import_react = require("react");
70
+ var defaultAuthConfig = {};
71
+ var AuthConfigContext = (0, import_react.createContext)(defaultAuthConfig);
72
+
73
+ // src/hooks/useAuthConfig.ts
74
+ function useAuthConfig() {
75
+ return (0, import_react2.useContext)(AuthConfigContext);
76
+ }
77
+
78
+ // src/hooks/useTokensFromQuery/index.native.ts
79
+ var useTokensFromQuery = () => false;
80
+
81
+ // src/state.ts
82
+ var import_store = require("@multiplatform.one/store");
83
+ var import_platform = require("@multiplatform.one/platform");
84
+ var persist = import_platform.isIframe || import_platform.isTauri || !import_platform.isBrowser && !import_platform.isServer;
85
+ var authStore = (0, import_store.createStore)(
86
+ { idToken: "", refreshToken: "", token: "" },
87
+ { name: "auth", persist }
88
+ );
89
+ function useAuthStore() {
90
+ const state2 = (0, import_store.useStore)(authStore);
91
+ return {
92
+ ...state2,
93
+ setIdToken: (idToken) => authStore.setState((prev) => ({ ...prev, idToken })),
94
+ setRefreshToken: (refreshToken) => authStore.setState((prev) => ({ ...prev, refreshToken })),
95
+ setToken: (token) => authStore.setState((prev) => ({ ...prev, token })),
96
+ setTokens: (tokens) => authStore.setState((prev) => ({ ...prev, ...tokens }))
97
+ };
98
+ }
99
+
100
+ // src/token.ts
101
+ var import_jwt_decode = require("jwt-decode");
102
+ function getWalletsFromToken(token) {
103
+ try {
104
+ const decoded = (0, import_jwt_decode.jwtDecode)(token);
105
+ return decoded.crypto?.wallets ?? {};
106
+ } catch {
107
+ return {};
108
+ }
109
+ }
110
+ function getWalletAddresses(token, walletType = "ethereum") {
111
+ const wallets = getWalletsFromToken(token);
112
+ return wallets[walletType]?.addresses ?? [];
113
+ }
114
+ function hasWallets(token) {
115
+ const wallets = getWalletsFromToken(token);
116
+ return Object.values(wallets).some((info) => info.addresses && info.addresses.length > 0);
117
+ }
118
+ function getAllWalletAddresses(token) {
119
+ const wallets = getWalletsFromToken(token);
120
+ const result = [];
121
+ for (const [walletType, info] of Object.entries(wallets)) {
122
+ if (info.addresses) {
123
+ for (const address of info.addresses) {
124
+ result.push({ walletType, address });
125
+ }
126
+ }
127
+ }
128
+ return result;
129
+ }
130
+ function validOrRefreshableToken(token, refreshToken) {
131
+ if (typeof token === "undefined") return;
132
+ if (!token) return false;
133
+ if (typeof token !== "string") return token;
134
+ if (refreshToken) {
135
+ if (refreshToken === true || !isTokenExpired(refreshToken)) return token;
136
+ return false;
137
+ }
138
+ return isTokenExpired(token) ? false : token;
139
+ }
140
+ function isTokenExpired(token) {
141
+ const { exp } = (0, import_jwt_decode.jwtDecode)(token);
142
+ if (!exp) return false;
143
+ return Math.floor(Date.now() / 1e3) >= exp;
144
+ }
145
+
146
+ // src/hooks/useTokensFromState.ts
147
+ function useTokensFromStore() {
148
+ const authStore2 = useAuthStore();
149
+ return !!validOrRefreshableToken(authStore2.token, authStore2.refreshToken);
150
+ }
151
+
152
+ // src/keycloak/index.native.ts
153
+ var import_platform2 = require("@multiplatform.one/platform");
154
+ var import_react6 = require("react");
155
+
156
+ // src/keycloak/base.ts
157
+ var import_jwt_decode2 = require("jwt-decode");
158
+ var import_react4 = require("react");
159
+
160
+ // src/keycloak/context.ts
161
+ var import_react3 = require("react");
162
+ var KeycloakContext = (0, import_react3.createContext)(void 0);
163
+
164
+ // src/keycloak/base.ts
165
+ var BaseKeycloak = class {
166
+ constructor(config, input, idToken, refreshToken, login, logout) {
167
+ this.config = config;
168
+ this.authenticated = false;
169
+ this.clientId = this.config.clientId;
170
+ this.realm = this.config.realm;
171
+ if (typeof input === "string") {
172
+ this.token = input;
173
+ this.idToken = idToken;
174
+ this.refreshToken = refreshToken;
175
+ this._parseTokens();
176
+ } else if (typeof input === "object") {
177
+ this._handleInputObject(input);
178
+ }
179
+ this._login = login;
180
+ this._logout = logout;
181
+ this._sync();
182
+ }
183
+ _handleInputObject(input) {
184
+ if (typeof input.init === "function") {
185
+ this._keycloakClient = input;
186
+ } else {
187
+ this._mock = input;
188
+ }
189
+ }
190
+ _parseTokens() {
191
+ if (this.token && !this.tokenParsed) {
192
+ this.tokenParsed = (0, import_jwt_decode2.jwtDecode)(this.token);
193
+ }
194
+ if (this.idToken && !this.idTokenParsed) {
195
+ this.idTokenParsed = (0, import_jwt_decode2.jwtDecode)(this.idToken);
196
+ }
197
+ if (this.refreshToken && !this.refreshTokenParsed) {
198
+ this.refreshTokenParsed = (0, import_jwt_decode2.jwtDecode)(this.refreshToken);
199
+ }
200
+ }
201
+ _clear() {
202
+ this.authenticated = false;
203
+ this.email = void 0;
204
+ this.idToken = void 0;
205
+ this.idTokenParsed = void 0;
206
+ this.realmAccess = void 0;
207
+ this.refreshToken = void 0;
208
+ this.refreshTokenParsed = void 0;
209
+ this.resourceAccess = void 0;
210
+ this.sessionId = void 0;
211
+ this.subject = void 0;
212
+ this.token = void 0;
213
+ this.tokenParsed = void 0;
214
+ this.username = void 0;
215
+ }
216
+ _sync() {
217
+ if (this._mock) {
218
+ this.authenticated = true;
219
+ this.email = this._mock.email;
220
+ this.username = this._mock.username;
221
+ } else if (this._keycloakClient) {
222
+ this.authenticated = !!this._keycloakClient.authenticated;
223
+ this.email = this._keycloakClient?.tokenParsed?.email;
224
+ this.idToken = this._keycloakClient?.idToken;
225
+ this.idTokenParsed = this._keycloakClient?.idTokenParsed;
226
+ this.realmAccess = this._keycloakClient?.realmAccess;
227
+ this.refreshToken = this._keycloakClient?.refreshToken;
228
+ this.refreshTokenParsed = this._keycloakClient?.refreshTokenParsed;
229
+ this.resourceAccess = this._keycloakClient?.resourceAccess;
230
+ this.sessionId = this._keycloakClient?.sessionId;
231
+ this.subject = this._keycloakClient?.subject;
232
+ this.token = this._keycloakClient?.token;
233
+ this.tokenParsed = this._keycloakClient?.tokenParsed;
234
+ this.username = this.tokenParsed?.preferred_username;
235
+ if (this._keycloakClient.realm) this.realm = this._keycloakClient.realm;
236
+ if (this._keycloakClient.clientId) {
237
+ this.clientId = this._keycloakClient.clientId;
238
+ }
239
+ } else if (this.tokenParsed) {
240
+ this.clientId = this.tokenParsed.azp || this.config.clientId;
241
+ this.email = this.tokenParsed.email;
242
+ this.realm = this.tokenParsed.iss?.split("/").pop() || this.config.realm;
243
+ this.realmAccess = this.tokenParsed.realm_access;
244
+ this.resourceAccess = this.tokenParsed.resource_access;
245
+ this.sessionId = this.tokenParsed.session_state;
246
+ this.subject = this.tokenParsed.sub;
247
+ this.authenticated = !!(this.tokenParsed?.exp && this.idToken && this.tokenParsed.exp > Date.now() / 1e3);
248
+ this.username = this.tokenParsed?.preferred_username;
249
+ } else {
250
+ return this._clear();
251
+ }
252
+ }
253
+ async getUserInfo() {
254
+ if (!this.authenticated) return;
255
+ const response = await fetch(
256
+ `${this.config.url}/realms/${this.realm}/protocol/openid-connect/userinfo`,
257
+ {
258
+ method: "GET",
259
+ headers: {
260
+ Authorization: `Bearer ${this.token}`,
261
+ Accept: "application/json"
262
+ }
263
+ }
264
+ );
265
+ if (response.ok) return response.json();
266
+ }
267
+ async login(options = {}) {
268
+ this._clear();
269
+ if (this._keycloakClient) {
270
+ await this._keycloakClient.login(options);
271
+ } else {
272
+ await this._login?.(options);
273
+ }
274
+ this._sync();
275
+ }
276
+ async logout(options = {}) {
277
+ if (this._keycloakClient) {
278
+ await this._keycloakClient.logout(options);
279
+ } else {
280
+ await this._logout?.(options);
281
+ }
282
+ this._clear();
283
+ }
284
+ };
285
+
286
+ // src/keycloak/config.ts
287
+ var import_react5 = require("react");
288
+ var KeycloakConfigContext = (0, import_react5.createContext)({
289
+ clientId: "",
290
+ realm: "",
291
+ url: ""
292
+ });
293
+
294
+ // src/keycloak/index.native.ts
295
+ var Keycloak = class extends BaseKeycloak {
296
+ };
297
+ function useKeycloak() {
298
+ const keycloak = (0, import_react6.useContext)(KeycloakContext);
299
+ const keycloakConfig = (0, import_react6.useContext)(KeycloakConfigContext);
300
+ const { disabled } = useAuthConfig();
301
+ if (disabled) return null;
302
+ if (keycloak) return keycloak;
303
+ if (import_platform2.isStorybook) {
304
+ return new Keycloak(keycloakConfig, {
305
+ email: "storybook@example.com",
306
+ username: "storybook"
307
+ });
308
+ }
309
+ }
310
+
311
+ // src/Authenticated.tsx
312
+ var import_jsx_runtime = require("react/jsx-runtime");
313
+ function Authenticated({
314
+ children,
315
+ disabled,
316
+ loggedOutComponent,
317
+ loadingComponent
318
+ }) {
319
+ const authConfig = useAuthConfig();
320
+ const keycloak = useKeycloak();
321
+ const tokensFromQuery = useTokensFromQuery();
322
+ (0, import_react7.useEffect)(() => {
323
+ if (!keycloak || !authConfig.iframeSso && import_platform3.isIframe || import_platform3.isServer || keycloak.authenticated || tokensFromQuery) {
324
+ return;
325
+ }
326
+ keycloak?.login({
327
+ redirectUri: authConfig.loginRedirectUri
328
+ });
329
+ }, [keycloak?.authenticated]);
330
+ if (typeof disabled === "undefined") disabled = authConfig.disabled;
331
+ if (disabled) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children });
332
+ if (typeof keycloak === "undefined") {
333
+ const LoadingComponent = loadingComponent;
334
+ return LoadingComponent ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LoadingComponent, {}) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native.Text, { children: authConfig.debug ? "loading" : null });
335
+ }
336
+ if (keycloak === null || keycloak.authenticated) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children });
337
+ const LoggedOutComponent = loggedOutComponent;
338
+ return LoggedOutComponent ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LoggedOutComponent, {}) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native.Text, { children: authConfig.debug ? "not authenticated" : null });
339
+ }
340
+ function withAuthenticated(Component, options = {}) {
341
+ return (props) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Authenticated, { ...options, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Component, { ...props }) });
342
+ }
343
+
344
+ // src/provider/AfterAuth.tsx
345
+ var import_logger = require("@multiplatform.one/logger");
346
+ var import_react8 = require("react");
347
+ var import_jsx_runtime2 = require("react/jsx-runtime");
348
+ function AfterAuth({ children, loadingComponent: _loadingComponent }) {
349
+ const authConfig = useAuthConfig();
350
+ const authStore2 = useAuthStore();
351
+ const keycloak = useKeycloak();
352
+ (0, import_react8.useEffect)(() => {
353
+ if (!persist || !keycloak?.authenticated) return;
354
+ if (keycloak.token) {
355
+ authStore2.setTokens({
356
+ token: keycloak.token,
357
+ ...keycloak.idToken && { idToken: keycloak.idToken },
358
+ ...keycloak.refreshToken && {
359
+ refreshToken: keycloak.refreshToken
360
+ }
361
+ });
362
+ }
363
+ }, [
364
+ authStore2,
365
+ persist,
366
+ keycloak?.authenticated,
367
+ keycloak?.token,
368
+ keycloak?.idToken,
369
+ keycloak?.refreshToken
370
+ ]);
371
+ (0, import_react8.useEffect)(() => {
372
+ if (authConfig.debug && keycloak?.token) {
373
+ import_logger.logger.debug("token", keycloak.token);
374
+ }
375
+ }, [authConfig.debug, keycloak?.token]);
376
+ (0, import_react8.useEffect)(() => {
377
+ if (authConfig.debug && keycloak?.idToken) {
378
+ import_logger.logger.debug("idToken", keycloak.idToken);
379
+ }
380
+ }, [authConfig.debug, keycloak?.idToken]);
381
+ (0, import_react8.useEffect)(() => {
382
+ if (authConfig.debug && keycloak?.refreshToken) {
383
+ import_logger.logger.debug("refreshToken", keycloak.refreshToken);
384
+ }
385
+ }, [authConfig.debug, keycloak?.refreshToken]);
386
+ (0, import_react8.useEffect)(() => {
387
+ if (authConfig.debug && keycloak?.authenticated) {
388
+ import_logger.logger.debug("authenticated", keycloak.authenticated);
389
+ }
390
+ }, [authConfig.debug, keycloak?.authenticated]);
391
+ (0, import_react8.useEffect)(() => {
392
+ if (authConfig.debug && keycloak === null) {
393
+ import_logger.logger.debug("keycloak disabled");
394
+ }
395
+ }, [authConfig.debug, keycloak]);
396
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children });
397
+ }
398
+
399
+ // src/provider/authProvider/index.native.tsx
400
+ var import_react10 = require("react");
401
+ var Linking2 = __toESM(require("expo-linking"));
402
+
403
+ // src/Loading.tsx
404
+ var import_react_native2 = require("react-native");
405
+ var import_jsx_runtime3 = require("react/jsx-runtime");
406
+ function Loading({ loadingComponent }) {
407
+ const { debug } = useAuthConfig();
408
+ const LoadingComponent = loadingComponent;
409
+ if (typeof LoadingComponent === "undefined") {
410
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native2.View, { style: { flex: 1, alignItems: "center", justifyContent: "center" }, children: [
411
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native2.ActivityIndicator, {}),
412
+ debug ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native2.Text, { children: "loading" }) : null
413
+ ] });
414
+ }
415
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(LoadingComponent, {});
416
+ }
417
+
418
+ // src/betterAuth/expoAuthFlow.native.ts
419
+ var Linking = __toESM(require("expo-linking"));
420
+ var SecureStore = __toESM(require("expo-secure-store"));
421
+ var WebBrowser = __toESM(require("expo-web-browser"));
422
+ var COOKIE_KEY = "better-auth_cookie";
423
+ var SECURE_COOKIE_PREFIX = "__Secure-";
424
+ function readJar() {
425
+ try {
426
+ return JSON.parse(SecureStore.getItem(COOKIE_KEY) || "{}");
427
+ } catch {
428
+ return {};
429
+ }
430
+ }
431
+ function writeJar(jar) {
432
+ SecureStore.setItem(COOKIE_KEY, JSON.stringify(jar));
433
+ }
434
+ function cookieHeader() {
435
+ return Object.entries(readJar()).reduce((acc, [key, v]) => {
436
+ if (v.expires && new Date(v.expires) < /* @__PURE__ */ new Date()) return acc;
437
+ return acc ? `${acc}; ${key}=${v.value}` : `${key}=${v.value}`;
438
+ }, "");
439
+ }
440
+ function absorbSetCookie(header) {
441
+ if (!header) return;
442
+ const jar = readJar();
443
+ for (const part of header.split(/,(?=[^;,]+?=)/)) {
444
+ const [pair, ...attrs] = part.split(";");
445
+ const eq = pair.indexOf("=");
446
+ if (eq === -1) continue;
447
+ const name = pair.slice(0, eq).trim();
448
+ const value = pair.slice(eq + 1).trim();
449
+ let expires = null;
450
+ let dead = false;
451
+ for (const attr of attrs) {
452
+ const [k, v] = attr.split("=").map((s) => s?.trim());
453
+ const lk = (k || "").toLowerCase();
454
+ if (lk === "max-age") {
455
+ const n = Number(v);
456
+ if (n <= 0) dead = true;
457
+ else expires = new Date(Date.now() + n * 1e3).toISOString();
458
+ } else if (lk === "expires" && !expires && v) {
459
+ const d = new Date(part.slice(part.toLowerCase().indexOf("expires=") + 8).split(";")[0]);
460
+ if (!Number.isNaN(d.getTime())) {
461
+ if (d.getTime() <= Date.now()) dead = true;
462
+ else expires = d.toISOString();
463
+ }
464
+ }
465
+ }
466
+ if (dead) delete jar[name];
467
+ else jar[name] = { value, expires };
468
+ }
469
+ writeJar(jar);
470
+ }
471
+ function getOAuthState() {
472
+ const jar = readJar();
473
+ for (const name of [
474
+ `${SECURE_COOKIE_PREFIX}better-auth.oauth_state`,
475
+ "better-auth.oauth_state"
476
+ ]) {
477
+ if (jar[name]?.value) return jar[name].value;
478
+ }
479
+ return null;
480
+ }
481
+ var activeFlow = null;
482
+ function getActiveExpoAuthFlow() {
483
+ return activeFlow;
484
+ }
485
+ function createExpoAuthFlow(baseURL, scheme) {
486
+ const base = baseURL.replace(/\/$/, "");
487
+ const origin = () => Linking.createURL("/", scheme ? { scheme } : void 0);
488
+ async function authFetch(path, init) {
489
+ const cookie = cookieHeader();
490
+ const res = await fetch(`${base}/api/auth${path}`, {
491
+ ...init,
492
+ credentials: "omit",
493
+ headers: {
494
+ "Content-Type": "application/json",
495
+ ...cookie ? { cookie } : {},
496
+ "expo-origin": origin(),
497
+ "x-skip-oauth-proxy": "true",
498
+ ...init?.headers || {}
499
+ }
500
+ });
501
+ absorbSetCookie(res.headers.get("set-cookie"));
502
+ return res;
503
+ }
504
+ const flow = {
505
+ async signIn(provider, callbackURL) {
506
+ const to = callbackURL || Linking.createURL("/");
507
+ const res = await authFetch("/sign-in/social", {
508
+ method: "POST",
509
+ body: JSON.stringify({ provider, callbackURL: to })
510
+ });
511
+ if (!res.ok) {
512
+ const body = await res.text().catch(() => "");
513
+ throw new Error(`sign-in failed (${res.status}): ${body.slice(0, 200)}`);
514
+ }
515
+ const data = await res.json();
516
+ if (!data?.url) throw new Error("sign-in response had no authorization url");
517
+ const params = new URLSearchParams({ authorizationURL: data.url });
518
+ const oauthState = getOAuthState();
519
+ if (oauthState) params.append("oauthState", oauthState);
520
+ const proxyURL = `${base}/api/auth/expo-authorization-proxy?${params.toString()}`;
521
+ const result = await WebBrowser.openAuthSessionAsync(proxyURL, to);
522
+ if (result.type !== "success") {
523
+ throw new Error(`sign-in ${result.type}`);
524
+ }
525
+ const cookie = new URL(result.url).searchParams.get("cookie");
526
+ if (cookie) absorbSetCookie(cookie);
527
+ },
528
+ async signOut() {
529
+ try {
530
+ await authFetch("/sign-out", { method: "POST", body: "{}" });
531
+ } finally {
532
+ writeJar({});
533
+ }
534
+ },
535
+ async getSession() {
536
+ const res = await authFetch("/get-session");
537
+ if (!res.ok) return null;
538
+ const data = await res.json().catch(() => null);
539
+ return data && Object.keys(data).length ? data : null;
540
+ },
541
+ async getAccessToken(providerId) {
542
+ const res = await authFetch("/get-access-token", {
543
+ method: "POST",
544
+ body: JSON.stringify({ providerId })
545
+ });
546
+ if (!res.ok) return null;
547
+ return await res.json().catch(() => null);
548
+ }
549
+ };
550
+ activeFlow = flow;
551
+ return flow;
552
+ }
553
+
554
+ // src/session/index.native.ts
555
+ var import_react9 = require("react");
556
+ var state = { session: null, status: "loading" };
557
+ var listeners = /* @__PURE__ */ new Set();
558
+ function setNativeSession(session, status) {
559
+ state = {
560
+ session,
561
+ status: status ?? (session?.user ? "authenticated" : "unauthenticated")
562
+ };
563
+ for (const l of listeners) l();
564
+ }
565
+ function subscribe(listener) {
566
+ listeners.add(listener);
567
+ return () => listeners.delete(listener);
568
+ }
569
+ async function getSession() {
570
+ return state.session;
571
+ }
572
+ function useSession(_options) {
573
+ return (0, import_react9.useSyncExternalStore)(
574
+ subscribe,
575
+ () => state,
576
+ () => state
577
+ );
578
+ }
579
+
580
+ // src/provider/authProvider/index.native.tsx
581
+ var import_jsx_runtime4 = require("react/jsx-runtime");
582
+ function AuthProvider({
583
+ children,
584
+ disabled,
585
+ keycloakConfig,
586
+ loadingComponent
587
+ }) {
588
+ const [keycloak, setKeycloak] = (0, import_react10.useState)();
589
+ const [isLoading, setIsLoading] = (0, import_react10.useState)(true);
590
+ const serverBaseUrl = (0, import_react10.useMemo)(() => {
591
+ return keycloakConfig.betterAuthBaseUrl || "http://localhost:8000";
592
+ }, [keycloakConfig]);
593
+ const scheme = (0, import_react10.useMemo)(() => keycloakConfig.expoScheme, [keycloakConfig]);
594
+ const flow = (0, import_react10.useMemo)(
595
+ () => disabled ? null : createExpoAuthFlow(serverBaseUrl, scheme),
596
+ [disabled, serverBaseUrl, scheme]
597
+ );
598
+ (0, import_react10.useEffect)(() => {
599
+ if (disabled || !flow) {
600
+ setIsLoading(false);
601
+ return;
602
+ }
603
+ let alive = true;
604
+ const refresh = async () => {
605
+ let session = null;
606
+ try {
607
+ session = await flow.getSession();
608
+ } catch (err) {
609
+ console.error("[keycloak] native session check failed:", err);
610
+ }
611
+ const _keycloak = new Keycloak(
612
+ keycloakConfig,
613
+ void 0,
614
+ void 0,
615
+ void 0,
616
+ async (options) => {
617
+ await flow.signIn("keycloak", options.redirectUri || Linking2.createURL("/"));
618
+ await refresh();
619
+ return void 0;
620
+ },
621
+ async (_options) => {
622
+ await flow.signOut();
623
+ await refresh();
624
+ return void 0;
625
+ }
626
+ );
627
+ if (session?.user) {
628
+ _keycloak.authenticated = true;
629
+ _keycloak.email = session.user.email || void 0;
630
+ _keycloak.username = session.user.name || void 0;
631
+ _keycloak.subject = session.user.id;
632
+ }
633
+ if (!alive) return;
634
+ setNativeSession(
635
+ session?.user ? {
636
+ user: {
637
+ id: session.user.id,
638
+ name: session.user.name ?? null,
639
+ email: session.user.email ?? null
640
+ }
641
+ } : null
642
+ );
643
+ setKeycloak(_keycloak);
644
+ };
645
+ refresh().finally(() => {
646
+ if (alive) setIsLoading(false);
647
+ });
648
+ return () => {
649
+ alive = false;
650
+ };
651
+ }, [disabled, flow, keycloakConfig]);
652
+ if (disabled) return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children });
653
+ if (isLoading || !keycloak) {
654
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Loading, { loadingComponent });
655
+ }
656
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(KeycloakConfigContext.Provider, { value: keycloakConfig, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(KeycloakContext.Provider, { value: keycloak, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(AfterAuth, { children }) }) });
657
+ }
658
+
659
+ // src/provider/KeycloakProvider.tsx
660
+ var import_react11 = require("react");
661
+ var import_jsx_runtime5 = require("react/jsx-runtime");
662
+ function KeycloakProvider({
663
+ baseUrl,
664
+ betterAuthBaseUrl,
665
+ children,
666
+ clientId,
667
+ expoScheme,
668
+ loginRedirectUri,
669
+ debug,
670
+ disabled,
671
+ iframeSso,
672
+ publicClientId,
673
+ realm
674
+ }) {
675
+ const authConfig = (0, import_react11.useMemo)(
676
+ () => ({ debug, disabled, iframeSso, loginRedirectUri }),
677
+ [debug, disabled, iframeSso, loginRedirectUri]
678
+ );
679
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(AuthConfigContext.Provider, { value: authConfig, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
680
+ AuthProvider,
681
+ {
682
+ disabled,
683
+ keycloakConfig: {
684
+ clientId: clientId || "app",
685
+ publicClientId,
686
+ realm: realm || "main",
687
+ url: baseUrl ?? "",
688
+ betterAuthBaseUrl,
689
+ expoScheme
690
+ },
691
+ children
692
+ }
693
+ ) });
694
+ }
695
+
696
+ // src/frappeToken.native.ts
697
+ var cached;
698
+ async function getKeycloakBearerToken() {
699
+ if (cached && cached.expiresAt - 6e4 > Date.now()) return cached.token;
700
+ const flow = getActiveExpoAuthFlow();
701
+ if (!flow) return void 0;
702
+ try {
703
+ const res = await flow.getAccessToken("keycloak");
704
+ if (!res?.accessToken) {
705
+ cached = void 0;
706
+ return void 0;
707
+ }
708
+ cached = {
709
+ token: res.accessToken,
710
+ expiresAt: res.accessTokenExpiresAt ? new Date(res.accessTokenExpiresAt).getTime() : Date.now() + 6e4
711
+ };
712
+ return cached.token;
713
+ } catch {
714
+ cached = void 0;
715
+ return void 0;
716
+ }
717
+ }