Files
web/packages/common/tokenManager/tokenManager.ts
Joakim Jäderberg 9620bfe76d Merged in SW-3476-use-separate-curity-client-for-service-tokens-for-partner-sas (pull request #2809)
feat(SW-3476): add support for using different curity clients when creating service tokens

* fix(SW-3476): add support for using different curity clients when creating service tokens

* remove log statement


Approved-by: Hrishikesh Vaipurkar
2025-09-18 07:04:45 +00:00

124 lines
3.0 KiB
TypeScript

import { trace, type Tracer } from "@opentelemetry/api"
import { getCacheClient } from "../dataCache"
import { env } from "../env/server"
import { createCounter } from "../telemetry"
interface ServiceTokenResponse {
access_token: string
scope?: string
token_type: string
expires_in: number
}
export async function getServiceToken() {
const tracer = trace.getTracer("getServiceToken")
return await tracer.startActiveSpan("getServiceToken", async () => {
const scopes = env.CURITY_CLIENT_SERVICE_SCOPES
const cacheKey = getServiceTokenCacheKey(scopes)
const cacheClient = await getCacheClient()
const token = await getOrSetServiceTokenFromCache(cacheKey, scopes, tracer)
if (token.expiresAt < Date.now()) {
await cacheClient.deleteKey(cacheKey)
const newToken = await getOrSetServiceTokenFromCache(
cacheKey,
scopes,
tracer
)
return newToken.jwt
}
return token.jwt
})
}
async function getOrSetServiceTokenFromCache(
cacheKey: string,
scopes: string[],
tracer: Tracer
) {
const cacheClient = await getCacheClient()
const token = await cacheClient.cacheOrGet(
cacheKey,
async () => {
return await tracer.startActiveSpan("fetch new token", async () => {
const newToken = await getJwt(scopes)
return newToken
})
},
"1h"
)
return token
}
async function getJwt(scopes: string[]) {
const getJwtCounter = createCounter("tokenManager", "getJwt")
const metricsGetJwt = getJwtCounter.init({
scopes,
})
metricsGetJwt.start()
const jwt = await fetchServiceToken(scopes)
const expiresAt = Date.now() + jwt.expires_in * 1000
metricsGetJwt.success()
return { expiresAt, jwt }
}
async function fetchServiceToken(scopes: string[]) {
const fetchServiceTokenCounter = createCounter(
"tokenManager",
"fetchServiceToken"
)
const metricsFetchServiceToken = fetchServiceTokenCounter.init({
scopes,
})
metricsFetchServiceToken.start()
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(" "),
}),
signal: AbortSignal.timeout(15_000),
})
if (!response.ok) {
await metricsFetchServiceToken.httpError(response)
const text = await response.text()
throw new Error(
`[fetchServiceToken] Failed to obtain service token: ${JSON.stringify({
status: response.status,
statusText: response.statusText,
text,
})}`
)
}
const result = response.json() as Promise<ServiceTokenResponse>
metricsFetchServiceToken.success()
return result
}
function getServiceTokenCacheKey(scopes: string[]): string {
return `serviceToken:${scopes.join(",")}`
}