Skip to content

Commit

Permalink
[jwt] fix unauthorized being thrown in onRequest (#3149)
Browse files Browse the repository at this point in the history
* [jwt] fix unauthorized being thrown in `onRequest`

* changeset

* fix tests
  • Loading branch information
EmrysMyrddin authored Dec 22, 2023
1 parent ab81a2e commit b9d2afc
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 25 deletions.
6 changes: 6 additions & 0 deletions .changeset/thirty-seas-attack.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@graphql-yoga/plugin-jwt': patch
---

Fix unauthorized error resulting in an response with 500 status or in a server crash (depending on
actual HTTP server implementation used).
66 changes: 48 additions & 18 deletions packages/plugins/jwt/src/__tests__/jwt.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,45 +21,70 @@ describe('jwt', () => {
it('should throw on unsupported header type', async () => {
const server = createTestServer();

await expect(server.queryWithAuth('Basic 123')).rejects.toMatchObject({
message: 'Unsupported token type provided: "Basic"',
extensions: { http: { status: 401 } },
const response = await server.queryWithAuth('Basic 123');
expect(response.status).toBe(401);
expect(await response.json()).toMatchObject({
errors: [
{
message: 'Unsupported token type provided: "Basic"',
},
],
});
});

it('should throw on invalid token', async () => {
const server = createTestServer();

await expect(server.queryWithAuth('Bearer abcd')).rejects.toMatchObject({
message: 'Failed to decode authentication token',
extensions: { http: { status: 401 } },
const response = await server.queryWithAuth('Bearer abcd');
expect(response.status).toBe(401);
expect(await response.json()).toMatchObject({
errors: [
{
message: 'Failed to decode authentication token. Verification failed.',
},
],
});
});

it('should not accept token without algorithm', async () => {
const server = createTestServer();

await expect(server.queryWithAuth(buildJWTWithoutAlg())).rejects.toMatchObject({
message: 'Failed to decode authentication token',
extensions: { http: { status: 401 } },
const response = await server.queryWithAuth(buildJWTWithoutAlg());
expect(response.status).toBe(401);
expect(await response.json()).toMatchObject({
errors: [
{
message: 'Failed to decode authentication token. Verification failed.',
},
],
});
});

it('should not allow non matching issuer', async () => {
const server = createTestServer();

await expect(server.queryWithAuth(buildJWT({ iss: 'test' }))).rejects.toMatchObject({
message: 'Failed to decode authentication token',
extensions: { http: { status: 401 } },
const response = await server.queryWithAuth(buildJWT({ iss: 'test' }));
expect(response.status).toBe(401);
expect(await response.json()).toMatchObject({
errors: [
{
message: 'Failed to decode authentication token. Verification failed.',
},
],
});
});

it('should not allow non matching audience', async () => {
const server = createTestServer({ audience: 'test' });

await expect(server.queryWithAuth(buildJWT({ aud: 'wrong' }))).rejects.toMatchObject({
message: 'Failed to decode authentication token',
extensions: { http: { status: 401 } },
const response = await server.queryWithAuth(buildJWT({ aud: 'wrong' }));
expect(response.status).toBe(401);
expect(await response.json()).toMatchObject({
errors: [
{
message: 'Failed to decode authentication token. Verification failed.',
},
],
});
});

Expand All @@ -74,9 +99,14 @@ describe('jwt', () => {
it('should not allow unknown key id', async () => {
const server = createTestServer({ signingKey: undefined, jwksUri: 'test' });

await expect(server.queryWithAuth(buildJWT({}, { keyid: 'unknown' }))).rejects.toMatchObject({
message: 'Failed to decode authentication token. Unknown key id.',
extensions: { http: { status: 401 } },
const response = await server.queryWithAuth(buildJWT({}, { keyid: 'unknown' }));
expect(response.status).toBe(401);
expect(await response.json()).toMatchObject({
errors: [
{
message: 'Failed to decode authentication token. Unknown key id.',
},
],
});
});

Expand Down
9 changes: 2 additions & 7 deletions packages/plugins/jwt/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ export function useJWT(options: JwtPluginOptions): Plugin {
}

return {
async onRequest({ request, serverContext, url }) {
async onRequestParse({ request, serverContext, url }) {
const token = await getToken({ request, serverContext, url });
if (token != null) {
const signingKey = options.signingKey ?? (await fetchKey(jwksClient, token));
Expand Down Expand Up @@ -133,12 +133,7 @@ function verify(
{ ...options, algorithms: options?.algorithms ?? ['RS256'] },
(err, result) => {
if (err) {
// Should we expose the error message? Perhaps only in development mode?
reject(
unauthorizedError('Failed to decode authentication token', {
originalError: err,
}),
);
reject(unauthorizedError('Failed to decode authentication token. Verification failed.'));
} else {
resolve(result as JwtPayload);
}
Expand Down

0 comments on commit b9d2afc

Please sign in to comment.