@imtf/auth 2.0.0-beta.0 → 3.0.0-beta.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 CHANGED
@@ -27,7 +27,7 @@ This package supplies IMTF's `better-auth` configuration and a couple of OIDC-fl
27
27
  ```ts
28
28
  import "server-only";
29
29
 
30
- import { getBetterAuthOptions } from "@imtf/auth";
30
+ import { getBetterAuthOptions } from "@imtf/auth/server";
31
31
  import { betterAuth, type BetterAuthOptions } from "better-auth";
32
32
 
33
33
  export const auth = betterAuth({
@@ -68,7 +68,7 @@ export const POST = handler;
68
68
  ### Federated sign-out route: `app/api/auth/sign-out-url/route.ts`
69
69
 
70
70
  ```ts
71
- import { getSignOutUrlRoute } from "@imtf/auth";
71
+ import { getSignOutUrlRoute } from "@imtf/auth/server";
72
72
  import { headers } from "next/headers";
73
73
  import { auth } from "@/auth";
74
74
 
@@ -76,9 +76,12 @@ export const GET = getSignOutUrlRoute({
76
76
  auth,
77
77
  discoveryUrl: `${process.env.OIDC_ISSUER_URL}/.well-known/openid-configuration`,
78
78
  headers,
79
+ clientId: process.env.OIDC_CLIENT_ID,
79
80
  });
80
81
  ```
81
82
 
83
+ > `clientId` lets the route build an end-session URL even when no ID token is available — for example when the OIDC provider's callback failed before better-auth stored an account cookie (missing email claim, denied consent, etc.). Without it, that case has no way to end the provider's SSO session, which otherwise leaves the user stuck in a silent re-authentication loop.
84
+
82
85
  ### Session check in a server component
83
86
 
84
87
  ```ts
@@ -121,14 +124,14 @@ function SignOutButton() {
121
124
 
122
125
  ### Full export list
123
126
 
127
+ The package has two entry points. `@imtf/auth` is safe to import from client components; `@imtf/auth/server` must only be imported from server code.
128
+
124
129
  ```ts
125
- import {
126
- getBetterAuthOptions,
127
- getBetterAuthClientOptions,
128
- getSignOutUrlRoute,
129
- useSignOut,
130
- PROVIDER_ID,
131
- } from "@imtf/auth";
130
+ // Client-safe — no `better-auth` server code in the module graph.
131
+ import { getBetterAuthClientOptions, useSignOut, PROVIDER_ID } from "@imtf/auth";
132
+
133
+ // Server only — pulls in `better-auth/next-js`, `better-auth/plugins` and `next/headers`.
134
+ import { getBetterAuthOptions, getSignOutUrlRoute, PROVIDER_ID } from "@imtf/auth/server";
132
135
  ```
133
136
 
134
137
  > `getBetterAuthClientOptions` is deprecated as of `better-auth` 1.7 — generic OAuth sign-in no longer needs a client plugin, so `createAuthClient()` can be called with no options as shown above.
@@ -161,4 +164,5 @@ pnpm run test:coverage
161
164
 
162
165
  - **OAuth callback URL path changed.** The redirect URI that `better-auth` exposes for the generic OIDC provider moved from `{baseURL}/oauth2/callback/{providerId}` to `{baseURL}/callback/{providerId}` (e.g. `https://app.example.com/api/auth/oauth2/callback/oidc-provider` → `https://app.example.com/api/auth/callback/oidc-provider`). **You must update the redirect URI registered with your OIDC provider/IdP client** before deploying this upgrade, or sign-in will fail.
163
166
  - **`getSignOutUrlRoute` dropped its `providerId` option.** `better-auth`'s `getAccessToken` API no longer accepts `providerId` to select an account; it now resolves the account from the signed account cookie (`useAccountCookie: true`), matching this package's database-less, `storeAccountCookie: true` configuration. If you were passing `providerId` to `getSignOutUrlRoute`, remove it — it is no longer read.
167
+ - **Server helpers moved to `@imtf/auth/server`.** `getBetterAuthOptions` and `getSignOutUrlRoute` are no longer exported from the package root; update those two imports to `@imtf/auth/server`. The root entry now exposes only the client-safe surface (`PROVIDER_ID`, `useSignOut`, `getBetterAuthClientOptions`). Previously the single barrel re-exported both halves, so any client component importing even a constant from `@imtf/auth` dragged `better-auth`'s server router into the browser bundle — including an unused OpenAPI reference page that loads a script from `cdn.jsdelivr.net`, which reads as a remote-script inclusion to security scanners.
164
168
  - **No client plugin is required for generic OAuth sign-in anymore.** `getBetterAuthClientOptions()` no longer registers `genericOAuthClient()` (removed upstream). Generic/custom OAuth providers now sign in through the standard `authClient.signIn.social({ provider })` / `authClient.linkSocial({ provider })` methods. No action needed on your end beyond upgrading — this package's exported options already reflect the change.
package/lib/index.esm.js CHANGED
@@ -1,6 +1,4 @@
1
- import { PROVIDER_ID } from "./constants.esm.js";
2
- import { getBetterAuthOptions } from "./options/auth.esm.js";
3
1
  import { getBetterAuthClientOptions } from "./options/auth-client.esm.js";
4
- import { getSignOutUrlRoute } from "./routes/getSignOutUrlRoute.esm.js";
2
+ import { PROVIDER_ID } from "./constants.esm.js";
5
3
  import { useSignOut } from "./hooks/useSignOut.esm.js";
6
- export { PROVIDER_ID, getBetterAuthClientOptions, getBetterAuthOptions, getSignOutUrlRoute, useSignOut };
4
+ export { PROVIDER_ID, getBetterAuthClientOptions, useSignOut };
@@ -1,5 +1,5 @@
1
1
  //#region src/routes/getSignOutUrlRoute.ts
2
- function getSignOutUrlRoute({ auth, discoveryUrl, federatedLogout = true, headers }) {
2
+ function getSignOutUrlRoute({ auth, discoveryUrl, federatedLogout = true, headers, clientId }) {
3
3
  /**
4
4
  * API route that constructs a federated logout URL for OIDC providers.
5
5
  * Returns null if federated logout is disabled.
@@ -19,7 +19,14 @@ function getSignOutUrlRoute({ auth, discoveryUrl, federatedLogout = true, header
19
19
  body: { useAccountCookie: true },
20
20
  headers: await headers()
21
21
  }).catch(() => null);
22
- if (!token?.idToken) return Response.json({ error: "No ID token found for user" }, { status: 400 });
22
+ /**
23
+ * No ID token is available, e.g. when the OIDC provider redirected back
24
+ * with an error (missing email, denied consent, etc.) before better-auth
25
+ * ever stored an account cookie. Without `client_id`, we have no way to
26
+ * end the provider's SSO session, which would otherwise leave the user
27
+ * stuck in a silent re-authentication loop with no way to recover.
28
+ */
29
+ if (!token?.idToken && !clientId) return Response.json({ error: "No ID token found for user" }, { status: 400 });
23
30
  /**
24
31
  * Fetch the well-known configuration from the OIDC provider to construct the
25
32
  * logout URL.
@@ -33,12 +40,17 @@ function getSignOutUrlRoute({ auth, discoveryUrl, federatedLogout = true, header
33
40
  * Construct the logout URL with parameters.
34
41
  * - end_session_endpoint: The OIDC provider's end_session_endpoint.
35
42
  * - post_logout_redirect_uri: The URL to redirect to after logout.
36
- * - id_token_hint: The user's ID token.
43
+ * - id_token_hint: The user's ID token, when available.
44
+ * - client_id: Fallback identity hint when there's no ID token. The
45
+ * provider uses its own browser session cookie to find the SSO
46
+ * session to end, so this still works without a token, though it may
47
+ * show a logout confirmation page instead of ending silently.
37
48
  */
38
49
  const logoutUrl = new URL(endSessionEndpoint);
39
50
  const postLogoutRedirectUri = request.nextUrl.searchParams.get("post_logout_redirect_uri") ?? "/auth/sign-in";
40
51
  logoutUrl.searchParams.set("post_logout_redirect_uri", postLogoutRedirectUri);
41
- logoutUrl.searchParams.set("id_token_hint", token.idToken);
52
+ if (token?.idToken) logoutUrl.searchParams.set("id_token_hint", token.idToken);
53
+ else if (clientId) logoutUrl.searchParams.set("client_id", clientId);
42
54
  return Response.json({ url: logoutUrl.toString() });
43
55
  } catch (error) {
44
56
  console.error("Failed to construct logout URL:", error);
@@ -0,0 +1,4 @@
1
+ import { PROVIDER_ID } from "./constants.esm.js";
2
+ import { getBetterAuthOptions } from "./options/auth.esm.js";
3
+ import { getSignOutUrlRoute } from "./routes/getSignOutUrlRoute.esm.js";
4
+ export { PROVIDER_ID, getBetterAuthOptions, getSignOutUrlRoute };
@@ -1,5 +1,3 @@
1
- export { getBetterAuthOptions } from "./options/auth";
2
1
  export { getBetterAuthClientOptions } from "./options/auth-client";
3
- export { getSignOutUrlRoute } from "./routes/getSignOutUrlRoute";
4
2
  export { PROVIDER_ID } from "./constants";
5
3
  export { useSignOut } from "./hooks/useSignOut";
@@ -1,9 +1,15 @@
1
1
  import type { Auth } from "better-auth";
2
2
  import { headers as nextHeaders } from "next/headers";
3
3
  import type { NextRequest } from "next/server";
4
- export declare function getSignOutUrlRoute({ auth, discoveryUrl, federatedLogout, headers, }: {
4
+ export declare function getSignOutUrlRoute({ auth, discoveryUrl, federatedLogout, headers, clientId, }: {
5
5
  auth: Auth;
6
6
  discoveryUrl?: string;
7
7
  federatedLogout?: boolean;
8
8
  headers: typeof nextHeaders;
9
+ /**
10
+ * OIDC client ID, used to build the end-session URL when no ID token can be
11
+ * retrieved (for example, when the user never completed a sign-in, so no
12
+ * account cookie was ever stored). Required to recover from that case.
13
+ */
14
+ clientId?: string;
9
15
  }): (request: NextRequest) => Promise<Response>;
@@ -0,0 +1,3 @@
1
+ export { getBetterAuthOptions } from "./options/auth";
2
+ export { getSignOutUrlRoute } from "./routes/getSignOutUrlRoute";
3
+ export { PROVIDER_ID } from "./constants";
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "@imtf/auth",
3
- "version": "2.0.0-beta.0",
3
+ "version": "3.0.0-beta.0",
4
4
  "description": "Better auth wrapper for imtf usage",
5
5
  "license": "UNLICENSED",
6
6
  "author": "",
7
7
  "type": "module",
8
+ "sideEffects": false,
8
9
  "files": [
9
10
  "lib",
10
11
  "schemas"
@@ -15,22 +16,24 @@
15
16
  ".": {
16
17
  "types": "./lib/types/index.d.ts",
17
18
  "import": "./lib/index.esm.js"
19
+ },
20
+ "./server": {
21
+ "types": "./lib/types/server.d.ts",
22
+ "import": "./lib/server.esm.js"
18
23
  }
19
24
  },
20
25
  "dependencies": {
21
- "@imtf/panache": "^5.10.2"
26
+ "@imtf/panache": "^6.0.1"
22
27
  },
23
28
  "peerDependencies": {
24
- "@mui/lab": "^7.0.1-beta.18",
25
- "@mui/material": "^7.1.0",
29
+ "@mui/material": "^7.1.0 || ^9.4.0",
26
30
  "react": "^18.0.0 || ^19.0.0",
27
31
  "react-dom": "^18.0.0 || ^19.0.0",
28
32
  "better-auth": "^1.7.1",
29
33
  "zod": "^4.3.6"
30
34
  },
31
35
  "devDependencies": {
32
- "@mui/lab": "^7.0.1-beta.18",
33
- "@mui/material": "^7.1.0",
36
+ "@mui/material": "^9.4.0",
34
37
  "better-auth": "^1.7.1",
35
38
  "zod": "^4.3.6"
36
39
  },