feat: SW-162 Updated as per review comments
This commit is contained in:
@@ -41,18 +41,13 @@ export async function GET(
|
|||||||
* https://github.com/nextauthjs/next-auth/blob/3c035ec/packages/next-auth/src/lib/actions.ts#L76
|
* https://github.com/nextauthjs/next-auth/blob/3c035ec/packages/next-auth/src/lib/actions.ts#L76
|
||||||
*/
|
*/
|
||||||
const redirectUrl = await signIn(
|
const redirectUrl = await signIn(
|
||||||
"curity",
|
"curity-mfa",
|
||||||
{
|
{
|
||||||
redirectTo,
|
redirectTo,
|
||||||
redirect: false,
|
redirect: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ui_locales: context.params.lang,
|
ui_locales: context.params.lang,
|
||||||
scope: "profile_update openid profile",
|
|
||||||
// The below acr value is required as for New Web same Curity Client is used for MFA
|
|
||||||
// while in current web it is being setup using different Curity Client ID and secret
|
|
||||||
acr_values:
|
|
||||||
"urn:se:curity:authentication:otp-authenticator:OTP-Authenticator_web",
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
70
auth.ts
70
auth.ts
@@ -1,4 +1,4 @@
|
|||||||
import { decode,encode } from "@auth/core/jwt"
|
import { encode } from "@auth/core/jwt"
|
||||||
import { cookies } from "next/headers"
|
import { cookies } from "next/headers"
|
||||||
import NextAuth from "next-auth"
|
import NextAuth from "next-auth"
|
||||||
|
|
||||||
@@ -22,18 +22,39 @@ function getLoginType(user: User) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const customProvider = {
|
const sharedConfig = {
|
||||||
clientId: env.CURITY_CLIENT_ID_USER,
|
clientId: env.CURITY_CLIENT_ID_USER,
|
||||||
clientSecret: env.CURITY_CLIENT_SECRET_USER,
|
clientSecret: env.CURITY_CLIENT_SECRET_USER,
|
||||||
id: "curity",
|
|
||||||
name: "Curity",
|
|
||||||
type: "oidc",
|
|
||||||
// FIXME: This is incorrect. We should not hard code this.
|
// FIXME: This is incorrect. We should not hard code this.
|
||||||
// It should be ${env.CURITY_ISSUER_USER}.
|
// It should be ${env.CURITY_ISSUER_USER}.
|
||||||
// This change requires sync between Curity deploy and CurrentWeb and NewWeb.
|
// This change requires sync between Curity deploy and CurrentWeb and NewWeb.
|
||||||
issuer: "https://scandichotels.com",
|
issuer: "https://scandichotels.com",
|
||||||
authorization: {
|
authorization: {
|
||||||
url: `${env.CURITY_ISSUER_USER}/oauth/v2/authorize`,
|
url: `${env.CURITY_ISSUER_USER}/oauth/v2/authorize`,
|
||||||
|
},
|
||||||
|
token: {
|
||||||
|
url: `${env.CURITY_ISSUER_USER}/oauth/v2/token`,
|
||||||
|
},
|
||||||
|
userinfo: {
|
||||||
|
url: `${env.CURITY_ISSUER_USER}/oauth/v2/userinfo`,
|
||||||
|
},
|
||||||
|
profile(profile: User) {
|
||||||
|
return {
|
||||||
|
id: profile.id,
|
||||||
|
sub: profile.sub,
|
||||||
|
given_name: profile.given_name,
|
||||||
|
login_with: profile.login_with,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const curityProvider = {
|
||||||
|
...sharedConfig,
|
||||||
|
id: "curity",
|
||||||
|
name: "Curity",
|
||||||
|
type: "oidc",
|
||||||
|
authorization: {
|
||||||
|
...sharedConfig.authorization,
|
||||||
params: {
|
params: {
|
||||||
scope: ["openid", "profile"].join(" "),
|
scope: ["openid", "profile"].join(" "),
|
||||||
/**
|
/**
|
||||||
@@ -44,27 +65,30 @@ const customProvider = {
|
|||||||
acr_values: "acr",
|
acr_values: "acr",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
token: {
|
} satisfies OIDCConfig<User>
|
||||||
url: `${env.CURITY_ISSUER_USER}/oauth/v2/token`,
|
|
||||||
},
|
|
||||||
userinfo: {
|
|
||||||
url: `${env.CURITY_ISSUER_USER}/oauth/v2/userinfo`,
|
|
||||||
},
|
|
||||||
|
|
||||||
profile(profile) {
|
const curityMFAProvider = {
|
||||||
return {
|
...sharedConfig,
|
||||||
id: profile.id,
|
id: "curity-mfa",
|
||||||
sub: profile.sub,
|
name: "Curity MFA",
|
||||||
given_name: profile.given_name,
|
type: "oidc",
|
||||||
login_with: profile.login_with,
|
authorization: {
|
||||||
acr: profile.acr,
|
...sharedConfig.authorization,
|
||||||
}
|
params: {
|
||||||
|
scope: ["profile_update", "openid"].join(" "),
|
||||||
|
/**
|
||||||
|
* The below acr value is required as for New Web same Curity Client is used for MFA
|
||||||
|
* while in current web it is being setup using different Curity Client ID and secret
|
||||||
|
*/
|
||||||
|
acr_values:
|
||||||
|
"urn:se:curity:authentication:otp-authenticator:OTP-Authenticator_web",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
} satisfies OIDCConfig<User>
|
} satisfies OIDCConfig<User>
|
||||||
|
|
||||||
export const config = {
|
export const config = {
|
||||||
debug: env.NEXTAUTH_DEBUG,
|
debug: env.NEXTAUTH_DEBUG,
|
||||||
providers: [customProvider],
|
providers: [curityProvider, curityMFAProvider],
|
||||||
redirectProxyUrl: env.NEXTAUTH_REDIRECT_PROXY_URL,
|
redirectProxyUrl: env.NEXTAUTH_REDIRECT_PROXY_URL,
|
||||||
trustHost: true,
|
trustHost: true,
|
||||||
session: {
|
session: {
|
||||||
@@ -117,10 +141,7 @@ export const config = {
|
|||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
async jwt({ account, session, token, trigger, user }) {
|
async jwt({ account, session, token, trigger, user }) {
|
||||||
if (
|
if (account?.provider == "curity-mfa") {
|
||||||
user?.acr ==
|
|
||||||
"urn:se:curity:authentication:otp-authenticator:OTP-Authenticator_web"
|
|
||||||
) {
|
|
||||||
const cookieStore = cookies()
|
const cookieStore = cookies()
|
||||||
const value = token.access_token
|
const value = token.access_token
|
||||||
const secret = env.NEXTAUTH_SECRET
|
const secret = env.NEXTAUTH_SECRET
|
||||||
@@ -135,6 +156,7 @@ export const config = {
|
|||||||
cookieStore.set("_SecureMFA-token", mfaCookie.toString(), {
|
cookieStore.set("_SecureMFA-token", mfaCookie.toString(), {
|
||||||
maxAge: maxAge,
|
maxAge: maxAge,
|
||||||
})
|
})
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
const loginType = getLoginType(user)
|
const loginType = getLoginType(user)
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ export const middleware = auth(async (request) => {
|
|||||||
nextUrlClone.host = publicUrl.host
|
nextUrlClone.host = publicUrl.host
|
||||||
nextUrlClone.hostname = publicUrl.hostname
|
nextUrlClone.hostname = publicUrl.hostname
|
||||||
|
|
||||||
const isMFAValid = async function () {
|
async function isMFAInvalid() {
|
||||||
const cookieStore = cookies()
|
const cookieStore = cookies()
|
||||||
const mfaCookieValue = cookieStore.get("_SecureMFA-token")?.value
|
const mfaCookieValue = cookieStore.get("_SecureMFA-token")?.value
|
||||||
if (mfaCookieValue) {
|
if (mfaCookieValue) {
|
||||||
@@ -66,22 +66,22 @@ export const middleware = auth(async (request) => {
|
|||||||
salt: "_SecureMFA-token",
|
salt: "_SecureMFA-token",
|
||||||
})
|
})
|
||||||
if (mfaToken?.exp) {
|
if (mfaToken?.exp) {
|
||||||
return true
|
return false
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log("JWT decode failed", e)
|
console.log("JWT decode failed", e)
|
||||||
cookieStore.set("_SecureMFA-token", "", { maxAge: 0 })
|
cookieStore.set("_SecureMFA-token", "", { maxAge: 0 })
|
||||||
return false
|
return true
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return false
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isLoggedIn && !hasError) {
|
if (isLoggedIn && !hasError) {
|
||||||
const isMFAPath = mfaRequired.includes(nextUrl.pathname)
|
const isMFAPath = mfaRequired.includes(nextUrl.pathname)
|
||||||
const mfaValid = isMFAPath ? await isMFAValid() : true
|
const mfaInvalid = isMFAPath ? await isMFAInvalid() : false
|
||||||
if (!mfaValid) {
|
if (mfaInvalid) {
|
||||||
const mfaLoginUrl = mfaLogin[lang]
|
const mfaLoginUrl = mfaLogin[lang]
|
||||||
const nextUrlClone = nextUrl.clone()
|
const nextUrlClone = nextUrl.clone()
|
||||||
nextUrlClone.host = publicUrl.host
|
nextUrlClone.host = publicUrl.host
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import createJiti from "jiti"
|
import createJiti from "jiti"
|
||||||
import { fileURLToPath } from "url"
|
import { fileURLToPath } from "url"
|
||||||
|
|
||||||
import { login, logout } from "./constants/routes/handleAuth.js"
|
import { login, logout, mfaLogin } from "./constants/routes/handleAuth.js"
|
||||||
import { hotelReservation } from "./constants/routes/hotelReservation.js"
|
import { hotelReservation } from "./constants/routes/hotelReservation.js"
|
||||||
import { myPages } from "./constants/routes/myPages.js"
|
import { myPages } from "./constants/routes/myPages.js"
|
||||||
|
|
||||||
@@ -80,6 +80,11 @@ const nextConfig = {
|
|||||||
{ source: logout.fi, destination: "/fi/logout" },
|
{ source: logout.fi, destination: "/fi/logout" },
|
||||||
{ source: logout.no, destination: "/no/logout" },
|
{ source: logout.no, destination: "/no/logout" },
|
||||||
{ source: logout.sv, destination: "/sv/logout" },
|
{ source: logout.sv, destination: "/sv/logout" },
|
||||||
|
{ source: mfaLogin.da, destination: "/da/mfa-login" },
|
||||||
|
{ source: mfaLogin.de, destination: "/de/mfa-login" },
|
||||||
|
{ source: mfaLogin.fi, destination: "/fi/mfa-login" },
|
||||||
|
{ source: mfaLogin.no, destination: "/no/mfa-login" },
|
||||||
|
{ source: mfaLogin.sv, destination: "/sv/mfa-login" },
|
||||||
{
|
{
|
||||||
source: `${myPages.en}/:path*`,
|
source: `${myPages.en}/:path*`,
|
||||||
destination: `/en/my-pages/:path*`,
|
destination: `/en/my-pages/:path*`,
|
||||||
|
|||||||
1
types/auth.d.ts
vendored
1
types/auth.d.ts
vendored
@@ -27,6 +27,5 @@ declare module "next-auth" {
|
|||||||
sub: string
|
sub: string
|
||||||
email?: string
|
email?: string
|
||||||
login_with: string
|
login_with: string
|
||||||
acr?: string
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user