109 lines
2.8 KiB
TypeScript
109 lines
2.8 KiB
TypeScript
import { metrics } from "@opentelemetry/api"
|
|
import { revalidateTag, unstable_cache } from "next/cache"
|
|
|
|
import { env } from "@/env/server"
|
|
|
|
import { generateServiceTokenTag } from "@/utils/generateTag"
|
|
|
|
import { ServiceTokenResponse } from "@/types/tokens"
|
|
|
|
// OpenTelemetry metrics: Service token
|
|
const meter = metrics.getMeter("trpc.context.serviceToken")
|
|
const fetchServiceTokenCounter = meter.createCounter(
|
|
"trpc.context.serviceToken.fetch-new-token"
|
|
)
|
|
const fetchTempServiceTokenCounter = meter.createCounter(
|
|
"trpc.context.serviceToken.fetch-temporary"
|
|
)
|
|
const fetchServiceTokenFailCounter = meter.createCounter(
|
|
"trpc.context.serviceToken.fetch-fail"
|
|
)
|
|
|
|
async function fetchServiceToken(scopes: string[]) {
|
|
fetchServiceTokenCounter.add(1)
|
|
|
|
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) {
|
|
const text = await response.text()
|
|
const error = {
|
|
status: response.status,
|
|
statusText: response.statusText,
|
|
text,
|
|
}
|
|
|
|
fetchServiceTokenFailCounter.add(1, {
|
|
error_type: "http_error",
|
|
error: JSON.stringify(error),
|
|
})
|
|
|
|
console.error(
|
|
"fetchServiceToken error",
|
|
JSON.stringify({
|
|
query: {
|
|
grant_type: "client_credentials",
|
|
client_id: env.CURITY_CLIENT_ID_SERVICE,
|
|
scope: scopes.join(" "),
|
|
},
|
|
error,
|
|
})
|
|
)
|
|
|
|
throw new Error(
|
|
`[fetchServiceToken] Failed to obtain service token: ${JSON.stringify(error)}`
|
|
)
|
|
}
|
|
|
|
return response.json() as Promise<ServiceTokenResponse>
|
|
}
|
|
|
|
export async function getServiceToken() {
|
|
let scopes: string[] = []
|
|
if (env.HIDE_FOR_NEXT_RELEASE) {
|
|
scopes = ["profile"]
|
|
} else {
|
|
scopes = ["profile", "hotel", "booking"]
|
|
}
|
|
const tag = generateServiceTokenTag(scopes)
|
|
const getCachedJwt = unstable_cache(
|
|
async (scopes) => {
|
|
const jwt = await fetchServiceToken(scopes)
|
|
|
|
const expiresAt = Date.now() + jwt.expires_in * 1000
|
|
return { expiresAt, jwt }
|
|
},
|
|
[tag],
|
|
{ tags: [tag] }
|
|
)
|
|
|
|
const cachedJwt = await getCachedJwt(scopes)
|
|
if (cachedJwt.expiresAt < Date.now()) {
|
|
console.log(
|
|
"trpc.context.serviceToken: Service token expired, revalidating tag"
|
|
)
|
|
|
|
revalidateTag(tag)
|
|
|
|
console.log(
|
|
"trpc.context.serviceToken: Fetching new temporary service token."
|
|
)
|
|
fetchTempServiceTokenCounter.add(1)
|
|
const newToken = await fetchServiceToken(scopes)
|
|
return newToken
|
|
}
|
|
|
|
return cachedJwt.jwt
|
|
}
|