@imtf/auth 2.0.0-beta.0 → 2.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.
package/README.md
CHANGED
|
@@ -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
|
|
@@ -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
|
-
|
|
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);
|
|
@@ -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>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@imtf/auth",
|
|
3
|
-
"version": "2.0.0
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Better auth wrapper for imtf usage",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"author": "",
|
|
@@ -18,19 +18,17 @@
|
|
|
18
18
|
}
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@imtf/panache": "^
|
|
21
|
+
"@imtf/panache": "^6.0.0"
|
|
22
22
|
},
|
|
23
23
|
"peerDependencies": {
|
|
24
|
-
"@mui/
|
|
25
|
-
"@mui/material": "^7.1.0",
|
|
24
|
+
"@mui/material": "^7.1.0 || ^9.4.0",
|
|
26
25
|
"react": "^18.0.0 || ^19.0.0",
|
|
27
26
|
"react-dom": "^18.0.0 || ^19.0.0",
|
|
28
27
|
"better-auth": "^1.7.1",
|
|
29
28
|
"zod": "^4.3.6"
|
|
30
29
|
},
|
|
31
30
|
"devDependencies": {
|
|
32
|
-
"@mui/
|
|
33
|
-
"@mui/material": "^7.1.0",
|
|
31
|
+
"@mui/material": "^9.4.0",
|
|
34
32
|
"better-auth": "^1.7.1",
|
|
35
33
|
"zod": "^4.3.6"
|
|
36
34
|
},
|