Just logging an error makes it difficult to relate the error log to code in the codebase. Error logging a message right before the error itself makes it easier to search the codebase for that error log.
233 lines
6.4 KiB
TypeScript
233 lines
6.4 KiB
TypeScript
import NextAuth from "next-auth"
|
|
|
|
import { env } from "@/env/server"
|
|
|
|
import { LoginTypeEnum } from "./types/components/tracking"
|
|
|
|
import type { NextAuthConfig, User } from "next-auth"
|
|
import type { OIDCConfig } from "next-auth/providers"
|
|
|
|
function getLoginType(user: User) {
|
|
// TODO: handle magic link, should be enough to just check for Nonce.
|
|
// if (user?.nonce) {
|
|
// return LoginTypeEnum.MagicLink
|
|
// }
|
|
|
|
if (user?.login_with.includes("@")) {
|
|
return LoginTypeEnum.email
|
|
} else {
|
|
return LoginTypeEnum["membership number"]
|
|
}
|
|
}
|
|
|
|
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,
|
|
login_with: profile.login_with,
|
|
}
|
|
},
|
|
} satisfies OIDCConfig<User>
|
|
|
|
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("Error in auth redirect callback")
|
|
console.error(e)
|
|
}
|
|
}
|
|
return baseUrl
|
|
},
|
|
async authorized({ auth, request }) {
|
|
return true
|
|
},
|
|
async jwt({ account, session, token, trigger, user }) {
|
|
const loginType = getLoginType(user)
|
|
if (account) {
|
|
return {
|
|
access_token: account.access_token,
|
|
expires_at: account.expires_at
|
|
? account.expires_at * 1000
|
|
: undefined,
|
|
refresh_token: account.refresh_token,
|
|
loginType,
|
|
}
|
|
} else if (Date.now() < token.expires_at) {
|
|
return token
|
|
} else {
|
|
try {
|
|
console.log(
|
|
"token-debug Access token expired, trying to refresh it.",
|
|
{
|
|
expires_at: token.expires_at,
|
|
sub: token.sub,
|
|
token: token.access_token,
|
|
}
|
|
)
|
|
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 new_tokens = await response.json()
|
|
|
|
if (!response.ok) {
|
|
console.log("token-debug Token response was not ok", {
|
|
status: response.status,
|
|
statusText: response.statusText,
|
|
sub: token.sub,
|
|
})
|
|
throw new_tokens
|
|
}
|
|
|
|
console.log("token-debug Successfully got new token(s)", {
|
|
expires_at: new_tokens.expires_at,
|
|
got_new_refresh_token:
|
|
new_tokens.refresh_token !== token.refresh_token,
|
|
got_new_access_token:
|
|
new_tokens.access_token !== token.access_token,
|
|
sub: token.sub,
|
|
})
|
|
|
|
return {
|
|
...token,
|
|
access_token: new_tokens.access_token,
|
|
expires_at: new_tokens.expires_at,
|
|
refresh_token: new_tokens.refresh_token ?? token.refresh_token,
|
|
}
|
|
} catch (error) {
|
|
console.log("token-debug Error thrown when trying to refresh", {
|
|
error,
|
|
sub: token.sub,
|
|
})
|
|
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)
|