@multiplatform.one/keycloak 6.6.0 → 7.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/dist/cjs/index.cjs +129 -55
  2. package/dist/cjs/index.native.cjs +239 -130
  3. package/dist/esm/index.js +131 -57
  4. package/dist/esm/index.native.js +239 -130
  5. package/package.json +16 -11
  6. package/src/betterAuth/client.ts +3 -1
  7. package/src/betterAuth/expoAuthFlow.native.spec.ts +307 -0
  8. package/src/betterAuth/expoAuthFlow.native.ts +189 -55
  9. package/src/frappeToken.native.spec.ts +104 -0
  10. package/src/frappeToken.native.ts +46 -2
  11. package/src/frappeToken.spec.ts +141 -0
  12. package/src/frappeToken.ts +106 -14
  13. package/src/index.ts +1 -1
  14. package/src/keycloak/index.ts +24 -7
  15. package/src/keycloak/index.webext.ts +16 -0
  16. package/src/oauth/authorizationUrl.spec.ts +83 -0
  17. package/src/oauth/authorizationUrl.ts +74 -0
  18. package/src/oauth/callback.spec.ts +72 -0
  19. package/src/oauth/callback.ts +46 -0
  20. package/src/oauth/callbackPage.spec.ts +61 -0
  21. package/src/oauth/callbackPage.ts +57 -0
  22. package/src/oauth/endpoints.spec.ts +193 -0
  23. package/src/oauth/endpoints.ts +135 -0
  24. package/src/oauth/errors.ts +18 -0
  25. package/src/oauth/form.spec.ts +35 -0
  26. package/src/oauth/form.ts +30 -0
  27. package/src/oauth/idToken.spec.ts +191 -0
  28. package/src/oauth/idToken.ts +153 -0
  29. package/src/oauth/index.ts +10 -0
  30. package/src/oauth/jwt.ts +90 -0
  31. package/src/oauth/loopbackFlow.spec.ts +226 -0
  32. package/src/oauth/loopbackFlow.ts +114 -0
  33. package/src/oauth/pkce.spec.ts +60 -0
  34. package/src/oauth/pkce.ts +80 -0
  35. package/src/oauth/tokenExchange.spec.ts +331 -0
  36. package/src/oauth/tokenExchange.ts +232 -0
  37. package/src/one.ts +30 -3
  38. package/src/provider/AfterAuth.tsx +7 -0
  39. package/src/provider/authProvider/index.native.tsx +37 -4
  40. package/src/session/index.ts +2 -2
  41. package/src/state.ts +5 -2
  42. package/types/betterAuth/client.d.ts +2 -791
  43. package/types/betterAuth/client.d.ts.map +1 -1
  44. package/types/betterAuth/expoAuthFlow.native.d.ts +50 -3
  45. package/types/betterAuth/expoAuthFlow.native.d.ts.map +1 -1
  46. package/types/frappeToken.d.ts +14 -1
  47. package/types/frappeToken.d.ts.map +1 -1
  48. package/types/frappeToken.native.d.ts +16 -2
  49. package/types/frappeToken.native.d.ts.map +1 -1
  50. package/types/index.d.ts +1 -1
  51. package/types/index.d.ts.map +1 -1
  52. package/types/keycloak/index.d.ts.map +1 -1
  53. package/types/keycloak/index.webext.d.ts.map +1 -1
  54. package/types/oauth/authorizationUrl.d.ts +32 -0
  55. package/types/oauth/authorizationUrl.d.ts.map +1 -0
  56. package/types/oauth/callback.d.ts +26 -0
  57. package/types/oauth/callback.d.ts.map +1 -0
  58. package/types/oauth/callbackPage.d.ts +34 -0
  59. package/types/oauth/callbackPage.d.ts.map +1 -0
  60. package/types/oauth/endpoints.d.ts +68 -0
  61. package/types/oauth/endpoints.d.ts.map +1 -0
  62. package/types/oauth/errors.d.ts +17 -0
  63. package/types/oauth/errors.d.ts.map +1 -0
  64. package/types/oauth/form.d.ts +16 -0
  65. package/types/oauth/form.d.ts.map +1 -0
  66. package/types/oauth/idToken.d.ts +83 -0
  67. package/types/oauth/idToken.d.ts.map +1 -0
  68. package/types/oauth/index.d.ts +11 -0
  69. package/types/oauth/index.d.ts.map +1 -0
  70. package/types/oauth/jwt.d.ts +31 -0
  71. package/types/oauth/jwt.d.ts.map +1 -0
  72. package/types/oauth/loopbackFlow.d.ts +64 -0
  73. package/types/oauth/loopbackFlow.d.ts.map +1 -0
  74. package/types/oauth/pkce.d.ts +49 -0
  75. package/types/oauth/pkce.d.ts.map +1 -0
  76. package/types/oauth/tokenExchange.d.ts +89 -0
  77. package/types/oauth/tokenExchange.d.ts.map +1 -0
  78. package/types/one.d.ts +7 -0
  79. package/types/one.d.ts.map +1 -1
  80. package/types/provider/AfterAuth.d.ts.map +1 -1
  81. package/types/provider/authProvider/index.native.d.ts.map +1 -1
  82. package/types/state.d.ts.map +1 -1
@@ -12,9 +12,14 @@
12
12
  */
13
13
 
14
14
  import { useEffect, useMemo, useState } from "react";
15
+ import { AppState, type AppStateStatus } from "react-native";
15
16
  import * as Linking from "expo-linking";
16
17
  import { Loading } from "../../Loading";
17
- import { createExpoAuthFlow } from "../../betterAuth/expoAuthFlow.native";
18
+ import {
19
+ createExpoAuthFlow,
20
+ rewriteLoopbackHostForNative,
21
+ } from "../../betterAuth/expoAuthFlow.native";
22
+ import { clearKeycloakBearerToken } from "../../frappeToken.native";
18
23
  import { KeycloakConfigContext } from "../../keycloak/config";
19
24
  import { Keycloak } from "../../keycloak/index";
20
25
  import type { KeycloakLoginOptions, KeycloakLogoutOptions } from "../../keycloak/base";
@@ -35,8 +40,12 @@ export function AuthProvider({
35
40
  // Resolve the Better Auth server base URL from keycloak config.
36
41
  // On native, the keycloakConfig.url is the Keycloak server — the Better Auth
37
42
  // server is the app backend (CreateApp wires BETTER_AUTH_BASE_URL/BASE_URL).
43
+ // Loopback hosts are rewritten for the device (127.0.0.1 / 10.0.2.2): see
44
+ // rewriteLoopbackHostForNative.
38
45
  const serverBaseUrl = useMemo(() => {
39
- return keycloakConfig.betterAuthBaseUrl || "http://localhost:8000";
46
+ return rewriteLoopbackHostForNative(
47
+ keycloakConfig.betterAuthBaseUrl || "http://localhost:8000",
48
+ );
40
49
  }, [keycloakConfig]);
41
50
 
42
51
  const scheme = useMemo(() => keycloakConfig.expoScheme, [keycloakConfig]);
@@ -72,11 +81,24 @@ export function AuthProvider({
72
81
  // can only receive exp:// redirects (Linking.createURL derives
73
82
  // exp://<host>/--/ there; the bare app scheme in dev builds).
74
83
  await flow.signIn("keycloak", options.redirectUri || Linking.createURL("/"));
84
+ // Drop any signed-out cooldown/stale token so the first Frappe
85
+ // request after sign-in fetches a fresh Bearer token.
86
+ clearKeycloakBearerToken();
75
87
  await refresh();
76
88
  return undefined;
77
89
  },
78
- async (_options: KeycloakLogoutOptions) => {
79
- await flow.signOut();
90
+ async (options: KeycloakLogoutOptions) => {
91
+ // Single sign-out: ends the Better Auth session AND rides the
92
+ // Keycloak end-session endpoint through the system browser (see
93
+ // expoAuthFlow.signOut). Only custom-scheme URLs make sense as the
94
+ // post-logout return on native; web-style paths fall back to the
95
+ // app's own runtime URL.
96
+ const redirectUri =
97
+ options.redirectUri && options.redirectUri.includes("://")
98
+ ? options.redirectUri
99
+ : Linking.createURL("/");
100
+ await flow.signOut(redirectUri);
101
+ clearKeycloakBearerToken();
80
102
  await refresh();
81
103
  return undefined;
82
104
  },
@@ -105,8 +127,19 @@ export function AuthProvider({
105
127
  refresh().finally(() => {
106
128
  if (alive) setIsLoading(false);
107
129
  });
130
+
131
+ // Re-probe when the app returns to the foreground: covers a completed
132
+ // ASWebAuthenticationSession whose Linking event arrived after dismiss,
133
+ // and a user who abandoned the end-session page (local jar already
134
+ // empty; this just refreshes the header).
135
+ const onAppState = (next: AppStateStatus) => {
136
+ if (next === "active") refresh().catch(() => undefined);
137
+ };
138
+ const appSub = AppState.addEventListener("change", onAppState);
139
+
108
140
  return () => {
109
141
  alive = false;
142
+ appSub.remove();
110
143
  };
111
144
  }, [disabled, flow, keycloakConfig]);
112
145
 
@@ -6,7 +6,7 @@
6
6
  * Replaces the previous next-auth/react useSession.
7
7
  */
8
8
 
9
- import { isBrowser, isTauri } from "@multiplatform.one/platform";
9
+ import { isBrowser } from "@multiplatform.one/platform";
10
10
  import { getAuthClient } from "../betterAuth/client";
11
11
 
12
12
  export interface Session {
@@ -37,7 +37,7 @@ let useSession = <R extends boolean = false>(
37
37
  return { session: null, status: "unauthenticated" };
38
38
  };
39
39
 
40
- if (isBrowser && !isTauri) {
40
+ if (isBrowser) {
41
41
  useSession = <R extends boolean = false>(
42
42
  _options?: Record<string, unknown>,
43
43
  ): SessionContextValue<R> => {
package/src/state.ts CHANGED
@@ -1,7 +1,10 @@
1
1
  import { createStore, useStore } from "@multiplatform.one/store";
2
- import { isBrowser, isIframe, isServer, isTauri } from "@multiplatform.one/platform";
2
+ import { isBrowser, isIframe, isServer } from "@multiplatform.one/platform";
3
3
 
4
- export const persist = isIframe || isTauri || (!isBrowser && !isServer);
4
+ // Browsers keep the session in a Better Auth cookie, so nothing to persist.
5
+ // Everywhere else (iframes fed tokens by a parent, native, and the GNOME/GJS
6
+ // target) the tokens are the only session there is.
7
+ export const persist = isIframe || (!isBrowser && !isServer);
5
8
 
6
9
  export interface AuthTokenState {
7
10
  idToken: string;