@multiplatform.one/keycloak 6.0.4 → 6.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +58 -0
- package/dist/cjs/index.cjs +46 -6
- package/dist/cjs/index.native.cjs +748 -0
- package/dist/esm/index.js +45 -5
- package/dist/esm/index.native.js +715 -0
- package/package.json +23 -17
- package/src/Authenticated.tsx +3 -1
- package/src/Loading.tsx +10 -2
- package/src/betterAuth/expoAuthFlow.native.ts +238 -0
- package/src/frappeToken.native.ts +45 -0
- package/src/frappeToken.ts +59 -0
- package/src/index.ts +1 -0
- package/src/provider/KeycloakProvider.tsx +11 -0
- package/src/provider/authProvider/index.native.tsx +72 -64
- package/src/session/index.native.ts +32 -7
- package/types/Authenticated.d.ts +9 -0
- package/types/Authenticated.d.ts.map +1 -0
- package/types/Loading.d.ts +6 -0
- package/types/Loading.d.ts.map +1 -0
- package/types/authConfig.d.ts +10 -0
- package/types/authConfig.d.ts.map +1 -0
- package/types/betterAuth/client.d.ts +798 -0
- package/types/betterAuth/client.d.ts.map +1 -0
- package/types/betterAuth/expoAuthFlow.native.d.ts +31 -0
- package/types/betterAuth/expoAuthFlow.native.d.ts.map +1 -0
- package/types/betterAuth/expoClient.d.ts +803 -0
- package/types/betterAuth/expoClient.d.ts.map +1 -0
- package/types/betterAuth/index.d.ts +3 -0
- package/types/betterAuth/index.d.ts.map +1 -0
- package/types/frappeToken.d.ts +19 -0
- package/types/frappeToken.d.ts.map +1 -0
- package/types/frappeToken.native.d.ts +15 -0
- package/types/frappeToken.native.d.ts.map +1 -0
- package/types/hooks/index.d.ts +4 -0
- package/types/hooks/index.d.ts.map +1 -0
- package/types/hooks/useAuthConfig.d.ts +2 -0
- package/types/hooks/useAuthConfig.d.ts.map +1 -0
- package/types/hooks/useTokensFromQuery/index.d.ts +2 -0
- package/types/hooks/useTokensFromQuery/index.d.ts.map +1 -0
- package/types/hooks/useTokensFromQuery/index.native.d.ts +2 -0
- package/types/hooks/useTokensFromQuery/index.native.d.ts.map +1 -0
- package/types/hooks/useTokensFromState.d.ts +2 -0
- package/types/hooks/useTokensFromState.d.ts.map +1 -0
- package/types/index.d.ts +10 -0
- package/types/index.d.ts.map +1 -0
- package/types/keycloak/base.d.ts +50 -0
- package/types/keycloak/base.d.ts.map +1 -0
- package/types/keycloak/config.d.ts +12 -0
- package/types/keycloak/config.d.ts.map +1 -0
- package/types/keycloak/context.d.ts +3 -0
- package/types/keycloak/context.d.ts.map +1 -0
- package/types/keycloak/index.d.ts +20 -0
- package/types/keycloak/index.d.ts.map +1 -0
- package/types/keycloak/index.native.d.ts +7 -0
- package/types/keycloak/index.native.d.ts.map +1 -0
- package/types/keycloak/index.webext.d.ts +19 -0
- package/types/keycloak/index.webext.d.ts.map +1 -0
- package/types/one.d.ts +28 -0
- package/types/one.d.ts.map +1 -0
- package/types/provider/AfterAuth.d.ts +6 -0
- package/types/provider/AfterAuth.d.ts.map +1 -0
- package/types/provider/KeycloakProvider.d.ts +17 -0
- package/types/provider/KeycloakProvider.d.ts.map +1 -0
- package/types/provider/authProvider/index.d.ts +13 -0
- package/types/provider/authProvider/index.d.ts.map +1 -0
- package/types/provider/authProvider/index.native.d.ts +15 -0
- package/types/provider/authProvider/index.native.d.ts.map +1 -0
- package/types/provider/authProvider/index.webext.d.ts +14 -0
- package/types/provider/authProvider/index.webext.d.ts.map +1 -0
- package/types/provider/authProvider/shared.d.ts +38 -0
- package/types/provider/authProvider/shared.d.ts.map +1 -0
- package/types/provider/index.d.ts +4 -0
- package/types/provider/index.d.ts.map +1 -0
- package/types/session/index.d.ts +29 -0
- package/types/session/index.d.ts.map +1 -0
- package/types/session/index.native.d.ts +13 -0
- package/types/session/index.native.d.ts.map +1 -0
- package/types/session/index.webext.d.ts +11 -0
- package/types/session/index.webext.d.ts.map +1 -0
- package/types/state.d.ts +18 -0
- package/types/state.d.ts.map +1 -0
- package/types/token.d.ts +129 -0
- package/types/token.d.ts.map +1 -0
- package/types/types.d.ts +19 -0
- package/types/types.d.ts.map +1 -0
|
@@ -2,19 +2,24 @@
|
|
|
2
2
|
* File: /src/provider/auth_provider/index.native.tsx
|
|
3
3
|
* Project: @multiplatform.one/keycloak
|
|
4
4
|
*
|
|
5
|
-
* Native (Expo) AuthProvider using Better Auth
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* Native (Expo) AuthProvider using the hand-rolled Better Auth flow
|
|
6
|
+
* (betterAuth/expoAuthFlow.native.ts): fetch + expo-web-browser + SecureStore.
|
|
7
|
+
* Deliberately NOT @better-auth/expo/client — its import graph drags
|
|
8
|
+
* better-auth's cookies module -> @better-auth/core db schema -> zod into the
|
|
9
|
+
* Hermes bundle, and zod's module init dies under the vxrn/rolldown dev
|
|
10
|
+
* transform. The server side stays better-auth (the expo() plugin provides
|
|
11
|
+
* /expo-authorization-proxy and the cookie hand-back).
|
|
9
12
|
*/
|
|
10
13
|
|
|
11
14
|
import { useEffect, useMemo, useState } from "react";
|
|
15
|
+
import * as Linking from "expo-linking";
|
|
12
16
|
import { Loading } from "../../Loading";
|
|
13
|
-
import {
|
|
17
|
+
import { createExpoAuthFlow } from "../../betterAuth/expoAuthFlow.native";
|
|
14
18
|
import { KeycloakConfigContext } from "../../keycloak/config";
|
|
15
19
|
import { Keycloak } from "../../keycloak/index";
|
|
16
20
|
import type { KeycloakLoginOptions, KeycloakLogoutOptions } from "../../keycloak/base";
|
|
17
21
|
import { KeycloakContext } from "../../keycloak/context";
|
|
22
|
+
import { setNativeSession } from "../../session/index.native";
|
|
18
23
|
import { AfterAuth } from "../AfterAuth";
|
|
19
24
|
import type { AuthProviderProps } from "./shared";
|
|
20
25
|
|
|
@@ -29,78 +34,81 @@ export function AuthProvider({
|
|
|
29
34
|
|
|
30
35
|
// Resolve the Better Auth server base URL from keycloak config.
|
|
31
36
|
// On native, the keycloakConfig.url is the Keycloak server — the Better Auth
|
|
32
|
-
// server is the app backend
|
|
37
|
+
// server is the app backend (CreateApp wires BETTER_AUTH_BASE_URL/BASE_URL).
|
|
33
38
|
const serverBaseUrl = useMemo(() => {
|
|
34
|
-
|
|
35
|
-
// For now, derive from keycloakConfig — the app server is typically on the same origin.
|
|
36
|
-
// In production, set BETTER_AUTH_BASE_URL in the app config / env.
|
|
37
|
-
return keycloakConfig.betterAuthBaseUrl || "http://localhost:5000";
|
|
39
|
+
return keycloakConfig.betterAuthBaseUrl || "http://localhost:8000";
|
|
38
40
|
}, [keycloakConfig]);
|
|
39
41
|
|
|
40
|
-
const scheme = useMemo(() => keycloakConfig.expoScheme
|
|
42
|
+
const scheme = useMemo(() => keycloakConfig.expoScheme, [keycloakConfig]);
|
|
41
43
|
|
|
42
|
-
const
|
|
43
|
-
() => (disabled ? null :
|
|
44
|
+
const flow = useMemo(
|
|
45
|
+
() => (disabled ? null : createExpoAuthFlow(serverBaseUrl, scheme)),
|
|
44
46
|
[disabled, serverBaseUrl, scheme],
|
|
45
47
|
);
|
|
46
48
|
|
|
47
|
-
// Check session on mount
|
|
48
49
|
useEffect(() => {
|
|
49
|
-
if (disabled || !
|
|
50
|
+
if (disabled || !flow) {
|
|
50
51
|
setIsLoading(false);
|
|
51
52
|
return;
|
|
52
53
|
}
|
|
53
|
-
|
|
54
|
+
let alive = true;
|
|
55
|
+
|
|
56
|
+
// Probe the session, build a Keycloak instance reflecting it, and push the
|
|
57
|
+
// result into the native session store (useSession's backing).
|
|
58
|
+
const refresh = async () => {
|
|
59
|
+
let session: Awaited<ReturnType<typeof flow.getSession>> = null;
|
|
54
60
|
try {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
keycloakConfig,
|
|
59
|
-
undefined, // no raw token
|
|
60
|
-
undefined,
|
|
61
|
-
undefined,
|
|
62
|
-
async (options: KeycloakLoginOptions) => {
|
|
63
|
-
await client.signIn.social({
|
|
64
|
-
provider: "keycloak" as any,
|
|
65
|
-
callbackURL: options.redirectUri,
|
|
66
|
-
});
|
|
67
|
-
},
|
|
68
|
-
async (_options: KeycloakLogoutOptions) => {
|
|
69
|
-
await client.signOut();
|
|
70
|
-
},
|
|
71
|
-
);
|
|
72
|
-
// Set minimal user info from session
|
|
73
|
-
_keycloak.authenticated = true;
|
|
74
|
-
_keycloak.email = session.data.user.email || undefined;
|
|
75
|
-
_keycloak.username = session.data.user.name || undefined;
|
|
76
|
-
_keycloak.subject = session.data.user.id;
|
|
77
|
-
setKeycloak(_keycloak);
|
|
78
|
-
} else {
|
|
79
|
-
setKeycloak(
|
|
80
|
-
new Keycloak(
|
|
81
|
-
keycloakConfig,
|
|
82
|
-
undefined,
|
|
83
|
-
undefined,
|
|
84
|
-
undefined,
|
|
85
|
-
async (options: KeycloakLoginOptions) => {
|
|
86
|
-
await client.signIn.social({
|
|
87
|
-
provider: "keycloak" as any,
|
|
88
|
-
callbackURL: options.redirectUri,
|
|
89
|
-
});
|
|
90
|
-
},
|
|
91
|
-
async (_options: KeycloakLogoutOptions) => {
|
|
92
|
-
await client.signOut();
|
|
93
|
-
},
|
|
94
|
-
),
|
|
95
|
-
);
|
|
96
|
-
}
|
|
97
|
-
} catch {
|
|
98
|
-
setKeycloak(new Keycloak(keycloakConfig));
|
|
99
|
-
} finally {
|
|
100
|
-
setIsLoading(false);
|
|
61
|
+
session = await flow.getSession();
|
|
62
|
+
} catch (err) {
|
|
63
|
+
console.error("[keycloak] native session check failed:", err);
|
|
101
64
|
}
|
|
102
|
-
|
|
103
|
-
|
|
65
|
+
const _keycloak = new Keycloak(
|
|
66
|
+
keycloakConfig,
|
|
67
|
+
undefined,
|
|
68
|
+
undefined,
|
|
69
|
+
undefined,
|
|
70
|
+
async (options: KeycloakLoginOptions) => {
|
|
71
|
+
// Default the OAuth return URL to the app's own runtime URL: Expo Go
|
|
72
|
+
// can only receive exp:// redirects (Linking.createURL derives
|
|
73
|
+
// exp://<host>/--/ there; the bare app scheme in dev builds).
|
|
74
|
+
await flow.signIn("keycloak", options.redirectUri || Linking.createURL("/"));
|
|
75
|
+
await refresh();
|
|
76
|
+
return undefined;
|
|
77
|
+
},
|
|
78
|
+
async (_options: KeycloakLogoutOptions) => {
|
|
79
|
+
await flow.signOut();
|
|
80
|
+
await refresh();
|
|
81
|
+
return undefined;
|
|
82
|
+
},
|
|
83
|
+
);
|
|
84
|
+
if (session?.user) {
|
|
85
|
+
_keycloak.authenticated = true;
|
|
86
|
+
_keycloak.email = session.user.email || undefined;
|
|
87
|
+
_keycloak.username = session.user.name || undefined;
|
|
88
|
+
_keycloak.subject = session.user.id;
|
|
89
|
+
}
|
|
90
|
+
if (!alive) return;
|
|
91
|
+
setNativeSession(
|
|
92
|
+
session?.user
|
|
93
|
+
? {
|
|
94
|
+
user: {
|
|
95
|
+
id: session.user.id,
|
|
96
|
+
name: session.user.name ?? null,
|
|
97
|
+
email: session.user.email ?? null,
|
|
98
|
+
},
|
|
99
|
+
}
|
|
100
|
+
: null,
|
|
101
|
+
);
|
|
102
|
+
setKeycloak(_keycloak);
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
refresh().finally(() => {
|
|
106
|
+
if (alive) setIsLoading(false);
|
|
107
|
+
});
|
|
108
|
+
return () => {
|
|
109
|
+
alive = false;
|
|
110
|
+
};
|
|
111
|
+
}, [disabled, flow, keycloakConfig]);
|
|
104
112
|
|
|
105
113
|
if (disabled) return <>{children}</>;
|
|
106
114
|
if (isLoading || !keycloak) {
|
|
@@ -2,20 +2,45 @@
|
|
|
2
2
|
* File: /src/session/index.native.ts
|
|
3
3
|
* Project: @multiplatform.one/keycloak
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* Native session hook backed by a tiny external store the AuthProvider feeds
|
|
6
|
+
* (no better-auth client on native -- see betterAuth/expoAuthFlow.native.ts).
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import
|
|
9
|
+
import { useSyncExternalStore } from "react";
|
|
10
|
+
import type { Session, SessionContextValue, SessionStatus } from "./index";
|
|
11
|
+
|
|
12
|
+
interface NativeSessionState {
|
|
13
|
+
session: Session | null;
|
|
14
|
+
status: SessionStatus;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
let state: NativeSessionState = { session: null, status: "loading" };
|
|
18
|
+
const listeners = new Set<() => void>();
|
|
19
|
+
|
|
20
|
+
/** Fed by the native AuthProvider on mount / sign-in / sign-out. */
|
|
21
|
+
export function setNativeSession(session: Session | null, status?: SessionStatus) {
|
|
22
|
+
state = {
|
|
23
|
+
session,
|
|
24
|
+
status: status ?? (session?.user ? "authenticated" : "unauthenticated"),
|
|
25
|
+
};
|
|
26
|
+
for (const l of listeners) l();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function subscribe(listener: () => void) {
|
|
30
|
+
listeners.add(listener);
|
|
31
|
+
return () => listeners.delete(listener);
|
|
32
|
+
}
|
|
10
33
|
|
|
11
34
|
export async function getSession() {
|
|
12
|
-
return
|
|
35
|
+
return state.session;
|
|
13
36
|
}
|
|
14
37
|
|
|
15
38
|
export function useSession<R extends boolean = false>(
|
|
16
39
|
_options?: Record<string, unknown>,
|
|
17
40
|
): SessionContextValue<R> {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
41
|
+
return useSyncExternalStore(
|
|
42
|
+
subscribe,
|
|
43
|
+
() => state,
|
|
44
|
+
() => state,
|
|
45
|
+
);
|
|
21
46
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type ComponentType, type PropsWithChildren } from "react";
|
|
2
|
+
export interface AuthenticatedProps extends PropsWithChildren {
|
|
3
|
+
disabled?: boolean;
|
|
4
|
+
loadingComponent?: ComponentType;
|
|
5
|
+
loggedOutComponent?: ComponentType;
|
|
6
|
+
}
|
|
7
|
+
export declare function Authenticated({ children, disabled, loggedOutComponent, loadingComponent, }: AuthenticatedProps): import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
export declare function withAuthenticated<P extends object>(Component: ComponentType<P>, options?: AuthenticatedProps): (props: P) => import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
//# sourceMappingURL=Authenticated.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Authenticated.d.ts","sourceRoot":"","sources":["../src/Authenticated.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,iBAAiB,EAAa,MAAM,OAAO,CAAC;AAO9E,MAAM,WAAW,kBAAmB,SAAQ,iBAAiB;IAC3D,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,gBAAgB,CAAC,EAAE,aAAa,CAAC;IACjC,kBAAkB,CAAC,EAAE,aAAa,CAAC;CACpC;AAED,wBAAgB,aAAa,CAAC,EAC5B,QAAQ,EACR,QAAQ,EACR,kBAAkB,EAClB,gBAAgB,GACjB,EAAE,kBAAkB,2CAoCpB;AAED,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,MAAM,EAChD,SAAS,EAAE,aAAa,CAAC,CAAC,CAAC,EAC3B,OAAO,GAAE,kBAAuB,IAExB,OAAO,CAAC,6CAKjB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Loading.d.ts","sourceRoot":"","sources":["../src/Loading.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAO3C,MAAM,WAAW,YAAY;IAC3B,gBAAgB,CAAC,EAAE,aAAa,CAAC;CAClC;AAED,wBAAgB,OAAO,CAAC,EAAE,gBAAgB,EAAE,EAAE,YAAY,2CAYzD"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare const defaultAuthConfig: AuthConfig;
|
|
2
|
+
export declare const AuthConfigContext: import("react").Context<AuthConfig>;
|
|
3
|
+
export interface AuthConfig {
|
|
4
|
+
debug?: boolean;
|
|
5
|
+
disabled?: boolean;
|
|
6
|
+
iframeSso?: boolean;
|
|
7
|
+
loginRedirectUri?: string;
|
|
8
|
+
messageHandlerKeys?: string[];
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=authConfig.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authConfig.d.ts","sourceRoot":"","sources":["../src/authConfig.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,iBAAiB,EAAE,UAAe,CAAC;AAEhD,eAAO,MAAM,iBAAiB,qCAA+C,CAAC;AAE9E,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC/B"}
|