@@ -153,8 +153,20 @@ const vantaTokenExchanges = new Map<string, Promise<string>>()
153153/** Evict cached tokens well before their one-hour expiry. */
154154const VANTA_TOKEN_EXPIRY_BUFFER_MS = 10 * 60 * 1000
155155
156- function vantaTokenCacheKey ( params : VantaTokenParams ) : string {
157- return [ params . region ?? 'us' , params . scope , params . clientId , params . clientSecret ] . join ( '|' )
156+ /**
157+ * Derives the cache key for a credential set. The client secret is included
158+ * only as a SHA-256 digest so plaintext secrets never persist in the
159+ * long-lived cache maps.
160+ */
161+ async function vantaTokenCacheKey ( params : VantaTokenParams ) : Promise < string > {
162+ const digest = await crypto . subtle . digest (
163+ 'SHA-256' ,
164+ new TextEncoder ( ) . encode ( `${ params . clientId } :${ params . clientSecret } ` )
165+ )
166+ const secretHash = Array . from ( new Uint8Array ( digest ) )
167+ . map ( ( byte ) => byte . toString ( 16 ) . padStart ( 2 , '0' ) )
168+ . join ( '' )
169+ return [ params . region ?? 'us' , params . scope , params . clientId , secretHash ] . join ( '|' )
158170}
159171
160172async function exchangeVantaToken ( params : VantaTokenParams , cacheKey : string ) : Promise < string > {
@@ -205,7 +217,7 @@ export async function getVantaAccessToken(
205217 params : VantaTokenParams ,
206218 options ?: { forceRefresh ?: boolean }
207219) : Promise < string > {
208- const cacheKey = vantaTokenCacheKey ( params )
220+ const cacheKey = await vantaTokenCacheKey ( params )
209221 if ( ! options ?. forceRefresh ) {
210222 const cached = vantaTokenCache . get ( cacheKey )
211223 if ( cached && cached . expiresAt > Date . now ( ) ) {
0 commit comments