Files
TJWaterFrontend_Refine/src/app/api/auth/[...nextauth]/options.ts
T

109 lines
3.1 KiB
TypeScript

import { NextAuthOptions } from "next-auth";
import { JWT } from "next-auth/jwt";
import KeycloakProvider from "next-auth/providers/keycloak";
import Avatar from "@assets/avatar/avatar-small.jpeg";
type KeycloakTokenResponse = {
access_token: string;
expires_in: number;
refresh_token?: string;
};
const keycloakIssuer = process.env.KEYCLOAK_ISSUER!;
const keycloakClientId = process.env.KEYCLOAK_CLIENT_ID!;
const keycloakClientSecret = process.env.KEYCLOAK_CLIENT_SECRET!;
const keycloakTokenEndpoint = `${keycloakIssuer.replace(/\/$/, "")}/protocol/openid-connect/token`;
const refreshAccessToken = async (token: JWT): Promise<JWT> => {
if (!token.refreshToken) {
return { ...token, error: "RefreshAccessTokenError" };
}
const body = new URLSearchParams({
grant_type: "refresh_token",
client_id: keycloakClientId,
client_secret: keycloakClientSecret,
refresh_token: token.refreshToken,
});
const response = await fetch(keycloakTokenEndpoint, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body,
});
const refreshed = (await response.json()) as KeycloakTokenResponse;
if (!response.ok || !refreshed.access_token || typeof refreshed.expires_in !== "number") {
return { ...token, error: "RefreshAccessTokenError" };
}
return {
...token,
accessToken: refreshed.access_token,
accessTokenExpires: Date.now() + refreshed.expires_in * 1000,
refreshToken: refreshed.refresh_token ?? token.refreshToken,
error: undefined,
};
};
const authOptions: NextAuthOptions = {
// Configure one or more authentication providers
providers: [
KeycloakProvider({
clientId: keycloakClientId,
clientSecret: keycloakClientSecret,
issuer: keycloakIssuer,
profile(profile) {
return {
id: profile.sub,
name: profile.name ?? profile.preferred_username,
email: profile.email,
image: Avatar.src,
};
},
}),
],
secret: process.env.NEXTAUTH_SECRET,
callbacks: {
jwt: async ({ token, profile, account }) => {
if (profile?.sub) {
token.sub = profile.sub;
}
if (account) {
if (account.access_token) {
token.accessToken = account.access_token;
}
if (account.refresh_token) {
token.refreshToken = account.refresh_token;
}
if (typeof account.expires_at === "number") {
token.accessTokenExpires = account.expires_at * 1000;
}
token.error = undefined;
return token;
}
if (typeof token.accessTokenExpires === "number" && Date.now() < token.accessTokenExpires - 30_000) {
return token;
}
return refreshAccessToken(token);
},
session: async ({ session, token }) => {
if (session.user && token.sub) {
session.user.id = token.sub;
}
if (token.accessToken) {
session.accessToken = token.accessToken;
}
if (token.error) {
session.error = token.error;
}
return session;
},
},
};
export default authOptions;