refactor: generic OIDC provider — Auth.js no longer Authentik-specific

Replaces the Authentik-preset provider with an inline generic OIDC config
(id: "oidc"). The verifier path was already protocol-generic; the only
Authentik-named piece was the next-auth provider preset, which mapped to
the same OIDC fields anyway. Any compliant IdP — Authentik, EntraID,
Keycloak, Okta, Auth0, Zitadel, etc. — now works with just the existing
OIDC_* env vars.

Breaking change for existing deployments: the Auth.js callback path
changes from /api/auth/callback/authentik → /api/auth/callback/oidc.
Update the Web-UI client's redirect URI in your IdP before redeploying.

README rewritten to frame Authentik as the worked example, with a
concept-mapping table for EntraID and Keycloak, and audience-claim notes
for non-Authentik IdPs in the troubleshooting section.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-15 10:15:44 -07:00
co-authored by Claude Opus 4.7
parent 10a1e0062e
commit d7707dd158
2 changed files with 79 additions and 44 deletions
+15 -7
View File
@@ -1,5 +1,4 @@
import NextAuth from "next-auth";
import Authentik from "next-auth/providers/authentik";
import { env } from "@/lib/env";
import { db } from "@/lib/db/client";
import { users } from "@/lib/db/schema";
@@ -7,17 +6,26 @@ import { users } from "@/lib/db/schema";
/**
* NextAuth (Auth.js v5) configuration.
*
* Authentik is the OIDC issuer. We store the user's OIDC `sub` + `iss` on
* first sign-in, upserting a row in `users`. The internal user UUID lives on
* the JWT/session so downstream code never has to re-resolve it.
* Uses a generic OIDC provider so any compliant identity provider works —
* Authentik (the example we run in dev), EntraID, Keycloak, Okta, Auth0,
* Zitadel, etc. The provider id is "oidc", which makes the callback URL
* `/api/auth/callback/oidc`. Whichever IdP you're using needs that URL
* registered as a redirect URI on its OAuth client.
*
* We store the user's OIDC `sub` + `iss` on first sign-in, upserting a row
* in `users`. The internal user UUID lives on the JWT/session so
* downstream code never has to re-resolve it.
*/
export const { auth, handlers, signIn, signOut } = NextAuth({
providers: [
Authentik({
{
id: "oidc",
name: "OIDC",
type: "oidc",
issuer: env().OIDC_ISSUER,
clientId: env().OIDC_CLIENT_ID_WEB,
clientSecret: env().OIDC_CLIENT_SECRET_WEB,
issuer: env().OIDC_ISSUER,
}),
},
],
secret: env().NEXTAUTH_SECRET,
session: { strategy: "jwt" },