Skip to content

Commit

Permalink
Add WWW-Authenticate headers to auth middleware responses - fixes #3145
Browse files Browse the repository at this point in the history
  • Loading branch information
benfrancis committed Aug 2, 2024
1 parent 6b5fb8f commit 1a2fcb0
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 5 deletions.
15 changes: 13 additions & 2 deletions src/jwt-middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ export function middleware(): express.Handler {
authenticate(req)
.then((jwt) => {
if (!jwt) {
// Send 401 with WWW-Authenticate header as per RFC 6750
res.set('WWW-Authenticate', 'Bearer');
res.status(401).end();
return;
}
Expand All @@ -111,12 +113,21 @@ export function middleware(): express.Handler {
scope = `${Constants.OAUTH_PATH}:${Constants.READWRITE}`;
}
if (!scopeAllowsRequest(scope, req)) {
res.status(401).send(`Token of role ${payload.role} used out of scope: ${scope}`);
// insufficient_scope error code as per RFC 6750
const wwwAuthenticate = `Bearer error="insufficient_scope", \
error_description="Token of role ${payload.role} used out of \
scope: ${scope}"`;
res.set('WWW-Authenticate', wwwAuthenticate);
res.status(403).end();
return;
}
if (payload.role !== Constants.USER_TOKEN) {
if (!payload.scope) {
res.status(400).send('Token must contain scope');
// invalid_request error code as per RFC 6750
const wwwAuthenticate = `Bearer error="invalid_request", \
error_description="Token must contain scope"`;
res.set('WWW-Authenticate', wwwAuthenticate);
res.status(400).end();
return;
}
}
Expand Down
25 changes: 22 additions & 3 deletions src/test/integration/oauth-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,19 @@ describe('oauth/', function () {
customCallbackHandler = customCallbackHandlerProvided || null;
}

it('rejects request with no JWT', async () => {
setupOAuth();

// Try using the access token
const res = await chai
.request(server)
.get(Constants.THINGS_PATH)
.set('Accept', 'application/json');
expect(res.status).toEqual(401);
expect(res.header).toHaveProperty('www-authenticate');
expect(res.get('WWW-Authenticate')).toEqual('Bearer');
});

it('performs simple authorization', async () => {
setupOAuth();

Expand Down Expand Up @@ -203,7 +216,9 @@ describe('oauth/', function () {
.get(Constants.OAUTHCLIENTS_PATH)
.set('Accept', 'application/json')
.set(...headerAuth(jwt));
expect(err.status).toEqual(401);
expect(err.status).toEqual(403);
expect(err.header).toHaveProperty('www-authenticate');
expect(err.get('www-authenticate')).toEqual(expect.stringContaining('insufficient_scope'));

res = await chai
.request(server)
Expand Down Expand Up @@ -488,7 +503,9 @@ describe('oauth/', function () {
.delete(`${Constants.THINGS_PATH}/${TEST_THING.id}`)
.set('Accept', 'application/json')
.set(...headerAuth(jwt));
expect(err.status).toEqual(401);
expect(err.status).toEqual(403);
expect(err.header).toHaveProperty('www-authenticate');
expect(err.get('www-authenticate')).toEqual(expect.stringContaining('insufficient_scope'));
});

it('rejects use of authorization code as access token', async () => {
Expand Down Expand Up @@ -521,6 +538,8 @@ describe('oauth/', function () {
.get(Constants.THINGS_PATH)
.set('Accept', 'application/json')
.set(...headerAuth(jwt));
expect(err.status).toEqual(401);
expect(err.status).toEqual(403);
expect(err.header).toHaveProperty('www-authenticate');
expect(err.get('www-authenticate')).toEqual(expect.stringContaining('insufficient_scope'));
});
});

0 comments on commit 1a2fcb0

Please sign in to comment.