diff --git a/apps/partner-sas/app/api/web/auth/callback/curity/route.ts b/apps/partner-sas/app/api/web/auth/callback/curity/route.ts index 71ef26666..b97fea14f 100644 --- a/apps/partner-sas/app/api/web/auth/callback/curity/route.ts +++ b/apps/partner-sas/app/api/web/auth/callback/curity/route.ts @@ -5,6 +5,7 @@ import { createLogger } from "@scandic-hotels/common/logger/createLogger" import { env } from "@/env/server" +import { signInCounter } from "@/auth" import { getToken } from "@/auth/scandic/getToken" import { createSocialSession } from "@/auth/scandic/session" @@ -13,14 +14,17 @@ export async function GET(req: NextRequest) { const code = req.nextUrl.searchParams.get("code") const state = req.nextUrl.searchParams.get("state") const savedState = req.cookies.get("oauth_state")?.value + const counter = signInCounter.init({ type: "curity" }) if (!code || !state) { logger.error("Missing code or state", { url: req.nextUrl.toString() }) + counter.fail("missing code or state") throw new Error("Missing code or state, auth failed") } if (!savedState) { logger.error("No saved state cookie", { url: req.nextUrl.toString() }) + counter.fail("missing state mismatch") throw new Error("Missing saved oauth state, auth failed") } @@ -30,6 +34,7 @@ export async function GET(req: NextRequest) { saved: savedState, url: req.nextUrl.toString(), }) + counter.fail("state mismatch") throw new Error("Invalid state, possible CSRF") } @@ -43,6 +48,8 @@ export async function GET(req: NextRequest) { expires_in: tokenResponse.expires_in, }) + counter.success() + const c = await cookies() c.delete({ name: "oauth_state", path: "/" }) diff --git a/apps/partner-sas/auth.ts b/apps/partner-sas/auth.ts index 3796e31e7..a60345c8c 100644 --- a/apps/partner-sas/auth.ts +++ b/apps/partner-sas/auth.ts @@ -3,6 +3,7 @@ import Auth0Provider from "next-auth/providers/auth0" import { dt } from "@scandic-hotels/common/dt" import { createLogger } from "@scandic-hotels/common/logger/createLogger" +import { createCounter } from "@scandic-hotels/common/telemetry" import { safeTry } from "@scandic-hotels/common/utils/safeTry" import { getEuroBonusProfileData } from "@scandic-hotels/trpc/routers/partners/sas/getEuroBonusProfile" @@ -13,6 +14,8 @@ import type { JWT } from "next-auth/jwt" const authLogger = createLogger("auth") export const PRE_REFRESH_TIME_IN_SECONDS = 180 +export const signInCounter = createCounter("auth", "signIn") + async function refreshTokens(token: JWT) { try { if (!token.refresh_token) { @@ -118,6 +121,7 @@ const config: NextAuthConfig = { async jwt(params) { if (params.trigger === "signIn") { + const counter = signInCounter.init({ type: "sas" }) const accessToken = params.account?.access_token // expires_at is in seconds for SAS, we need milliseconds const expiresAt = params.account?.expires_at @@ -125,16 +129,19 @@ const config: NextAuthConfig = { : null if (!accessToken) { + counter.fail("missing access token") throw new Error("AuthError: Missing access token") } if (!expiresAt) { + counter.fail("missing expiry time") throw new Error("AuthError: Missing expiry time") } const refreshToken = params.account?.refresh_token if (!refreshToken) { + counter.fail("missing refresh token") authLogger.warn("⚠️ refreshToken missing") } @@ -146,6 +153,7 @@ const config: NextAuthConfig = { authLogger.error("Failed to fetch EuroBonus profile", error) } + counter.success() return { ...params.token, isLinked: eurobonusProfile?.linkStatus === "LINKED", diff --git a/apps/scandic-web/auth.ts b/apps/scandic-web/auth.ts index f575276e1..792ab5783 100644 --- a/apps/scandic-web/auth.ts +++ b/apps/scandic-web/auth.ts @@ -2,6 +2,7 @@ import NextAuth, { type NextAuthConfig, type User } from "next-auth" import { LoginTypeEnum } from "@scandic-hotels/common/constants/loginType" import { logger } from "@scandic-hotels/common/logger" +import { createCounter } from "@scandic-hotels/common/telemetry" import { PRE_REFRESH_TIME_IN_SECONDS } from "@/constants/auth" import { env } from "@/env/server" @@ -9,6 +10,8 @@ import { env } from "@/env/server" import type { JWT } from "next-auth/jwt" import type { OIDCConfig } from "next-auth/providers" +export const signInCounter = createCounter("auth", "signIn") + function getLoginType(user: User) { if (user?.login_with.includes("@")) { return LoginTypeEnum.email @@ -177,6 +180,9 @@ const baseConfig = { async jwt({ account, session, token, trigger, user, profile }) { const loginType = getLoginType(user) if (trigger === "signIn" && account) { + const counter = signInCounter.init({ type: "curity" }) + counter.success() + const mfa_scope = profile?.amr == "urn:com:scandichotels:scandic-otp" const tokenExpiry = account.expires_at ? account.expires_at * 1000