fix: handle cache tags for service tokens

This commit is contained in:
Christel Westerberg
2024-10-01 14:48:28 +02:00
committed by Pontus Dreij
parent e5f85e0499
commit 3c548923ff
4 changed files with 75 additions and 25 deletions

View File

@@ -1,35 +1,58 @@
import { revalidateTag, unstable_cache } from "next/cache"
import { env } from "@/env/server"
import { generateServiceTokenTag } from "@/utils/generateTag"
import { ServiceTokenScope } from "@/types/enums/serviceToken"
import { ServiceTokenResponse } from "@/types/tokens"
const SERVICE_TOKEN_REVALIDATE_SECONDS = 3599 // 59 minutes and 59 seconds.
async function getServiceToken(scopes: ServiceTokenScope[]) {
const response = await fetch(`${env.CURITY_ISSUER_USER}/oauth/v2/token`, {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
Accept: "application/json",
},
body: new URLSearchParams({
grant_type: "client_credentials",
client_id: env.CURITY_CLIENT_ID_SERVICE,
client_secret: env.CURITY_CLIENT_SECRET_SERVICE,
scope: scopes.join(" "),
}),
})
if (!response.ok) {
throw new Error("Failed to obtain service token")
}
return response.json()
}
export async function fetchServiceToken(
scopes: string[]
scopes: ServiceTokenScope[]
): Promise<ServiceTokenResponse> {
try {
const response = await fetch(`${env.CURITY_ISSUER_USER}/oauth/v2/token`, {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
Accept: "application/json",
},
body: new URLSearchParams({
grant_type: "client_credentials",
client_id: env.CURITY_CLIENT_ID_SERVICE,
client_secret: env.CURITY_CLIENT_SECRET_SERVICE,
scope: scopes.join(" "),
}),
next: {
revalidate: SERVICE_TOKEN_REVALIDATE_SECONDS,
},
})
const tag = generateServiceTokenTag(scopes)
const getCachedJwt = unstable_cache(
async (scopes) => {
const jwt = await getServiceToken(scopes)
if (!response.ok) {
throw new Error("Failed to obtain service token")
const expiresAt = Date.now() + jwt.expires_in * 1000
return { expiresAt, jwt }
},
scopes,
{ tags: [tag] }
)
const cachedJwt = await getCachedJwt(scopes)
if (cachedJwt.expiresAt < Date.now()) {
revalidateTag(tag)
const newToken = await getServiceToken(scopes)
return newToken
}
return response.json()
return cachedJwt.jwt
} catch (error) {
console.error("Error fetching service token:", error)
throw error

View File

@@ -17,6 +17,10 @@ import { langInput } from "./utils"
import type { Session } from "next-auth"
import {
ServiceTokenScope,
ServiceTokenScopeEnum,
} from "@/types/enums/serviceToken"
import type { Meta } from "@/types/trpc/meta"
const t = initTRPC
@@ -121,7 +125,7 @@ export const safeProtectedProcedure = t.procedure.use(async function (opts) {
})
})
function createServiceProcedure(serviceName: string) {
function createServiceProcedure(serviceName: ServiceTokenScope) {
return t.procedure.use(async (opts) => {
const { access_token } = await fetchServiceToken([serviceName])
if (!access_token) {
@@ -135,9 +139,15 @@ function createServiceProcedure(serviceName: string) {
})
}
export const bookingServiceProcedure = createServiceProcedure("booking")
export const hotelServiceProcedure = createServiceProcedure("hotel")
export const profileServiceProcedure = createServiceProcedure("profile")
export const bookingServiceProcedure = createServiceProcedure(
ServiceTokenScopeEnum.booking
)
export const hotelServiceProcedure = createServiceProcedure(
ServiceTokenScopeEnum.hotel
)
export const profileServiceProcedure = createServiceProcedure(
ServiceTokenScopeEnum.profile
)
export const serverActionProcedure = t.procedure.experimental_caller(
experimental_nextAppDirCaller({

View File

@@ -0,0 +1,7 @@
export enum ServiceTokenScopeEnum {
profile = "profile",
hotel = "hotel",
booking = "booking",
}
export type ServiceTokenScope = keyof typeof ServiceTokenScopeEnum

View File

@@ -99,3 +99,13 @@ export function generateLoyaltyConfigTag(
) {
return `${lang}:loyalty_config:${contentTypeUid}:${id}`
}
/**
* Function to generate tags for service tokens
*
* @param serviceTokenScope scope of service token
* @returns string
*/
export function generateServiceTokenTag(serviceTokenScopes: string[]) {
return `service_token:${serviceTokenScopes.join("-")}`
}