{"id":14969156,"url":"https://github.com/fastify/fastify-oauth2","last_synced_at":"2025-05-14T18:06:04.317Z","repository":{"id":32570069,"uuid":"136843573","full_name":"fastify/fastify-oauth2","owner":"fastify","description":"Enable to perform login using oauth2 protocol","archived":false,"fork":false,"pushed_at":"2025-05-01T21:17:46.000Z","size":323,"stargazers_count":276,"open_issues_count":15,"forks_count":76,"subscribers_count":15,"default_branch":"main","last_synced_at":"2025-05-01T22:25:00.466Z","etag":null,"topics":["fastify","fastify-plugin"],"latest_commit_sha":null,"homepage":"https://npmjs.com/package/@fastify/oauth2","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fastify.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":"fastify","open_collective":"fastify"}},"created_at":"2018-06-10T20:44:43.000Z","updated_at":"2025-05-01T21:16:13.000Z","dependencies_parsed_at":"2024-01-23T20:11:58.365Z","dependency_job_id":"c3249c59-c43c-4956-a842-38932e588382","html_url":"https://github.com/fastify/fastify-oauth2","commit_stats":{"total_commits":246,"total_committers":58,"mean_commits":4.241379310344827,"dds":0.7886178861788617,"last_synced_commit":"fa3abdaf5979558f93a4c8a9b896930635414c21"},"previous_names":[],"tags_count":55,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fastify%2Ffastify-oauth2","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fastify%2Ffastify-oauth2/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fastify%2Ffastify-oauth2/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fastify%2Ffastify-oauth2/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fastify","download_url":"https://codeload.github.com/fastify/fastify-oauth2/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254198514,"owners_count":22030965,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["fastify","fastify-plugin"],"created_at":"2024-09-24T13:41:14.601Z","updated_at":"2025-05-14T18:05:59.308Z","avatar_url":"https://github.com/fastify.png","language":"JavaScript","readme":"# @fastify/oauth2\n\n[![CI](https://github.com/fastify/fastify-oauth2/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/fastify/fastify-oauth2/actions/workflows/ci.yml)\n[![NPM version](https://img.shields.io/npm/v/@fastify/oauth2.svg?style=flat)](https://www.npmjs.com/package/@fastify/oauth2)\n[![neostandard javascript style](https://img.shields.io/badge/code_style-neostandard-brightgreen?style=flat)](https://github.com/neostandard/neostandard)\n\nWrapper around the [`simple-oauth2`](https://github.com/lelylan/simple-oauth2) library.\n\nv4.x of this module support Fastify v3.x\n[v3.x](https://github.com/fastify/fastify-oauth2/tree/3.x) of this module support Fastify v2.x\n\n## Install\n\n```\nnpm i @fastify/oauth2\n```\n\n## Usage\n\nTwo separate endpoints need to be created when using the fastify-oauth2 module, one for the callback from the OAuth2 service provider (such as Facebook or Discord) and another for initializing the OAuth2 login flow.\n\n```js\nconst fastify = require('fastify')({ logger: { level: 'trace' } })\nconst oauthPlugin = require('@fastify/oauth2')\n\nfastify.register(oauthPlugin, {\n  name: 'facebookOAuth2',\n  credentials: {\n    client: {\n      id: '\u003cCLIENT_ID\u003e',\n      secret: '\u003cCLIENT_SECRET\u003e'\n    },\n    auth: oauthPlugin.FACEBOOK_CONFIGURATION\n  },\n  // register a fastify url to start the redirect flow to the service provider's OAuth2 login\n  startRedirectPath: '/login/facebook',\n  // service provider redirects here after user login\n  callbackUri: 'http://localhost:3000/login/facebook/callback'\n  // You can also define callbackUri as a function that takes a FastifyRequest and returns a string\n  // callbackUri: req =\u003e `${req.protocol}://${req.hostname}/login/facebook/callback`,\n})\n\n// This is the new endpoint that initializes the OAuth2 login flow\n// This endpoint is only required if startRedirectPath has not been provided\nfastify.get('/login/facebook', {}, (req, reply) =\u003e {\n  fastify.facebookOAuth2.generateAuthorizationUri(\n    req,\n    reply,\n    (err, authorizationEndpoint) =\u003e {\n     if (err) console.error(err)\n     reply.redirect(authorizationEndpoint)\n    }\n  );\n});\n\n// The service provider redirect the user here after successful login\nfastify.get('/login/facebook/callback', async function (request, reply) {\n  const { token } = await this.facebookOAuth2.getAccessTokenFromAuthorizationCodeFlow(request)\n\n  console.log(token.access_token)\n\n  // if later need to refresh the token this can be used\n  // const { token: newToken } = await this.getNewAccessTokenUsingRefreshToken(token)\n\n  reply.send({ access_token: token.access_token })\n})\n```\n\nIn short, it is necessary to initially navigate to the `/login/facebook` endpoint manually in a web browser. This will redirect to the OAuth2 service provider's login screen. From there, the service provider will automatically redirect back to the `/login/facebook/callback` endpoint where the access token can be retrieved and used. The `CLIENT_ID` and `CLIENT_SECRET` need to be replaced with the ones provided by the service provider.\n\nA complete example is provided at [fastify-discord-oauth2-example](https://github.com/fastify/fastify-oauth2/blob/main/examples/discord.js)\n\n### Usage with `@fastify/cookie`\n\nSince v7.2.0, `@fastify/oauth2` requires the use of cookies to securely implement the OAuth2 exchange. Therefore, if you need `@fastify/cookie` yourself,\nyou will need to register it _before_ `@fastify/oauth2`.\n\n```js\nconst fastify = require('fastify')({ logger: { level: 'trace' } })\nconst oauthPlugin = require('@fastify/oauth2')\n\nfastify.register(require('@fastify/cookie'), cookieOptions)\nfastify.register(oauthPlugin, oauthOptions)\n```\n\nCookies are by default `httpOnly`, `sameSite: Lax`. If this does not suit your use case, it is possible to override the default cookie settings by providing options in the configuration object, for example\n\n```js\nfastify.register(oauthPlugin, {\n  ...,\n  cookie: {\n    secure: true,\n    sameSite: 'none'\n  }\n})\n```\n\nAdditionally, you can customize the names of the cookies by setting the `redirectStateCookieName` and `verifierCookieName` options.\nThe default values for these cookies are `oauth2-code-verifier` for `verifierCookieName` and `oauth2-redirect-state` for `redirectStateCookieName`.\n\n```js\nfastify.register(oauthPlugin, {\n  ...,\n  redirectStateCookieName: 'custom-redirect-state',\n  verifierCookieName: 'custom-code-verifier'\n})\n```\n\n### Preset configurations\n\nYou can choose some default setup to assign to `auth` option.\n\n- `APPLE_CONFIGURATION`\n- `FACEBOOK_CONFIGURATION`\n- `GITHUB_CONFIGURATION`\n- `GITLAB_CONFIGURATION`\n- `LINKEDIN_CONFIGURATION`\n- `GOOGLE_CONFIGURATION`\n- `MICROSOFT_CONFIGURATION`\n- `VKONTAKTE_CONFIGURATION`\n- `SPOTIFY_CONFIGURATION`\n- `DISCORD_CONFIGURATION`\n- `TWITCH_CONFIGURATION`\n- `VATSIM_CONFIGURATION`\n- `VATSIM_DEV_CONFIGURATION`\n- `EPIC_GAMES_CONFIGURATION`\n- `YANDEX_CONFIGURATION`\n\n### Custom configuration\n\nOf course, you can set the OAUTH endpoints by yourself if a preset is not in our module:\n\n```js\nfastify.register(oauthPlugin, {\n  name: 'customOauth2',\n  credentials: {\n    client: {\n      id: '\u003cCLIENT_ID\u003e',\n      secret: '\u003cCLIENT_SECRET\u003e'\n    },\n    auth: {\n      authorizeHost: 'https://my-site.com',\n      authorizePath: '/authorize',\n      tokenHost: 'https://token.my-site.com',\n      tokenPath: '/api/token'\n    }\n  },\n  startRedirectPath: '/login',\n  callbackUri: 'http://localhost:3000/login/callback',\n  callbackUriParams: {\n    exampleParam: 'example param value'\n  }\n})\n```\n\n## Use automated discovery endpoint\n\nWhen your provider supports OpenID connect discovery and you want to configure authorization, token and revocation endpoints automatically,\nthen you can use discovery option.\n`discovery` is a simple object that requires `issuer` property.\n\nIssuer is expected to be string URL or metadata url.\nVariants with or without trailing slash are supported.\n\nYou can see more in [example here](./examples/discovery.js).\n\n```js\nfastify.register(oauthPlugin, {\n  name: 'customOAuth2',\n  scope: ['profile', 'email'],\n  credentials: {\n    client: {\n      id: '\u003cCLIENT_ID\u003e',\n      secret: '\u003cCLIENT_SECRET\u003e',\n    },\n    // Note how \"auth\" is not needed anymore when discovery is used.\n  },\n  startRedirectPath: '/login',\n  callbackUri: 'http://localhost:3000/callback',\n  discovery: { issuer: 'https://identity.mycustomdomain.com' }\n  // pkce: 'S256', you can still do this explicitly, but since discovery is used,\n  // it's BEST to let plugin do it itself\n  // based on what Authorization Server Metadata response\n});\n```\n\nImportant notes for discovery:\n\n- You should not set up `credentials.auth` anymore when discovery mechanics is used.\n- When your provider supports it, plugin will also select appropriate PKCE method in authorization code grant\n- In case you still want to select method yourself, and know exactly what you are doing; you can still do it explicitly.\n\n### Schema configuration\n\nYou can specify your own schema for the `startRedirectPath` end-point. It allows you to create a well-documented document when using `@fastify/swagger` together.\nNote: `schema` option will override the `tags` option without merging them.\n\n```js\nfastify.register(oauthPlugin, {\n  name: 'facebookOAuth2',\n  credentials: {\n    client: {\n      id: '\u003cCLIENT_ID\u003e',\n      secret: '\u003cCLIENT_SECRET\u003e'\n    },\n    auth: oauthPlugin.FACEBOOK_CONFIGURATION\n  },\n  // register a fastify url to start the redirect flow\n  startRedirectPath: '/login/facebook',\n  // facebook redirect here after the user login\n  callbackUri: 'http://localhost:3000/login/facebook/callback',\n  // add tags for the schema\n  tags: ['facebook', 'oauth2'],\n  // add schema\n  schema: {\n    tags: ['facebook', 'oauth2'] // this will take the precedence\n  }\n})\n```\n\n## Set custom state\n\nThe `generateStateFunction` accepts a function to generate the `state` parameter for the OAUTH flow. This function receives the Fastify instance's `request` object as a parameter.\nThe `state` parameter will be also set into a `httpOnly`, `sameSite: Lax` cookie.\nWhen you set it, it is required to provide the function `checkStateFunction` in order to validate the states generated.\n\n```js\n  fastify.register(oauthPlugin, {\n    name: 'facebookOAuth2',\n    credentials: {\n      client: {\n        id: '\u003cCLIENT_ID\u003e',\n        secret: '\u003cCLIENT_SECRET\u003e'\n      },\n      auth: oauthPlugin.FACEBOOK_CONFIGURATION\n    },\n    // register a fastify url to start the redirect flow\n    startRedirectPath: '/login/facebook',\n    // facebook redirect here after the user login\n    callbackUri: 'http://localhost:3000/login/facebook/callback',\n    // custom function to generate the state\n    generateStateFunction: (request) =\u003e {\n      const state = request.query.customCode\n      request.session.state = state\n      return state\n    },\n    // custom function to check the state is valid\n    checkStateFunction: (request, callback) =\u003e {\n      if (request.query.state === request.session.state) {\n        callback()\n        return\n      }\n      callback(new Error('Invalid state'))\n    }\n  })\n```\n\nAsync functions are supported here, and the fastify instance can be accessed via `this`.\n\n```js\n  fastify.register(oauthPlugin, {\n    name: 'facebookOAuth2',\n    credentials: {\n      client: {\n        id: '\u003cCLIENT_ID\u003e',\n        secret: '\u003cCLIENT_SECRET\u003e'\n      },\n      auth: oauthPlugin.FACEBOOK_CONFIGURATION\n    },\n    // register a fastify url to start the redirect flow\n    startRedirectPath: '/login/facebook',\n    // facebook redirect here after the user login\n    callbackUri: 'http://localhost:3000/login/facebook/callback',\n    // custom function to generate the state and store it into the redis\n    generateStateFunction: async function (request) {\n      const state = request.query.customCode\n      await this.redis.set(stateKey, state)\n      return state\n    },\n    // custom function to check the state is valid\n    checkStateFunction: async function (request, callback) {\n      if (request.query.state !== request.session.state) {\n        throw new Error('Invalid state')\n      }\n      return true\n    }\n  })\n```\n\n## Set custom callbackUri Parameters\n\nThe `callbackUriParams` accepts an object that will be translated to query parameters for the callback OAUTH flow. The default value is {}.\n\n```js\nfastify.register(oauthPlugin, {\n  name: 'googleOAuth2',\n  scope: ['profile', 'email'],\n  credentials: {\n    client: {\n      id: '\u003cCLIENT_ID\u003e',\n      secret: '\u003cCLIENT_SECRET\u003e',\n    },\n    auth: oauthPlugin.GOOGLE_CONFIGURATION,\n  },\n  startRedirectPath: '/login/google',\n  callbackUri: 'http://localhost:3000/login/google/callback',\n  callbackUriParams: {\n    // custom query param that will be passed to callbackUri\n    access_type: 'offline', // will tell Google to send a refreshToken too\n  },\n  pkce: 'S256'\n  // check if your provider supports PKCE,\n  // in case they do,\n  // use of this parameter is highly encouraged\n  // in order to prevent authorization code interception attacks\n});\n```\n\n## Set custom tokenRequest body Parameters\n\nThe `tokenRequestParams` parameter accepts an object that will be translated to additional parameters in the POST body\nwhen requesting access tokens via the service’s token endpoint.\n\n## Examples\n\nSee the [`example/`](./examples/) folder for more examples.\n\n## Reference\n\nThis Fastify plugin decorates the fastify instance with the [`simple-oauth2`](https://github.com/lelylan/simple-oauth2)\ninstance inside a **namespace** specified by the property `name` both with and without an `oauth2` prefix.\n\nE.g. For `name: 'customOauth2'`, the `simple-oauth2` instance will become accessible like this:\n\n`fastify.oauth2CustomOauth2.oauth2` and `fastify.customOauth2.oauth2`\n\nIn this manner, we can register multiple OAuth providers and each OAuth providers `simple-oauth2` instance will live in its own **namespace**.\n\nE.g.\n\n- `fastify.oauth2Facebook.oauth2`\n- `fastify.oauth2Github.oauth2`\n- `fastify.oauth2Spotify.oauth2`\n- `fastify.oauth2Vkontakte.oauth2`\n\nAssuming we have registered multiple OAuth providers like this:\n\n- `fastify.register(oauthPlugin, { name: 'facebook', { ... } // facebooks credentials, startRedirectPath, callbackUri etc )`\n- `fastify.register(oauthPlugin, { name: 'github', { ... } // githubs credentials, startRedirectPath, callbackUri etc )`\n- `fastify.register(oauthPlugin, { name: 'spotify', { ... } // spotifys credentials, startRedirectPath, callbackUri etc )`\n- `fastify.register(oauthPlugin, { name: 'vkontakte', { ... } // vkontaktes credentials, startRedirectPath, callbackUri etc )`\n\n## Utilities\n\nThis fastify plugin adds 6 utility decorators to your fastify instance using the same **namespace**:\n\n- `getAccessTokenFromAuthorizationCodeFlow(request, callback)`: A function that uses the Authorization code flow to fetch an OAuth2 token using the data in the last request of the flow. If the callback is not passed it will return a promise. The callback call or promise resolution returns an [AccessToken](https://github.com/lelylan/simple-oauth2/blob/master/API.md#accesstoken) object, which has an `AccessToken.token` property with the following keys:\n  - `access_token`\n  - `refresh_token` (optional, only if the `offline scope` was originally requested, as seen in the callbackUriParams example)\n  - `token_type` (generally `'Bearer'`)\n  - `expires_in` (number of seconds for the token to expire, e.g. `240000`)\n\n- OR `getAccessTokenFromAuthorizationCodeFlow(request, reply, callback)` variant with 3 arguments, which should be used when PKCE extension is used.\n  This allows fastify-oauth2 to delete PKCE code_verifier cookie so it doesn't stay in browser in case server has issue when fetching token. See [Google With PKCE example for more](./examples/google-with-pkce.js).\n\n  *Important to note*: if your provider supports `S256` as code_challenge_method, always prefer that.\n  Only use `plain` when your provider doesn't support `S256`.\n\n\n- `getNewAccessTokenUsingRefreshToken(Token, params, callback)`: A function that takes a `AccessToken`-Object as `Token` and retrieves a new `AccessToken`-Object. This is generally useful with background processing workers to re-issue a new AccessToken when the previous AccessToken has expired. The `params` argument is optional and it is an object that can be used to pass in additional parameters to the refresh request (e.g. a stricter set of scopes). If the callback is not passed this function will return a Promise. The object resulting from the callback call or the resolved Promise is a new `AccessToken` object (see above). Example of how you would use it for `name:googleOAuth2`:\n```js\nfastify.googleOAuth2.getNewAccessTokenUsingRefreshToken(currentAccessToken, (err, newAccessToken) =\u003e {\n   // Handle the new accessToken\n});\n```\n\n- `generateAuthorizationUri(requestObject, replyObject, callback)`: A function that generates the authorization uri. If the callback is not passed this function will return a Promise. The string resulting from the callback call or the resolved Promise is the authorization uri. This is generally useful when you want to handle the redirect yourself in a specific route. The `requestObject` argument passes the request object to the `generateStateFunction`). You **do not** need to declare a `startRedirectPath` if you use this approach. Example of how you would use it:\n\n```js\nfastify.get('/external', { /* Hooks can be used here */ }, (req, reply) =\u003e {\n  fastify.oauth2CustomOAuth2.generateAuthorizationUri(req, reply, (err, authorizationEndpoint) =\u003e {\n    reply.redirect(authorizationEndpoint)\n  });\n});\n```\n\n- `revokeToken(Token, tokenType, params, callback)`: A function to revoke the current access_token or refresh_token on the authorization server. If the callback is not passed it will return a promise. The callback call or promise resolution returns `void`\n```js\nfastify.googleOAuth2.revokeToken(currentAccessToken, 'access_token', undefined, (err) =\u003e {\n   // Handle the reply here\n});\n```\n- `revokeAllToken(Token, params, callback)`: A function to revoke the current access_token and refresh_token on the authorization server. If the callback is not passed it will return a promise. The callback call or promise resolution returns `void`\n```js\nfastify.googleOAuth2.revokeAllToken(currentAccessToken, undefined, (err) =\u003e {\n   // Handle the reply here\n});\n```\n\n- `userinfo(tokenOrTokenSet)`: A function to retrieve userinfo data from Authorization Provider. Both token (as object) or `access_token` string value can be passed.\n\nImportant note:\nUserinfo will only work when `discovery` option is used and such endpoint is advertised by identity provider.\n\nFor a statically configured plugin, you need to make a HTTP call yourself.\n\nSee more on OIDC standard definition for [Userinfo endpoint](https://openid.net/specs/openid-connect-core-1_0.html#UserInfo)\n\nSee more on `userinfo_endpoint` property in [OIDC Discovery Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata) standard definition.\n\n```js\nfastify.googleOAuth2.userinfo(currentAccessToken, (err, userinfo) =\u003e {\n   // do something with userinfo\n});\n// with custom params\nfastify.googleOAuth2.userinfo(currentAccessToken, { method: 'GET', params: { /* add your custom key value pairs here to be appended to request */ } },  (err, userinfo) =\u003e {\n   // do something with userinfo\n});\n\n// or promise version\nconst userinfo = await fastify.googleOAuth2.userinfo(currentAccessToken);\n// use custom params\nconst userinfo = await fastify.googleOAuth2.userinfo(currentAccessToken, { method: 'GET', params: { /* ... */ } });\n```\n\nThere are variants with callback and promises.\nCustom parameters can be passed as option.\nSee [Types](./types/index.d.ts) and usage patterns [in examples](./examples/userinfo.js).\n\nNote:\n\nWe support HTTP `GET` and `POST` requests to userinfo endpoint sending access token using `Bearer` schema in headers.\nYou can do this by setting (`via: \"header\"` parameter), but it's not mandatory since it's a default value.\n\nWe also support `POST` by sending `access_token` in a request body. You can do this by explicitly providing `via: \"body\"` parameter.\n\nE.g. For `name: 'customOauth2'`, the helpers `getAccessTokenFromAuthorizationCodeFlow` and `getNewAccessTokenUsingRefreshToken` will become accessible like this:\n\n- `fastify.oauth2CustomOauth2.getAccessTokenFromAuthorizationCodeFlow`\n- `fastify.oauth2CustomOauth2.getNewAccessTokenUsingRefreshToken`\n\n## Usage with TypeScript\n\nType definitions are provided with the package. Decorations are applied during runtime and are based on auth configuration name. One solution is to leverage TypeScript declaration merging to add type-safe namespace. Make sure you have `@types/node` installed for this to work correctly.\n\nIn project declarations files .d.ts\n\n```ts\nimport { OAuth2Namespace } from '@fastify/oauth2';\n\ndeclare module 'fastify' {\n  interface FastifyInstance {\n    facebookOAuth2: OAuth2Namespace;\n    myCustomOAuth2: OAuth2Namespace;\n  }\n}\n```\n\nAll auth configurations are made available with an `oauth2` prefix that's typed to `OAuth2Namespace | undefined`, such as eg. `fastify.oauth2CustomOauth2` for `customOauth2`.\n\n## Provider Quirks\n\nThe following providers require additional work to be set up correctly.\n\n### Twitch\n\nTwitch requires that the request for a token in the oauth2 flow contains the `client_id` and `client_secret` properties in `tokenRequestParams`:\n\n```js\nfastify.register(oauthPlugin, {\n  name: 'twitchOauth2',\n  credentials: {\n    client: {\n      id: '\u003cCLIENT_ID\u003e',\n      secret: '\u003cCLIENT_SECRET\u003e'\n    },\n    auth: oauthPlugin.TWITCH_CONFIGURATION\n  },\n  tokenRequestParams: {\n    client_id: '\u003cCLIENT_ID\u003e',\n    client_secret: '\u003cCLIENT_SECRET\u003e',\n  },\n  // register a fastify url to start the redirect flow\n  startRedirectPath: '/login/twitch',\n  // twitch redirect here after the user login\n  callbackUri: 'http://localhost:3000/login/twitch/callback'\n})\n```\n\n## License\n\nLicensed under [MIT](./LICENSE).\n\n*NB* See [`simple-oauth2`](https://github.com/lelylan/simple-oauth2) license too\n","funding_links":["https://github.com/sponsors/fastify","https://opencollective.com/fastify"],"categories":["JavaScript"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffastify%2Ffastify-oauth2","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffastify%2Ffastify-oauth2","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffastify%2Ffastify-oauth2/lists"}