Merged in feature/curity-social-login (pull request #2963)
feat(SW-3541): Do social login after login to SAS * feat(auth): wip social login via curity * Setup social login auth flow * Merge branch 'master' of bitbucket.org:scandic-swap/web into feature/curity-social-login * Added support for getting scandic tokens and refresh them * feat: Enhance social login and session management with auto-refresh and improved error handling * Merge branch 'master' of bitbucket.org:scandic-swap/web into feature/curity-social-login * wrap layout in suspense * revert app/layout.tsx * fix import * cleanup * merge * merge * dont pass client_secret in the url to curity * add state validation when doing social login through /authorize * remove debug logging Approved-by: Anton Gunnarsson
This commit is contained in:
@@ -2,13 +2,7 @@
|
||||
|
||||
import { usePathname, useSearchParams } from "next/navigation"
|
||||
import { parseAsInteger, useQueryState } from "nuqs"
|
||||
import {
|
||||
createContext,
|
||||
useCallback,
|
||||
useContext,
|
||||
useMemo,
|
||||
useState,
|
||||
} from "react"
|
||||
import { createContext, useCallback, useContext, useMemo } from "react"
|
||||
import { type IntlShape, useIntl } from "react-intl"
|
||||
|
||||
import { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
|
||||
@@ -76,8 +70,7 @@ export function SelectRateProvider({
|
||||
parseAsInteger.withDefault(0)
|
||||
)
|
||||
|
||||
const [_bookingCodeFilter, setBookingCodeFilter] =
|
||||
useState<BookingCodeFilterEnum>(BookingCodeFilterEnum.Discounted)
|
||||
const [_bookingCodeFilter, setBookingCodeFilter] = useBookingCodeFilter()
|
||||
|
||||
const selectRateBooking = parseSelectRateSearchParams(
|
||||
searchParamsToRecord(searchParams)
|
||||
@@ -313,7 +306,7 @@ export function SelectRateProvider({
|
||||
_bookingCodeFilter === BookingCodeFilterEnum.Discounted &&
|
||||
!selectRateInput.data?.booking.bookingCode
|
||||
? BookingCodeFilterEnum.All
|
||||
: _bookingCodeFilter
|
||||
: (_bookingCodeFilter as BookingCodeFilterEnum)
|
||||
|
||||
const roomAvailabilityWithAdjustedRoomCount: (AvailabilityWithRoomInfo | null)[][] =
|
||||
roomAvailability.map((availability, roomIndex) => {
|
||||
@@ -557,12 +550,15 @@ function getAvailabilityForRoom(
|
||||
|
||||
function useUpdateBooking() {
|
||||
const pathname = usePathname()
|
||||
const [bookingCodeFilter] = useBookingCodeFilter()
|
||||
|
||||
return function updateBooking(booking: SelectRateBooking) {
|
||||
const newUrl = new URL(pathname, window.location.origin)
|
||||
|
||||
// TODO: Handle existing search params
|
||||
newUrl.search = serializeBookingSearchParams(booking).toString()
|
||||
newUrl.searchParams.set(BookingCodeFilterQueryName, bookingCodeFilter)
|
||||
|
||||
// router.replace(newUrl.toString(), { scroll: false })
|
||||
window.history.replaceState({}, "", newUrl.toString())
|
||||
}
|
||||
@@ -575,3 +571,10 @@ function isRoomPackage(x: {
|
||||
x.code as RoomPackageCodeEnum
|
||||
)
|
||||
}
|
||||
|
||||
const BookingCodeFilterQueryName = "bookingCodeFilter"
|
||||
function useBookingCodeFilter() {
|
||||
return useQueryState(BookingCodeFilterQueryName, {
|
||||
defaultValue: BookingCodeFilterEnum.Discounted,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import { createLogger } from "@scandic-hotels/common/logger/createLogger"
|
||||
import { env } from "../../../../env/server"
|
||||
import { protectedProcedure } from "../../../procedures"
|
||||
|
||||
import type { Session } from "next-auth"
|
||||
import type { LoginType } from "@scandic-hotels/common/constants/loginType"
|
||||
|
||||
const outputSchema = z.object({
|
||||
eurobonusNumber: z.string(),
|
||||
@@ -48,28 +48,39 @@ const outputSchema = z.object({
|
||||
const sasLogger = createLogger("SAS")
|
||||
const url = new URL("/api/scandic-partnership/v1/profile", env.SAS_API_ENDPOINT)
|
||||
|
||||
export async function getEuroBonusProfileData(session: Session) {
|
||||
if (session.token.loginType !== "sas") {
|
||||
return {
|
||||
error: {
|
||||
message: `Failed to fetch EuroBonus profile, expected loginType to be "sas" but was ${session.token.loginType}`,
|
||||
},
|
||||
} as const
|
||||
const requiredLoginType: LoginType[] = ["sas"]
|
||||
|
||||
export const getEuroBonusProfile = protectedProcedure
|
||||
.output(outputSchema)
|
||||
.query(async function ({ ctx }) {
|
||||
return await getEuroBonusProfileData({
|
||||
accessToken: ctx.session.token.access_token,
|
||||
loginType: ctx.session.token.loginType,
|
||||
})
|
||||
})
|
||||
|
||||
export async function getEuroBonusProfileData({
|
||||
accessToken,
|
||||
loginType,
|
||||
}: {
|
||||
loginType: LoginType
|
||||
accessToken: string
|
||||
}) {
|
||||
if (!accessToken) {
|
||||
throw new Error("Access token is required to fetch EuroBonus profile")
|
||||
}
|
||||
|
||||
if (!session.token.expires_at || session.token.expires_at < Date.now()) {
|
||||
return {
|
||||
error: {
|
||||
message: "Token expired sas",
|
||||
},
|
||||
} as const
|
||||
if (!requiredLoginType.includes(loginType)) {
|
||||
throw new Error(
|
||||
`Failed to fetch EuroBonus profile, expected loginType to be "${requiredLoginType}" but was "${loginType}"`
|
||||
)
|
||||
}
|
||||
|
||||
const response = await fetch(url, {
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Ocp-Apim-Subscription-Key": env.SAS_OCP_APIM,
|
||||
Authorization: `Bearer ${session?.token?.access_token}`,
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
},
|
||||
})
|
||||
|
||||
@@ -77,12 +88,9 @@ export async function getEuroBonusProfileData(session: Session) {
|
||||
sasLogger.error(
|
||||
`Failed to get EuroBonus profile, status: ${response.status}, statusText: ${response.statusText}`
|
||||
)
|
||||
return {
|
||||
error: {
|
||||
message: "Failed to fetch EuroBonus profile",
|
||||
cause: { status: response.status, statusText: response.statusText },
|
||||
},
|
||||
} as const
|
||||
throw new Error("Failed to fetch EuroBonus profile", {
|
||||
cause: { status: response.status, statusText: response.statusText },
|
||||
})
|
||||
}
|
||||
|
||||
const responseJson = await response.json()
|
||||
@@ -91,24 +99,8 @@ export async function getEuroBonusProfileData(session: Session) {
|
||||
sasLogger.error(
|
||||
`Failed to parse EuroBonus profile, cause: ${data.error.cause}, message: ${data.error.message}`
|
||||
)
|
||||
return {
|
||||
error: {
|
||||
message: `Failed to parse EuroBonus profile: ${data.error.message}`,
|
||||
cause: { status: response.status, statusText: response.statusText },
|
||||
},
|
||||
} as const
|
||||
throw new Error(`Failed to parse EuroBonus profile: ${data.error.message}`)
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
export const getEuroBonusProfile = protectedProcedure.query(async function ({
|
||||
ctx,
|
||||
}) {
|
||||
const verifiedSasUser = await getEuroBonusProfileData(ctx.session)
|
||||
if ("error" in verifiedSasUser) {
|
||||
throw new Error(verifiedSasUser.error?.message, {
|
||||
cause: verifiedSasUser.error?.cause,
|
||||
})
|
||||
}
|
||||
return verifiedSasUser.data
|
||||
})
|
||||
return data.data
|
||||
}
|
||||
|
||||
@@ -11,7 +11,10 @@ export async function getUserPointsBalance(
|
||||
|
||||
const verifiedUser =
|
||||
session.token.loginType === "sas"
|
||||
? await getEuroBonusProfileData(session)
|
||||
? await getEuroBonusProfileData({
|
||||
accessToken: session.token.access_token,
|
||||
loginType: session.token.loginType,
|
||||
})
|
||||
: await getVerifiedUser({ session })
|
||||
|
||||
if (!verifiedUser || "error" in verifiedUser) {
|
||||
@@ -19,8 +22,8 @@ export async function getUserPointsBalance(
|
||||
}
|
||||
|
||||
const points =
|
||||
"points" in verifiedUser.data
|
||||
? verifiedUser.data.points.total
|
||||
"points" in verifiedUser
|
||||
? verifiedUser.points.total
|
||||
: verifiedUser.data.membership?.currentPoints
|
||||
|
||||
return points ?? 0
|
||||
|
||||
@@ -67,6 +67,8 @@
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@sentry/nextjs": "^10",
|
||||
"@tanstack/react-query": "^5.75.5",
|
||||
"@tanstack/react-query-devtools": "^5.75.5",
|
||||
"next": "^15",
|
||||
"react": "^19"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user