Files
zkPix/src/oauth.ts
T
2026-09-14 21:19:58 -03:00

54 lines
1.7 KiB
TypeScript

import { AccessToken, ClientCredentials } from 'simple-oauth2';
import type { Config } from './config';
/** Scopes the prover itself needs: it creates participants and charges. */
export const FULL_SCOPES = [
'checkout.solicitacoes-requisicao',
'checkout.participantes-requisicao',
'checkout.solicitacoes-info',
'checkout.participantes-info',
];
/**
* Scope of the token that travels with the attested request. It can only
* read charges, so its exposure to the Primus attestor (and to anyone who
* sees the attested request) cannot cancel charges or rewrite a seller's
* bank account.
*/
export const READ_SCOPES = ['checkout.solicitacoes-info'];
/** Seconds before expiry at which a cached token is renewed. */
const RENEW_WINDOW = 60;
/**
* One cached client-credentials token per scope set. The token endpoint does
* not require mTLS, so this runs outside the attestation.
*/
export class OAuth {
private readonly client: ClientCredentials;
private readonly tokens = new Map<string, AccessToken>();
constructor(config: Config['bb']) {
const url = new URL(config.oauthUrl);
this.client = new ClientCredentials({
client: { id: config.clientId, secret: config.clientSecret },
auth: { tokenHost: url.origin, tokenPath: url.pathname },
});
}
async bearer(scopes: string[]): Promise<string> {
const scope = scopes.join(' ');
let token = this.tokens.get(scope);
if (!token || token.expired(RENEW_WINDOW)) {
token = await this.client.getToken({ scope });
this.tokens.set(scope, token);
}
return `Bearer ${token.token.access_token as string}`;
}
/** Drops a cached token so the next call mints a fresh one. */
forget(scopes: string[]): void {
this.tokens.delete(scopes.join(' '));
}
}