First steps towards the SAS partnership * otp flow now pretends to do the linking * Update LinkAccountForm header * Update redirect times * Clean up comments * Set maxAge on sas cookies * make all SAS routes protected * Merge remote-tracking branch 'refs/remotes/origin/feature/sas-login' into feature/sas-login * Require auth for sas link flow * Fix resend otp * Add error support to OneTimePasswordForm * Add Sentry to SAS error boundary * Move SAS_REQUEST_OTP_STATE_STORAGE_COOKIE_NAME * Add missing translations * Merge branch 'master' of bitbucket.org:scandic-swap/web into feature/sas-login * Merge branch 'feature/sas-login' of bitbucket.org:scandic-swap/web into feature/sas-login * Add TooManyCodesError component * Refactor GenericError to support new errors * Add FailedAttemptsError * remove removed component <VWOScript/> * Merge branch 'feature/sas-login' of bitbucket.org:scandic-swap/web into feature/sas-login * remove local cookie-bot reference * Fix sas campaign logo scaling * feature toggle the SAS stuff * Merge branch 'feature/sas-login' of bitbucket.org:scandic-swap/web into feature/sas-login * fix: use env vars for SAS endpoints Approved-by: Linus Flood
117 lines
2.8 KiB
TypeScript
117 lines
2.8 KiB
TypeScript
import { TRPCError } from "@trpc/server"
|
|
import { cookies } from "next/headers"
|
|
import { v4 as uuidv4 } from "uuid"
|
|
import { z } from "zod"
|
|
|
|
import { env } from "@/env/server"
|
|
import { protectedProcedure } from "@/server/trpc"
|
|
|
|
import { getSasToken } from "../../getSasToken"
|
|
import { SAS_REQUEST_OTP_STATE_STORAGE_COOKIE_NAME } from "../constants"
|
|
import {
|
|
parseSASRequestOtpError,
|
|
type RequestOtpGeneralError,
|
|
} from "./requestOtpError"
|
|
|
|
import type { OtpState } from "../getOTPState"
|
|
|
|
const inputSchema = z.object({})
|
|
|
|
const outputSchema = z.object({
|
|
status: z.string(),
|
|
referenceId: z.string().uuid(),
|
|
databaseUUID: z.string().uuid(),
|
|
otpExpiration: z.number(),
|
|
otpReceiver: z.string(),
|
|
})
|
|
|
|
export const requestOtp = protectedProcedure
|
|
.input(inputSchema)
|
|
.output(outputSchema)
|
|
.mutation(async function ({ ctx, input }) {
|
|
const sasAuthToken = getSasToken()
|
|
|
|
if (!sasAuthToken) {
|
|
// TODO: Should we verify that the SAS token isn't expired?
|
|
throw createError("AUTH_TOKEN_NOT_FOUND")
|
|
}
|
|
|
|
const tokenResponse = await fetchRequestOtp({ sasAuthToken })
|
|
console.log(
|
|
"[SAS] requestOtp",
|
|
tokenResponse.status,
|
|
tokenResponse.statusText
|
|
)
|
|
if (!tokenResponse.ok) {
|
|
const errorBody = await tokenResponse.json()
|
|
console.error("[SAS] requestOtp error", errorBody)
|
|
throw createError(errorBody)
|
|
}
|
|
|
|
const parseResult = outputSchema.safeParse(await tokenResponse.json())
|
|
if (!parseResult.success) {
|
|
throw createError(parseResult.error)
|
|
}
|
|
|
|
setSASOtpCookie(parseResult.data)
|
|
|
|
return parseResult.data
|
|
})
|
|
|
|
function createError(
|
|
errorBody:
|
|
| {
|
|
status: string
|
|
error: string
|
|
errorCode: number
|
|
databaseUUID: string
|
|
}
|
|
| Error
|
|
| RequestOtpGeneralError
|
|
): TRPCError {
|
|
const errorInfo = parseSASRequestOtpError(errorBody)
|
|
console.error("[SAS] createError", errorInfo)
|
|
return new TRPCError({
|
|
code: "BAD_REQUEST",
|
|
cause: errorInfo,
|
|
})
|
|
}
|
|
|
|
async function fetchRequestOtp({ sasAuthToken }: { sasAuthToken: string }) {
|
|
const endpoint = `${env.SAS_API_ENDPOINT}/api/scandic-partnership/customer/send-otp`
|
|
|
|
console.log("[SAS]: Requesting OTP")
|
|
|
|
return await fetch(endpoint, {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
"Ocp-Apim-Subscription-Key": env.SAS_OCP_APIM,
|
|
Authorization: `Bearer ${sasAuthToken}`,
|
|
},
|
|
body: JSON.stringify({
|
|
referenceId: uuidv4(),
|
|
}),
|
|
})
|
|
}
|
|
|
|
function setSASOtpCookie({
|
|
referenceId,
|
|
databaseUUID,
|
|
}: {
|
|
referenceId: string
|
|
databaseUUID: string
|
|
}) {
|
|
cookies().set(
|
|
SAS_REQUEST_OTP_STATE_STORAGE_COOKIE_NAME,
|
|
JSON.stringify({
|
|
referenceId: referenceId,
|
|
databaseUUID: databaseUUID,
|
|
} satisfies OtpState),
|
|
{
|
|
httpOnly: true,
|
|
maxAge: 3600,
|
|
}
|
|
)
|
|
}
|