Skip to content

Commit f411395

Browse files
feat: jwt sub
1 parent b59bd6e commit f411395

File tree

2 files changed

+34
-4
lines changed

2 files changed

+34
-4
lines changed

src/wrapper/ReferralExchangeJwtClient.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export declare namespace ReferralExchangeJwtClient {
1010
export interface Options extends Omit<ReferralExchangeClient.Options, "fetcher" | "apiKey"> {
1111
privateKey: string;
1212
apiKeyName: string;
13+
subject?: string;
1314
tokenCache?: TokenCacheOptions;
1415
}
1516

@@ -20,8 +21,8 @@ export declare namespace ReferralExchangeJwtClient {
2021

2122
export class ReferralExchangeJwtClient extends ReferralExchangeClient {
2223
constructor(options: ReferralExchangeJwtClient.Options) {
23-
const { privateKey, apiKeyName, tokenCache, ...baseOptions } = options;
24-
const signer = new JwtSigner({ privateKey, issuer: apiKeyName, tokenCache });
24+
const { privateKey, apiKeyName, subject, tokenCache, ...baseOptions } = options;
25+
const signer = new JwtSigner({ privateKey, issuer: apiKeyName, subject, tokenCache });
2526
const fetcher = createJwtFetcher(signer);
2627
super({
2728
...baseOptions,
@@ -33,19 +34,22 @@ export class ReferralExchangeJwtClient extends ReferralExchangeClient {
3334
interface JwtSignerConfig {
3435
privateKey: string;
3536
issuer: string;
37+
subject?: string;
3638
tokenCache?: ReferralExchangeJwtClient.TokenCacheOptions;
3739
}
3840

3941
class JwtSigner {
4042
private readonly privateKey: string;
4143
private readonly issuer: string;
44+
private readonly subject?: string;
4245
private readonly cacheEnabled: boolean;
4346
private readonly refreshBufferSeconds: number;
4447
private cachedToken: { token: string; expiresAtEpochSeconds: number } | undefined;
4548

46-
constructor({ privateKey, issuer, tokenCache }: JwtSignerConfig) {
49+
constructor({ privateKey, issuer, subject, tokenCache }: JwtSignerConfig) {
4750
this.privateKey = privateKey;
4851
this.issuer = issuer;
52+
this.subject = subject;
4953
const cacheEnabled = tokenCache != null;
5054
this.cacheEnabled = cacheEnabled;
5155
this.refreshBufferSeconds = cacheEnabled ? clampRefreshBufferSeconds(tokenCache.refreshBufferSeconds) : 0;
@@ -73,6 +77,7 @@ class JwtSigner {
7377
return createSignedJwt({
7478
privateKey: this.privateKey,
7579
issuer: this.issuer,
80+
subject: this.subject,
7681
});
7782
}
7883
}
@@ -93,9 +98,10 @@ function createJwtFetcher(signer: JwtSigner): FetchFunction {
9398
interface CreateSignedJwtArgs {
9499
privateKey: string;
95100
issuer: string;
101+
subject?: string;
96102
}
97103

98-
function createSignedJwt({ privateKey, issuer }: CreateSignedJwtArgs): {
104+
function createSignedJwt({ privateKey, issuer, subject }: CreateSignedJwtArgs): {
99105
token: string;
100106
expiresAtEpochSeconds: number;
101107
} {
@@ -109,6 +115,7 @@ function createSignedJwt({ privateKey, issuer }: CreateSignedJwtArgs): {
109115
algorithm: "ES256",
110116
issuer,
111117
expiresIn: JWT_TTL_SECONDS,
118+
subject,
112119
},
113120
);
114121

tests/unit/wrapper/ReferralExchangeJwtClient.test.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,29 @@ describe("ReferralExchangeJwtClient", () => {
5757
);
5858
});
5959

60+
it("includes subject claim when provided", async () => {
61+
signMock.mockReturnValue("token-1");
62+
63+
const client = new ReferralExchangeJwtClient({
64+
privateKey: "fake-private-key",
65+
apiKeyName: "issuer",
66+
subject: "user-123",
67+
});
68+
69+
const fetcher = ((client as any)._options.fetcher) as (args: typeof requestArgs) => Promise<unknown>;
70+
71+
await fetcher(requestArgs);
72+
73+
expect(signMock).toHaveBeenCalledWith(
74+
{},
75+
"fake-private-key",
76+
expect.objectContaining({
77+
issuer: "issuer",
78+
subject: "user-123",
79+
}),
80+
);
81+
});
82+
6083
it("reuses cached tokens until the refresh buffer elapses", async () => {
6184
signMock.mockReturnValueOnce("token-1").mockReturnValueOnce("token-2");
6285

0 commit comments

Comments
 (0)