Skip to content

Commit

Permalink
Code review
Browse files Browse the repository at this point in the history
  • Loading branch information
satazor committed Aug 2, 2024
1 parent e2ce127 commit 54fcc4f
Show file tree
Hide file tree
Showing 5 changed files with 31 additions and 23 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ jobs:
with:
node-version: 22

- name: Install node packages
- name: Install dependencies
run: npm ci

- name: Configure git
Expand Down
20 changes: 12 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ You can pass the following options during the plugin registration:
```js
await fastify.register(import('@fastify/fastify-openapi-router-plugin'), {
spec: './petstore.json',
securityHandler: {
securityHandlers: {
APIAuth: (value, request) => {}
}
});
Expand Down Expand Up @@ -83,7 +83,7 @@ If you haven't defined any [Security Schemes](https://github.com/OAI/OpenAPI-Spe

Security handlers are executed as a `onRequest` hook for every API operation if plugin founds a [Security Requirement Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#security-requirement-object) defined on the root level or operation level of your OpenAPI specification. According to [Fastify Lifecycle](https://fastify.dev/docs/latest/Reference/Lifecycle/), it is the most secure way to implement an authentication layer because it avoids parsing the body for unauthorized accesses.

The functions you define in `securityHandlers` are cached per request. If your operation `security` uses the same security scheme, the plugin will call the security handler only once. Moreover, the plugin will only call the security handler if there's a value extracted from the request.
If your operation `security` entries use the same security scheme, the plugin will call the security handler only once. Moreover, the plugin will only call the security handler if there's a value extracted from the request.

The security handler should either throw an error or return an object with `{ data, scopes }` where `data` becomes available as `request.oas.security.<name>` in your route handler and `scopes` is array of strings that will be used to verify if the scopes defined in the API operation are satisfied.

Expand Down Expand Up @@ -142,7 +142,7 @@ await fastify.register(import('@fastify/fastify-openapi-router-plugin'), {

This method is used to register a new route by translating the given `operationId` to a compliant Fastify route.

`options` must be an `Object` containing at least the `operationId` and `handler(request, reply)`. All the available [routes options](https://fastify.dev/docs/latest/Reference/Routes/#routes-options) can be used except `method`, `url` and `schema` because those are loaded from your OpenAPI specification.
`options` must be an object containing at least the `operationId` and `handler(request, reply)`. All the available [routes options](https://fastify.dev/docs/latest/Reference/Routes/#routes-options) can be used except `method`, `url` and `schema` because those are loaded from your OpenAPI specification.

**Example**

Expand All @@ -165,21 +165,25 @@ This object contains all error classes that can be thrown by the plugin:

#### `request.oas`

For your convenience, the object `request.oas` is populated with data related to the request being made. This is an object containing `{ operation, security }`, where `operation` is the raw API operation that activated the Fastify route and `security` is an object containing the result of all security handlers called for the Fastify route.
For your convenience, the object `request.oas` is populated with data related to the request being made. This is an object containing `{ operation, security, securityReport }`, where:

- `operation` is the raw API operation that activated the Fastify route.
- `security` is an object where keys are security scheme names and values the returned `data` field from security handlers.
- `securityReport`: A detailed report of the security verification process. Check the [Error handler](#error-handler) section for more information.

**Example**

```js
await fastify.register(import('@fastify/fastify-openapi-router-plugin'), {
spec: './petstore.json',
securitySchemes: {
securityHandlers: {
OAuth2: async (request, reply) => {
// Validate and decode token.
const { userId } = verifyToken(token);
const { userId, scopes } = verifyToken(token);

return {
data: { userId },
scopes: tokenData.scopes
scopes,
};
}
}
Expand All @@ -203,7 +207,7 @@ If there was an error associated with `security` processing of a request, the pl
```js
fastify.setErrorHandler((error, request, reply) => {
if (error instanceof fastify.oas.errors.UnauthorizedError) {
// Do something with `error.report`and call `reply` accordingly
// Do something with `error.report`and call `reply` accordingly.
}

// ...
Expand Down
13 changes: 5 additions & 8 deletions examples/petstore/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,12 @@ const fastify = Fastify({
await fastify.register(openApiRouter, {
securityHandlers: {
api_key: async () => {},
petstore_auth: async (request, { verifyScopes }) => {
const { missing, ok } = verifyScopes(['read:pets']);

if (!ok) {
throw new Error('Missing scopes ' + missing.join(', '));
}

petstore_auth: async () => {
return {
user: { name: 'John Doe' }
data: {
user: { name: 'John Doe' }
},
scopes: ['read:pets']
};
}
},
Expand Down
11 changes: 8 additions & 3 deletions src/errors/scopes-mismatch-error.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,14 @@ const ScopesMismatchError = createError('FST_OAS_SCOPES_MISMATCH', 'Scopes do no
const createScopesMismatchError = (providedScopes, requiredScopes, missingScopes) => {
const err = new ScopesMismatchError();

err.providedScopes = providedScopes;
err.requiredScopes = requiredScopes;
err.missingScopes = missingScopes;
Object.defineProperty(err, 'scopes', {
enumerable: false,
value: {
missing: missingScopes,
provided: providedScopes,
required: requiredScopes
}
});

return err;
};
Expand Down
8 changes: 5 additions & 3 deletions src/utils/security.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@ describe('verifyScopes()', () => {
} catch (err) {
expect(err).toBeInstanceOf(errors.ScopesMismatchError);
expect(err).toMatchObject({
missingScopes: missing,
providedScopes: provided,
requiredScopes: required
scopes: {
missing: missing,
provided: provided,
required: required
}
});
}
};
Expand Down

0 comments on commit 54fcc4f

Please sign in to comment.