import NextAuth from "next-auth" import { env } from "@/env/server" import type { NextAuthConfig, User } from "next-auth" import type { OIDCConfig } from "next-auth/providers" const customProvider = { clientId: env.CURITY_CLIENT_ID_USER, clientSecret: env.CURITY_CLIENT_SECRET_USER, id: "curity", name: "Curity", type: "oidc", // FIXME: This is incorrect. We should not hard code this. // It should be ${env.CURITY_ISSUER_USER}. // This change requires sync between Curity deploy and CurrentWeb and NewWeb. issuer: "https://scandichotels.com", authorization: { url: `${env.CURITY_ISSUER_USER}/oauth/v2/authorize`, params: { scope: ["openid", "profile"].join(" "), /** * The `acr_values` param is used to make Curity display the proper login * page for Scandic. Without the parameter Curity presents some choices * to the user which we do not want. */ acr_values: "acr", }, }, token: { url: `${env.CURITY_ISSUER_USER}/oauth/v2/token`, }, userinfo: { url: `${env.CURITY_ISSUER_USER}/oauth/v2/userinfo`, }, profile(profile) { return { id: profile.id, sub: profile.sub, given_name: profile.given_name, } }, } satisfies OIDCConfig export const config = { debug: env.NEXTAUTH_DEBUG, providers: [customProvider], redirectProxyUrl: env.NEXTAUTH_REDIRECT_PROXY_URL, trustHost: true, session: { strategy: "jwt", }, callbacks: { async signIn() { return true }, async session({ session, token }) { session.error = token.error if (session.user) { return { ...session, token, user: { ...session.user, id: token.sub, }, } } return session }, async redirect({ baseUrl, url }) { if (url.startsWith("/")) { // Allows relative callback URLs return `${baseUrl}${url}` } else { // Assume absolute URL try { const parsedUrl = new URL(url) if ( /\.scandichotels\.(dk|de|com|fi|no|se)$/.test(parsedUrl.hostname) ) { // Allows any subdomains on all top level domains above return url } else if (parsedUrl.origin === baseUrl) { // Allows callback URLs on the same origin return url } } catch (e) { console.error(e) } } return baseUrl }, async authorized({ auth, request }) { return true }, async jwt({ account, session, token, trigger }) { if (account) { return { access_token: account.access_token, expires_at: account.expires_at ? account.expires_at * 1000 : undefined, refresh_token: account.refresh_token, } } else if (Date.now() < token.expires_at) { return token } else { try { const response = await fetch( `${env.CURITY_ISSUER_USER}/oauth/v2/token`, { body: new URLSearchParams({ client_id: env.CURITY_CLIENT_ID_USER, client_secret: env.CURITY_CLIENT_SECRET_USER, grant_type: "refresh_token", refresh_token: token.refresh_token, }), headers: { "Content-Type": "application/x-www-form-urlencoded", }, method: "POST", } ) const tokens = await response.json() if (!response.ok) { throw tokens } return { ...token, access_token: tokens.access_token, expires_at: tokens.expires_at, refresh_token: tokens.refresh_token ?? token.refresh_token, } } catch (error) { return { ...token, error: "RefreshAccessTokenError" as const, } } } }, }, // events: { // async signIn() { // console.log("#### SIGNIN EVENT ARGS ######") // console.log(arguments) // console.log("#### END - SIGNIN EVENT ARGS ######") // }, // async signOut() { // console.log("#### SIGNOUT EVENT ARGS ######") // console.log(arguments) // console.log("#### END - SIGNOUT EVENT ARGS ######") // }, // async session() { // console.log("#### SESSION EVENT ARGS ######") // console.log(arguments) // console.log("#### END - SESSION EVENT ARGS ######") // }, // }, // logger: { // error(code, ...message) { // console.info("ERROR LOGGER") // console.error(code, message) // }, // warn(code, ...message) { // console.info("WARN LOGGER") // console.warn(code, message) // }, // debug(code, ...message) { // console.info("DEBUG LOGGER") // console.debug(code, message) // }, // }, } satisfies NextAuthConfig export const { handlers: { GET, POST }, auth, signIn, signOut, } = NextAuth(config)