{"id":18539066,"url":"https://github.com/simov/fastify-grant","last_synced_at":"2025-04-09T17:38:06.597Z","repository":{"id":57233293,"uuid":"292496897","full_name":"simov/fastify-grant","owner":"simov","description":"Fastify plugin for Grant OAuth Proxy","archived":false,"fork":false,"pushed_at":"2020-11-19T09:51:08.000Z","size":10,"stargazers_count":24,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-24T09:47:00.496Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":false,"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/simov.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}},"created_at":"2020-09-03T07:22:08.000Z","updated_at":"2025-01-20T02:17:57.000Z","dependencies_parsed_at":"2022-08-31T14:11:19.985Z","dependency_job_id":null,"html_url":"https://github.com/simov/fastify-grant","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simov%2Ffastify-grant","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simov%2Ffastify-grant/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simov%2Ffastify-grant/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simov%2Ffastify-grant/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/simov","download_url":"https://codeload.github.com/simov/fastify-grant/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248078597,"owners_count":21044141,"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":[],"created_at":"2024-11-06T19:46:06.905Z","updated_at":"2025-04-09T17:38:06.325Z","avatar_url":"https://github.com/simov.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"\n# fastify-grant\n\n\u003e _Fastify plugin for **[Grant][grant]** OAuth Proxy_\n\n```js\nvar fastify = require('fastify')\nvar cookie = require('fastify-cookie')\nvar session = require('fastify-session')\nvar grant = require('fastify-grant')\n\nfastify()\n  .register(cookie)\n  .register(session, {secret: 'grant', cookie: {secure: false}})\n  .register(grant({/*configuration - see below*/}))\n  .listen(3000)\n```\n\n\u003e _[Examples](https://github.com/simov/grant/tree/master/examples)_\n\n## ES Modules and TypeScript\n\nImport Grant in your `.mjs` files:\n\n```js\nimport fastify from 'fastify'\nimport cookie from 'fastify-cookie'\nimport session from 'fastify-session'\nimport grant from 'fastify-grant'\n\nfastify()\n  .register(cookie)\n  .register(session, {secret: 'grant', cookie: {secure: false}})\n  .register(grant({/*configuration - see below*/}))\n  .listen(3000)\n```\n\nGrant ships with extensive [type definitions][type-definitions] for TypeScript. However, a few additional type definitions that extend the typings of your HTTP framework of choice can be found [here][grant-types].\n\n# Configuration\n\n\n## Configuration: Basics\n\n```json\n{\n  \"defaults\": {\n    \"origin\": \"http://localhost:3000\",\n    \"transport\": \"session\",\n    \"state\": true\n  },\n  \"google\": {\n    \"key\": \"...\",\n    \"secret\": \"...\",\n    \"scope\": [\"openid\"],\n    \"nonce\": true,\n    \"custom_params\": {\"access_type\": \"offline\"},\n    \"callback\": \"/hello\"\n  },\n  \"twitter\": {\n    \"key\": \"...\",\n    \"secret\": \"...\",\n    \"callback\": \"/hi\"\n  }\n}\n```\n\n- **defaults** - default configuration for all providers\n  - **origin** - where your client server can be reached `http://localhost:3000` | `https://site.com` ...\n  - **transport** - a [transport](#callback-transport) used to deliver the [response data](#callback-response) in your `callback` route\n  - **state** - generate random state string\n- **provider** - any [supported provider](#grant) `google` | `twitter` ...\n  - **key** - `consumer_key` or `client_id` of your OAuth app\n  - **secret** - `consumer_secret` or `client_secret` of your OAuth app\n  - **scope** - array of OAuth scopes to request\n  - **nonce** - generate random nonce string ([OpenID Connect](#connect-openid-connect) only)\n  - **custom_params** - custom [authorization parameters](#connect-custom-parameters)\n  - **callback** - relative route or absolute URL to receive the response data `/hello` | `https://site.com/hey` ...\n\n\n## Configuration: Description\n\nKey | Location | Description\n:-| :-: | :-\n***Authorization Server*** |\n**`request_url`** | [oauth.json] | OAuth 1.0a only, first step\n**`authorize_url`** | [oauth.json] | OAuth 2.0 first step, OAuth 1.0a second step\n**`access_url`** | [oauth.json] | OAuth 2.0 second step, OAuth 1.0a third step\n**`oauth`** | [oauth.json] | OAuth version number\n**`scope_delimiter`** | [oauth.json] | String delimiter used for concatenating multiple scopes\n**`token_endpoint_auth_method`** | `[provider]` | Authentication method for the token endpoint\n**`token_endpoint_auth_signing_alg`** | `[provider]` | Signing algorithm for the token endpoint\n***Client Server*** |\n**`origin`** | `defaults` | Where your client server can be reached\n**`prefix`** | `defaults` | Path prefix for the Grant internal routes\n**`state`** | `defaults` | Random state string for OAuth 2.0\n**`nonce`** | `defaults` | Random nonce string for OpenID Connect\n**`pkce`** | `defaults` | Toggle PKCE support\n**`response`** | `defaults` | Response data to receive\n**`transport`** | `defaults` | A way to deliver the response data\n**`callback`** | `[provider]` | Relative or absolute URL to receive the response data\n**`overrides`** | `[provider]` | Static configuration overrides for a provider\n**`dynamic`** | `[provider]` | Configuration keys that can be overridden dynamically over HTTP\n***Client App*** |\n**`key`** **`client_id`** **`consumer_key`** | `[provider]` | The `client_id` or `consumer_key` of your OAuth app\n**`secret`** **`client_secret`**  **`consumer_secret`** | `[provider]` | The `client_secret` or `consumer_secret` of your OAuth app\n**`scope`** | `[provider]` | List of scopes to request\n**`custom_params`** | `[provider]` | Custom authorization parameters and their values\n**`subdomain`** | `[provider]` | String to embed into the authorization server URLs\n**`public_key`** | `[provider]` | Public PEM or JWK\n**`private_key`** | `[provider]` | Private PEM or JWK\n**`redirect_uri`** | `generated` | Absolute redirect URL of the OAuth app\n***Grant*** |\n**`name`** | `generated` | Provider's [name](#grant)\n**`[provider]`** | `generated` | Provider's [name](#grant) as key\n**`profile_url`** | [profile.json] | User profile URL\n\n\n## Configuration: Values\n\nKey | Location | Value\n:- | :-: | :-:\n***Authorization Server*** |\n**`request_url`** | [oauth.json] | `'https://api.twitter.com/oauth/request_token'`\n**`authorize_url`** | [oauth.json] | `'https://api.twitter.com/oauth/authenticate'`\n**`access_url`** | [oauth.json] | `'https://api.twitter.com/oauth/access_token'`\n**`oauth`** | [oauth.json] | `2` `1`\n**`scope_delimiter`** | [oauth.json] | `','` `' '`\n**`token_endpoint_auth_method`** | `[provider]` | `'client_secret_post'` `'client_secret_basic'` `'private_key_jwt'`\n**`token_endpoint_auth_signing_alg`** | `[provider]` | `'RS256'` `'ES256'` `'PS256'`\n***Client Server*** |\n**`origin`** | `defaults` | `'http://localhost:3000'` `https://site.com`\n**`prefix`** | `defaults` | `'/connect'` `/oauth` `''`\n**`state`** | `defaults` | `true`\n**`nonce`** | `defaults` | `true`\n**`pkce`** | `defaults` | `true`\n**`response`** | `defaults` | `['tokens', 'raw', 'jwt', 'profile']`\n**`transport`** | `defaults` | `'querystring'` `'session'` `'state'`\n**`callback`** | `[provider]` | `'/hello'` `'https://site.com/hi'`\n**`overrides`** | `[provider]` | `{something: {scope: ['..']}}`\n**`dynamic`** | `[provider]` | `['scope', 'subdomain']`\n***Client App*** |\n**`key`** **`client_id`** **`consumer_key`** | `[provider]` | `'123'`\n**`secret`** **`client_secret`**  **`consumer_secret`** | `[provider]` | `'123'`\n**`scope`** | `[provider]` | `['openid', '..']`\n**`custom_params`** | `[provider]` | `{access_type: 'offline'}`\n**`subdomain`** | `[provider]` | `'myorg'`\n**`public_key`** | `[provider]` | `'..PEM..'` `'{..JWK..}'`\n**`private_key`** | `[provider]` | `'..PEM..'` `'{..JWK..}'`\n**`redirect_uri`** |`generated` | `'http://localhost:3000/connect/twitter/callback'`\n***Grant*** |\n**`name`** |`generated` | `name: 'twitter'`\n**`[provider]`** |`generated` | `twitter: true`\n**`profile_url`** | [profile.json] | `'https://api.twitter.com/1.1/users/show.json'`\n\n---\n\n# Connect\n\n\n## Connect: Origin\n\n```json\n{\n  \"defaults\": {\n    \"origin\": \"http://localhost:3000\"\n  }\n}\n```\n\nThe `origin` is where your client server can be reached.\n\nYou login by navigating to the `/connect/:provider` route where `:provider` is a key in your configuration, usually one of the [officially supported](#grant) ones, but you can define [your own](#misc-custom-providers) as well. Additionally you can login through a [static override](#connect-static-overrides) defined for that provider by navigating to the `/connect/:provider/:override?` route.\n\n## Connect: Prefix\n\nBy default Grant operates on the following two routes:\n\n```\n/connect/:provider/:override?\n/connect/:provider/callback\n```\n\nHowever, the default `/connect` prefix can be configured:\n\n```json\n{\n  \"defaults\": {\n    \"origin\": \"http://localhost:3000\",\n    \"prefix\": \"/oauth\"\n  }\n}\n```\n\n\n## Connect: Redirect URI\n\nThe [`redirect_uri`](#misc-redirect-uri) of your OAuth app should follow this format:\n\n```\n[origin][prefix]/[provider]/callback\n```\n\nWhere [`origin`](#connect-origin) and [`prefix`](#connect-prefix) have to match the ones set in your configuration, and [`provider`](#grant) is a provider key found in your configuration.\n\nFor example: `http://localhost:3000/connect/google/callback`\n\nThis redirect URI is used internally by Grant. Depending on the [`transport`](#callback-transport) being used you will receive the response data in the [`callback`](#callback-data) route or absolute URL configured for that provider.\n\n\n## Connect: Custom Parameters\n\nSome providers may employ custom authorization parameters that you can configure using the `custom_params` key:\n\n```json\n{\n  \"google\": {\n    \"custom_params\": {\"access_type\": \"offline\", \"prompt\": \"consent\"}\n  },\n  \"reddit\": {\n    \"custom_params\": {\"duration\": \"permanent\"}\n  },\n  \"trello\": {\n    \"custom_params\": {\"name\": \"my app\", \"expiration\": \"never\"}\n  }\n}\n```\n\n\n## Connect: OpenID Connect\n\nThe `openid` scope is required, and generating a random `nonce` string is optional but recommended:\n\n```json\n{\n  \"google\": {\n    \"scope\": [\"openid\"],\n    \"nonce\": true\n  }\n}\n```\n\nGrant **does not** verify the signature of the returned `id_token` by default.\n\nHowever, the following two claims of the `id_token` are being validated:\n\n1. `aud` - is the token intended for my OAuth app?\n2. `nonce` - does it tie to a request of my own?\n\n\n## Connect: PKCE\n\nPKCE can be enabled for all providers or for a specific provider only:\n\n```json\n{\n  \"google\": {\n    \"pkce\": true\n  }\n}\n```\n\nProviders that do not support PKCE will ignore the additional parameters being sent.\n\n\n## Connect: Static Overrides\n\nProvider sub configurations can be configured using the `overrides` key:\n\n```json\n{\n  \"github\": {\n    \"key\": \"...\", \"secret\": \"...\",\n    \"scope\": [\"public_repo\"],\n    \"callback\": \"/hello\",\n    \"overrides\": {\n      \"notifications\": {\n        \"key\": \"...\", \"secret\": \"...\",\n        \"scope\": [\"notifications\"]\n      },\n      \"all\": {\n        \"scope\": [\"repo\", \"gist\", \"user\"],\n        \"callback\": \"/hey\"\n      }\n    }\n  }\n}\n```\n\nNavigate to:\n\n- `/connect/github` to request the public_repo `scope`\n- `/connect/github/notifications` to request the notifications `scope` using another OAuth App (`key` and `secret`)\n- `/connect/github/all` to request a bunch of `scope`s and also receive the response data in another `callback` route\n\n---\n\n# Callback\n\n\n## Callback: Data\n\nBy default the response data will be returned in your `callback` route or absolute URL encoded as querystring.\n\nDepending on the [`transport`](#callback-transport) being used the response data can be returned in the `session` or in the `state` object instead.\n\nThe amount of the returned data can be controlled through the [`response`](#callback-response) configuration.\n\n### OAuth 2.0\n\n```js\n{\n  id_token: '...',\n  access_token: '...',\n  refresh_token: '...',\n  raw: {\n    id_token: '...',\n    access_token: '...',\n    refresh_token: '...',\n    some: 'other data'\n  }\n}\n```\n\nThe `refresh_token` is optional. The `id_token` is returned only for [OpenID Connect](#connect-openid-connect) providers requesting the `openid` scope.\n\n\n### OAuth 1.0a\n\n```js\n{\n  access_token: '...',\n  access_secret: '...',\n  raw: {\n    oauth_token: '...',\n    oauth_token_secret: '...',\n    some: 'other data'\n  }\n}\n```\n\n\n### Error\n\n```js\n{\n  error: {\n    some: 'error data'\n  }\n}\n```\n\n\n## Callback: Transport\n\n### querystring\n\nBy default Grant will encode the OAuth [response data](#callback-data) as `querystring` in your `callback` route or absolute URL:\n\n```json\n{\n  \"github\": {\n    \"callback\": \"https://site.com/hello\"\n  }\n}\n```\n\nThis is useful when using Grant as [OAuth Proxy](#dynamic-oauth-proxy). However this final `https://site.com/hello?access_token=...` redirect can potentially leak private data in your server logs, especially when sitting behind a reverse proxy.\n\n### session\n\nFor local `callback` routes the session `transport` is recommended:\n\n```json\n{\n  \"defaults\": {\n    \"transport\": \"session\"\n  },\n  \"github\": {\n    \"callback\": \"/hello\"\n  }\n}\n```\n\nThis will make the OAuth [response data](#callback-data) available in the `session` object instead:\n\n```js\nreq.session.grant.response // Fastify\n```\n\n### state\n\nThe request/response lifecycle `state` can be used as well:\n\n```json\n{\n  \"defaults\": {\n    \"transport\": \"state\"\n  }\n}\n```\n\nIn this case a `callback` route is not needed, and it will be ignored if provided. The response data will be available in the request/response lifecycle state object instead:\n\n```js\nres.grant.response // Fastify\n```\n\n## Callback: Response\n\nBy default Grant returns all of the available tokens and the `raw` response data returned by the Authorization server:\n\n```js\n{\n  id_token: '...',\n  access_token: '...',\n  refresh_token: '...',\n  raw: {\n    id_token: '...',\n    access_token: '...',\n    refresh_token: '...',\n    some: 'other data'\n  }\n}\n```\n\n### querystring\n\nWhen using the querystring [`transport`](#callback-transport) it might be a good idea to limit the response data:\n\n```json\n{\n  \"defaults\": {\n    \"response\": [\"tokens\"]\n  }\n}\n```\n\nThis will return only the tokens available, without the `raw` response data.\n\nThis is useful when using Grant as [OAuth Proxy](#dynamic-oauth-proxy). Encoding potentially large amounts of data as querystring can lead to incompatibility issues with some servers and browsers, and generally is considered a bad practice.\n\n### session\n\nUsing the session [`transport`](#callback-transport) is generally safer, but it also depends on the implementation of your session store.\n\nIn case your session store encodes the entire session in a cookie, not just the session ID, some servers may reject the HTTP request because of HTTP headers size being too big.\n\n```json\n{\n  \"google\": {\n    \"response\": [\"tokens\"]\n  }\n}\n```\n\nThis will return only the tokens available, without the `raw` response data.\n\n### jwt\n\nGrant can also return even larger [response data](#callback-data) by including the decoded JWT for [OpenID Connect](#connect-openid-connect) providers that return `id_token`:\n\n```json\n{\n  \"google\": {\n    \"response\": [\"tokens\", \"raw\", \"jwt\"]\n  }\n}\n```\n\nThis will make the decoded JWT available in the response data:\n\n```js\n{\n  id_token: '...',\n  access_token: '...',\n  refresh_token: '...',\n  raw: {\n    id_token: '...',\n    access_token: '...',\n    refresh_token: '...',\n    some: 'other data'\n  },\n  jwt: {id_token: {header: {}, payload: {}, signature: '...'}}\n}\n```\n\nMake sure you include all of the response keys that you want to be returned when configuring the `response` data explicitly.\n\n\n### profile\n\nOutside of the regular OAuth flow, Grant can request the user profile as well:\n\n```json\n{\n  \"google\": {\n    \"response\": [\"tokens\", \"profile\"]\n  }\n}\n```\n\nAdditionaly a `profile` key will be available in the response data:\n\n```js\n{\n  access_token: '...',\n  refresh_token: '...',\n  profile: {some: 'user data'}\n}\n```\n\nThe `profile` key contains either the raw response data returned by the user profile endpoint or an error message.\n\nNot all of the supported providers have their `profile_url` set, and some of them might require custom parameters. Usually the user profile endpoint is accessible only when certain `scope`s were requested.\n\n---\n\n# Dynamic Configuration\n\n\n## Dynamic: Instance\n\nEvery Grant instance have a `config` property attached to it:\n\n```js\nvar grant = Grant(require('./config'))\nconsole.log(grant.config)\n```\n\nYou can use the `config` property to alter the Grant's behavior during runtime without having to restart your server.\n\nThis property contains the **generated** configuration used internally by Grant, and changes made to that configuration affects the **entire** Grant instance!\n\n\n## Dynamic: State\n\nThe request/response lifecycle state can be used to alter configuration on every request:\n\n```js\nreq.grant = {dynamic: {subdomain: 'usershop'}} // Fastify\n```\n\nThis is useful in cases when you want to configure Grant dynamically with potentially sensitive data that you don't want to send over HTTP.\n\nThe request/response lifecycle state is not controlled by the [`dynamic`](#dynamic-http) configuration, meaning that you can override any configuration key.\n\nAny allowed [`dynamic`](#dynamic-http) configuration key sent through HTTP GET/POST request will override the identical one set using a state override.\n\n## Dynamic: HTTP\n\nThe `dynamic` configuration allows certain configuration keys to be set dynamically over HTTP GET/POST request.\n\nFor example `shopify` requires your shop name to be embedded into the OAuth URLs, so it makes sense to allow the [`subdomain`](#subdomain-urls) configuration key to be set dynamically:\n\n```json\n{\n  \"shopify\": {\n    \"dynamic\": [\"subdomain\"]\n  }\n}\n```\n\nThen you can have a web form on your website allowing the user to specify the shop name:\n\n```html\n\u003cform action=\"/connect/shopify\" method=\"POST\" accept-charset=\"utf-8\"\u003e\n  \u003cinput type=\"text\" name=\"subdomain\" value=\"\" /\u003e\n  \u003cbutton\u003eLogin\u003c/button\u003e\n\u003c/form\u003e\n```\n\nWhen making a `POST` request to the `/connect/:provider/:override?` route you have to mount a form body parser middleware before mounting Grant:\n\n```js\n// fastify\nvar parser = require('fastify-formbody')\n.register(parser)\n.register(grant(config))\n```\n\nAlternatively you can make a `GET` request to the `/connect/:provider/:override?` route:\n\n```\nhttps://awesome.com/connect/shopify?subdomain=usershop\n```\n\nAny `dynamic` configuration sent over HTTP GET/POST request overrides any other configuration.\n\n\n  [grant]: https://github.com/simov/grant\n  [grant-oauth]: https://grant.outofindex.com\n\n  [oauth.json]: https://github.com/simov/grant/blob/master/config/oauth.json\n  [profile.json]: https://github.com/simov/grant/blob/master/config/profile.json\n  [reserved-keys]: https://github.com/simov/grant/blob/master/config/reserved.json\n  [examples]: https://github.com/simov/grant/tree/master/examples\n\n  [grant-types]: https://github.com/simov/grant-types\n  [type-definitions]: https://github.com/simov/grant/blob/master/grant.d.ts\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsimov%2Ffastify-grant","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsimov%2Ffastify-grant","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsimov%2Ffastify-grant/lists"}