@multiplatform.one/keycloak 7.87.1 → 7.88.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@multiplatform.one/keycloak",
3
- "version": "7.87.1",
3
+ "version": "7.88.1",
4
4
  "description": "keycloak client for multiplatform.one ecosystem",
5
5
  "keywords": [
6
6
  "auth",
@@ -62,16 +62,16 @@
62
62
  },
63
63
  "dependencies": {
64
64
  "@better-auth/expo": "~1.6.33",
65
- "@multiplatform.one/keycloak-js": "^7.87.1",
66
- "@multiplatform.one/store": "^7.87.1",
65
+ "@multiplatform.one/keycloak-js": "^7.88.1",
66
+ "@multiplatform.one/store": "^7.88.1",
67
67
  "better-auth": "~1.6.33",
68
68
  "jwt-decode": "^4.0.0"
69
69
  },
70
70
  "devDependencies": {
71
- "@multiplatform.one/config": "7.87.1",
72
- "@multiplatform.one/logger": "7.87.1",
73
- "@multiplatform.one/platform": "7.87.1",
74
- "@multiplatform.one/test-utils": "7.87.1",
71
+ "@multiplatform.one/config": "7.88.1",
72
+ "@multiplatform.one/logger": "7.88.1",
73
+ "@multiplatform.one/platform": "7.88.1",
74
+ "@multiplatform.one/test-utils": "7.88.1",
75
75
  "@tamagui/build": "2.7.6",
76
76
  "@testing-library/jest-dom": "^7.0.1",
77
77
  "@testing-library/react": "^16.3.3",
@@ -85,8 +85,8 @@
85
85
  "vitest": "^4.1.12"
86
86
  },
87
87
  "peerDependencies": {
88
- "@multiplatform.one/logger": "^7.87.1",
89
- "@multiplatform.one/platform": "^7.87.1",
88
+ "@multiplatform.one/logger": "^7.88.1",
89
+ "@multiplatform.one/platform": "^7.88.1",
90
90
  "react": "^19.1.0",
91
91
  "react-native": ">=0.83.0"
92
92
  },
@@ -0,0 +1,107 @@
1
+ // @vitest-environment jsdom
2
+ // jsdom, not the package's happy-dom: happy-dom's Request and Response drop
3
+ // Cookie and Set-Cookie (forbidden headers in a browser), and the cookies are
4
+ // the point here. jsdom leaves Node's own fetch classes in place.
5
+ /**
6
+ * FZ-8696: native sign-in through createAuth must come back signed in.
7
+ *
8
+ * createAuth wires Keycloak through Better Auth's genericOAuth, whose callback
9
+ * is /oauth2/callback/keycloak. @better-auth/expo hands the session to the
10
+ * app by appending ?cookie=<set-cookie> to the app-scheme redirect, but only
11
+ * on the callback paths its after-hook matches. Under better-auth 1.6.30 that
12
+ * was /callback* alone, so the genericOAuth callback came back as a bare
13
+ * <scheme>:/// and the app stayed signed out with no error (Lookout FZ-8618).
14
+ *
15
+ * This drives the real sign-in and callback against a stubbed Keycloak, under
16
+ * whatever better-auth version MPO pins, and checks the session rides the
17
+ * redirect.
18
+ */
19
+
20
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
21
+ import { createAuth } from "./one";
22
+
23
+ const appUrl = "http://app.test";
24
+ const keycloakUrl = "http://kc.test/auth";
25
+ const realm = "mpo";
26
+ const issuer = `${keycloakUrl}/realms/${realm}`;
27
+ const scheme = "mpo-test";
28
+
29
+ function stubKeycloak() {
30
+ const realFetch = globalThis.fetch;
31
+ vi.stubGlobal(
32
+ "fetch",
33
+ vi.fn(async (input: RequestInfo | URL, init?: RequestInit) => {
34
+ const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
35
+ const json = (body: unknown) =>
36
+ new Response(JSON.stringify(body), { headers: { "content-type": "application/json" } });
37
+ if (url === `${issuer}/.well-known/openid-configuration`) {
38
+ return json({
39
+ issuer,
40
+ authorization_endpoint: `${issuer}/protocol/openid-connect/auth`,
41
+ token_endpoint: `${issuer}/protocol/openid-connect/token`,
42
+ userinfo_endpoint: `${issuer}/protocol/openid-connect/userinfo`,
43
+ });
44
+ }
45
+ if (url === `${issuer}/protocol/openid-connect/token`) {
46
+ return json({ access_token: "access", token_type: "Bearer", expires_in: 300 });
47
+ }
48
+ if (url === `${issuer}/protocol/openid-connect/userinfo`) {
49
+ return json({
50
+ sub: "user-1",
51
+ email: "ada@example.test",
52
+ name: "Ada",
53
+ email_verified: true,
54
+ });
55
+ }
56
+ return realFetch(input, init);
57
+ }),
58
+ );
59
+ }
60
+
61
+ beforeEach(stubKeycloak);
62
+ afterEach(() => {
63
+ vi.unstubAllGlobals();
64
+ });
65
+
66
+ describe("createAuth native sign-in (FZ-8696)", () => {
67
+ it("hands the session back on the genericOAuth callback's app-scheme redirect", async () => {
68
+ const auth = createAuth({
69
+ baseUrl: appUrl,
70
+ secret: "a-test-secret-that-is-long-enough-32",
71
+ expoScheme: scheme,
72
+ keycloak: { baseUrl: keycloakUrl, realm, clientId: "app", clientSecret: "secret" },
73
+ });
74
+
75
+ const signIn = await auth.handler(
76
+ new Request(`${appUrl}/api/auth/sign-in/oauth2`, {
77
+ method: "POST",
78
+ headers: { "content-type": "application/json", "expo-origin": `${scheme}://` },
79
+ body: JSON.stringify({
80
+ providerId: "keycloak",
81
+ callbackURL: `${scheme}://`,
82
+ disableRedirect: true,
83
+ }),
84
+ }),
85
+ );
86
+ expect(signIn.status).toBe(200);
87
+ const { url } = (await signIn.json()) as { url: string };
88
+ const state = new URL(url).searchParams.get("state");
89
+ expect(state).toBeTruthy();
90
+ const cookies = signIn.headers
91
+ .getSetCookie()
92
+ .map((cookie) => cookie.split(";")[0])
93
+ .join("; ");
94
+ expect(cookies).toMatch(/oauth_state=/);
95
+
96
+ const callback = await auth.handler(
97
+ new Request(`${appUrl}/api/auth/oauth2/callback/keycloak?code=abc&state=${state}`, {
98
+ headers: { cookie: cookies },
99
+ }),
100
+ );
101
+ expect(callback.status).toBe(302);
102
+ const location = new URL(callback.headers.get("location") ?? "");
103
+ expect(location.protocol).toBe(`${scheme}:`);
104
+ const handedBack = location.searchParams.get("cookie") ?? "";
105
+ expect(handedBack, `redirect was ${location.href}`).toMatch(/better-auth\.session_token=/);
106
+ });
107
+ });