Skip to content

Commit

Permalink
Merge pull request #358 from seriousme/securityHandlers-status-code
Browse files Browse the repository at this point in the history
feat: allow custom status code in security handlers
  • Loading branch information
seriousme authored Jun 18, 2022
2 parents bb369ce + 2cf8f1c commit 3cbe28b
Show file tree
Hide file tree
Showing 5 changed files with 46 additions and 3 deletions.
6 changes: 5 additions & 1 deletion docs/securityHandlers.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,17 @@ If you provide a securityHandler called `petstore_auth` then it will be called a

If you want authentication to succeed you can simply return. If you want authentication to fail you can just throw an error.

If your error contains a `statusCode` property then the status code of the last failing handler will be passed to fastify. The default status code that is returned upon validation failure is `401`.

Any errors that result from `securityHandlers` are available to registered error handlers. E.g.:
```javascript
fastify.setErrorHandler((err, req, reply) => {
reply.code(err.statusCode).send(err);
});
```
will return errors originating from the securityHandlers as well.
will return errors originating from the securityHandlers as well in `err.errors`.
**Please make sure this does not expose sensitive information to the requestor!**

You can use `err.errors` also to trigger other behaviour in a registered error handler.

For a more elaborate example see the [examples/generatedProject](/examples/generatedProject) folder.
2 changes: 1 addition & 1 deletion examples/generatedProject/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
},
"dependencies": {
"fastify-plugin": "^3.0.1",
"fastify-openapi-glue": "^3.0.1"
"fastify-openapi-glue": "^3.1.0"
},
"devDependencies": {
"fastify": "^4.0.3",
Expand Down
6 changes: 5 additions & 1 deletion lib/securityHandlers.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,17 @@ export default class Security {
return async (req, reply) => {
const handlerErrors = [];
const schemeList = [];
let statusCode = 401;
for (const scheme of schemes) {
try {
await securityHandlers[scheme.name](req, reply, scheme.parameters);
return; // If one security check passes, no need to try any others
} catch (err) {
req.log.debug(`Security handler '${scheme.name}' failed: '${err}'`);
handlerErrors.push(err);
if (err.statusCode !== undefined){
statusCode = err.statusCode;
}
}
schemeList.push(scheme.name);
}
Expand All @@ -66,7 +70,7 @@ export default class Security {
", "
)}) successfully authenticated this request.`
);
err.statusCode = 401;
err.statusCode = statusCode;
err.name = "Unauthorized";
err.errors = handlerErrors;
throw err;
Expand Down
6 changes: 6 additions & 0 deletions test/security.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ class Security {
throw 'API key was invalid or not found';
}

async failingAuthCheckCustomStatusCode(req, reply, params) {
const err = new Error('API key was invalid or not found');
err.statusCode = 451;
throw err;
}

}

export default new Security();
29 changes: 29 additions & 0 deletions test/test-securityHandlers.v3.js
Original file line number Diff line number Diff line change
Expand Up @@ -234,3 +234,32 @@ test("security preHandler gets parameters passed", t => {
}
);
});

test("security preHandler throws error with custom StatusCode", t => {
const opts = {
specification: testSpec,
service,
securityHandlers: {
api_key: securityHandlers.failingAuthCheckCustomStatusCode
}
};

t.plan(4);
const fastify = Fastify();
fastify.setErrorHandler((err, req, reply) => {
t.equal(err.errors.length, 3);
reply.code(err.statusCode).send(err);
});
fastify.register(fastifyOpenapiGlue, opts);
fastify.inject(
{
method: "GET",
url: "/operationSecurity",
},
(err, res) => {
t.error(err);
t.equal(res.statusCode, 451);
t.equal(res.statusMessage, 'Unavailable For Legal Reasons');
}
);
});

0 comments on commit 3cbe28b

Please sign in to comment.