Skip to content

Commit 950ffed

Browse files
authored
feat(backend): Add JWKS BAPI endpoint (#5588)
1 parent 78d22d4 commit 950ffed

7 files changed

Lines changed: 80 additions & 0 deletions

File tree

.changeset/bright-spoons-fix.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
'@clerk/backend': minor
3+
---
4+
5+
Adds the ability to grab an instance's JWKS to the Backend API client.
6+
7+
```ts
8+
import { createClerkClient } from '@clerk/backend';
9+
10+
const clerkClient = createClerkClient(...);
11+
await clerkClient.jwks.getJWKS();
12+
```

packages/backend/src/api/__tests__/factory.test.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { http, HttpResponse } from 'msw';
22
import { describe, expect, it } from 'vitest';
33

4+
import jwksJson from '../../fixtures/jwks.json';
45
import userJson from '../../fixtures/user.json';
56
import { server, validateHeaders } from '../../mock-server';
67
import { createBackendApiClient } from '../factory';
@@ -275,4 +276,30 @@ describe('api.client', () => {
275276
expect(data[0].token).toBe('<token>');
276277
expect(data[0].scopes).toEqual(['email', 'profile']);
277278
});
279+
280+
describe('JWKS', () => {
281+
it('executes a successful backend API request for a single resource and returns the raw response', async () => {
282+
server.use(
283+
http.get(
284+
`https://api.clerk.test/v1/jwks`,
285+
validateHeaders(() => {
286+
return HttpResponse.json(jwksJson);
287+
}),
288+
),
289+
);
290+
291+
const response = await apiClient.jwks.getJwks();
292+
const key = response.keys?.[0];
293+
294+
expect(key).toBeDefined();
295+
expect(key?.kid).toBe('ins_1234');
296+
expect(key?.alg).toBe('RS256');
297+
expect(key?.kty).toBe('RSA');
298+
expect(key?.use).toBe('sig');
299+
expect(key?.e).toBe('BQGF');
300+
expect(key?.n).toBe(
301+
'xV3jihnMy4sr5jJ4S66YTc6FxnFsVy3weiyJFYOAdo515AZMrpMMdraAiVmnXZfolZpv7CcnsnG290cg-XfGRNk-Jil_tJt2SLGtiT9LtWT_iev4zN8veRGzTaOb6C-Qb6T_8xsjP_sp0a92zyNgyc4UxR-acMmOqxjkHmx1q0U1fCom83WI59Yu5VmvLM4MA-1sLkmAE1bTzp4ie-_xu9anwsS3H97MONGtildB4nAG0L-lj7tReNHoYLkciEKCqqUMoK-o6JN29OKozpqiI4dVv0oityWw2ygf6eR5qrKZZjrjbAMt_emXBFGQ5Y1QSsriJoRoykGcdbXaU7S_QV',
302+
);
303+
});
304+
});
278305
});
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import type { JwksJSON } from '../resources/JSON';
2+
import { AbstractAPI } from './AbstractApi';
3+
4+
const basePath = '/jwks';
5+
6+
export class JwksAPI extends AbstractAPI {
7+
public async getJwks() {
8+
return this.request<JwksJSON>({
9+
method: 'GET',
10+
path: basePath,
11+
});
12+
}
13+
}

packages/backend/src/api/endpoints/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export * from './ClientApi';
55
export * from './DomainApi';
66
export * from './EmailAddressApi';
77
export * from './InvitationApi';
8+
export * from './JwksApi';
89
export * from './OrganizationApi';
910
export * from './PhoneNumberApi';
1011
export * from './RedirectUrlApi';

packages/backend/src/api/factory.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
DomainAPI,
66
EmailAddressAPI,
77
InvitationAPI,
8+
JwksAPI,
89
OrganizationAPI,
910
PhoneNumberAPI,
1011
RedirectUrlAPI,
@@ -31,6 +32,7 @@ export function createBackendApiClient(options: CreateBackendApiOptions) {
3132
clients: new ClientAPI(request),
3233
emailAddresses: new EmailAddressAPI(request),
3334
invitations: new InvitationAPI(request),
35+
jwks: new JwksAPI(request),
3436
organizations: new OrganizationAPI(request),
3537
phoneNumbers: new PhoneNumberAPI(request),
3638
redirectUrls: new RedirectUrlAPI(request),

packages/backend/src/api/resources/JSON.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,19 @@ export interface ExternalAccountJSON extends ClerkResourceJSON {
126126
verification: VerificationJSON | null;
127127
}
128128

129+
export interface JwksJSON {
130+
keys?: JwksKeyJSON[];
131+
}
132+
133+
export interface JwksKeyJSON {
134+
use: string;
135+
kty: string;
136+
kid: string;
137+
alg: string;
138+
n: string;
139+
e: string;
140+
}
141+
129142
export interface SamlAccountJSON extends ClerkResourceJSON {
130143
object: typeof ObjectType.SamlAccount;
131144
provider: string;
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"keys": [
3+
{
4+
"use": "sig",
5+
"kty": "RSA",
6+
"kid": "ins_1234",
7+
"alg": "RS256",
8+
"n": "xV3jihnMy4sr5jJ4S66YTc6FxnFsVy3weiyJFYOAdo515AZMrpMMdraAiVmnXZfolZpv7CcnsnG290cg-XfGRNk-Jil_tJt2SLGtiT9LtWT_iev4zN8veRGzTaOb6C-Qb6T_8xsjP_sp0a92zyNgyc4UxR-acMmOqxjkHmx1q0U1fCom83WI59Yu5VmvLM4MA-1sLkmAE1bTzp4ie-_xu9anwsS3H97MONGtildB4nAG0L-lj7tReNHoYLkciEKCqqUMoK-o6JN29OKozpqiI4dVv0oityWw2ygf6eR5qrKZZjrjbAMt_emXBFGQ5Y1QSsriJoRoykGcdbXaU7S_QV",
9+
"e": "BQGF"
10+
}
11+
]
12+
}

0 commit comments

Comments
 (0)