NextAuth v5(Auth.js)+ Authentik:signOut无法彻底登出用户
我在一个使用Next.js App Router的应用中,使用NextAuth v5,并将Authentik作为OIDC提供者。
当用户点击“登出”时,我调用 signOut({ redirect: false }) 并重定向到 /login。NextAuth的会话cookie会被清除,但Authentik的浏览器会话仍然存在——如果有人在登录页输入相同的邮箱,Authentik会在不发送新的魔法链接的情况下自动认证他们。
I've tried the OIDC end-session redirect approach:
await signOut({ redirect: false });
const logoutUrl = new URL("https://auth.example.com/application/o/my-app/end-session/");
logoutUrl.searchParams.set("id_token_hint", idToken);
logoutUrl.searchParams.set("post_logout_redirect_uri", "https://myapp.example.com/login");
redirect(logoutUrl.toString());
但Authentik会显示它的“Default Invalidation Flow”界面,而不是重定向到 post\_logout\_redirect\_uri。如何让用户在NextAuth与 Authentik双方都彻底登出,并直接返回到我的登录页?
解决方案
端会话重定向的方法在Authentik上并不能可靠地工作——它的Default Invalidation Flow会拦截该请求,无论 post\_logout\_redirect\_uri 的具体含义为何。更可靠的做法是在服务端通过管理员API撤销Authentik会话,然后清除NextAuth的 cookie,并直接重定向回你的登录页面。
export async function revokeAuthentikSessions(email: string): Promise<void> {
const userRes = await fetch(
`${AUTHENTIK_BASE}/core/users/?email=${encodeURIComponent(email)}`,
{ headers: authHeaders() }
);
if (!userRes.ok) return;
const { results } = await userRes.json();
if (!results.length) return;
// Iterate ALL users matching this email — if you use JIT provisioning,
// multiple Authentik accounts may share the same email address. Taking
// only results[0] will silently miss the active session.
await Promise.all(
results.map(async (u: { pk: number }) => {
const sessRes = await fetch(
`${AUTHENTIK_BASE}/core/authenticated_sessions/?user=${u.pk}`,
{ headers: authHeaders() }
);
if (!sessRes.ok) return;
const { results: sessions } = await sessRes.json();
await Promise.all(
sessions.map((s: { uuid: string }) =>
fetch(`${AUTHENTIK_BASE}/core/authenticated_sessions/${s.uuid}/`, {
method: "DELETE",
headers: authHeaders(),
})
)
);
})
);
}
然后在你的登出服务器操作中:
action={async () => {
"use server";
const session = await auth();
if (session?.user?.email) await revokeAuthentikSessions(session.user.email);
await signOut({ redirect: false });
redirect("/login");
}}
即时按需创建(JIT)用户的问题:如果你的应用在首次登录时按需创建Authentik用户(如 provider\_42 等),按邮箱查找会返回多个用户。results[0] 是非确定性的——活动会话可能属于与第一次返回的用户不同的那个用户。上面的循环可以处理这个问题。
带背景的完整写法:https://gist.github.com/speckofthecosmos/e8c471ee9a59f7d9494a854fff4074ad