{"_id":"@barfittc/ts-oauth2-server","_rev":"2-9bbb5c63b42e3133f9805eed9a33d80b","name":"@barfittc/ts-oauth2-server","dist-tags":{"latest":"3.0.4"},"versions":{"3.0.2":{"name":"@barfittc/ts-oauth2-server","version":"3.0.2","type":"module","author":{"name":"Jason Raimondi","email":"jason@raimondi.us"},"funding":"https://github.com/barfittc/ts-oauth2-server","license":"MIT","private":false,"scripts":{"clean":"rimraf dist","prebuild":"run-s clean","bundle":"tsup","build":"run-s clean bundle","start":"tsc -p tsconfig.build.json --watch","test":"vitest run","test:watch":"vitest","test:cov":"vitest run --coverage","format":"prettier --write \"**/*.ts\"","prepublishOnly":"run-s build test","docs:dev":"vitepress dev docs","docs:build":"vitepress build docs","docs:preview":"vitepress preview docs"},"exports":{".":"./src/index.ts","./express":"./src/adapters/express.ts","./fastify":"./src/adapters/fastify.ts"},"publishConfig":{"access":"public","main":"./dist/index.js","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"import":"./dist/index.js","require":"./dist/index.cjs","types":"./dist/index.d.ts"},"./express":{"import":"./dist/express.js","require":"./dist/express.cjs","types":"./dist/express.d.ts"},"./fastify":{"import":"./dist/fastify.js","require":"./dist/fastify.cjs","types":"./dist/fastify.d.ts"}},"typesVersions":{"*":{"*":["./dist/*","./dist/index.d.ts"]}}},"engines":{"node":">=16"},"devDependencies":{"@types/body-parser":"^1.19.2","@types/express":"^4.17.17","@types/jsonwebtoken":"^9.0.2","@types/ms":"^0.7.31","@types/node":"^20.5.9","@types/supertest":"^2.0.12","@vitest/coverage-istanbul":"^0.31.4","body-parser":"^1.20.2","express":"^4.18.2","fastify":"^4.17.0","npm-run-all":"^4.1.5","prettier":"^2.8.8","rimraf":"^5.0.1","supertest":"^6.3.3","tslib":"^2.5.2","tsup":"^6.7.0","typescript":"^5.1.3","vite":"^4.3.9","vitepress":"1.0.0-beta.1","vitest":"^0.31.4"},"dependencies":{"jsonwebtoken":"^9.0.0","ms":"^2.1.3","uri-js":"^4.4.1"},"tsup":{"entry":{"index":"./src/index.ts","express":"./src/adapters/express.ts","fastify":"./src/adapters/fastify.ts"},"format":["cjs","esm"],"target":"node16","clean":true,"dts":true,"splitting":false,"sourcemap":true},"gitHead":"ab03ec420cc5ed44f72db0edee32a6330a6a917b","description":"[![GitHub Workflow Status]( https://img.shields.io/github/actions/workflow/status/jasonraimondi/ts-oauth2-server/build-and-test.yml?branch=main&style=flat-square)](https://github.com/jasonraimondi/ts-oauth2-server) [![Test Coverage](https://img.shields.io","_id":"@barfittc/ts-oauth2-server@3.0.2","_nodeVersion":"16.20.2","_npmVersion":"8.19.4","dist":{"integrity":"sha512-JyPLuHSWnZydeOCx+aHNQJLtCxoKeourH7oS0mFlC8R7XmSn8i21Fqv3FMTKbBXhEct6PBYPxsEH3TjNPELhRA==","shasum":"b4c8a876ec7d672a5fa9890a0f95b5035b91d42c","tarball":"https://registry.npmjs.org/@barfittc/ts-oauth2-server/-/ts-oauth2-server-3.0.2.tgz","fileCount":57,"unpackedSize":428222,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIF3qEklT7EbYzIztu8qbsA/7ys2EVJsd6j60aiMsT5wjAiAj0+o5ahxzITQawJCFKFV9rVDf4/KGI0bggejnw4zUBQ=="}]},"_npmUser":{"name":"barfittc","email":"barfittc@gmail.com"},"directories":{},"maintainers":[{"name":"barfittc","email":"barfittc@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/ts-oauth2-server_3.0.2_1694053507563_0.3427772141690757"},"_hasShrinkwrap":false},"3.0.3":{"name":"@barfittc/ts-oauth2-server","version":"3.0.3","type":"module","author":{"name":"Jason Raimondi","email":"jason@raimondi.us"},"funding":"https://github.com/barfittc/ts-oauth2-server","license":"MIT","main":"src/index.ts","private":false,"scripts":{"clean":"rimraf dist","prebuild":"run-s clean","bundle":"tsup","build":"run-s clean bundle","start":"tsc -p tsconfig.build.json --watch","test":"vitest run","test:watch":"vitest","test:cov":"vitest run --coverage","format":"prettier --write \"**/*.ts\"","prepublishOnly":"run-s build test","docs:dev":"vitepress dev docs","docs:build":"vitepress build docs","docs:preview":"vitepress preview docs"},"exports":{".":"./src/index.ts","./express":"./src/adapters/express.ts","./fastify":"./src/adapters/fastify.ts"},"publishConfig":{"access":"public","main":"./dist/index.js","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"import":"./dist/index.js","require":"./dist/index.cjs","types":"./dist/index.d.ts"},"./express":{"import":"./dist/express.js","require":"./dist/express.cjs","types":"./dist/express.d.ts"},"./fastify":{"import":"./dist/fastify.js","require":"./dist/fastify.cjs","types":"./dist/fastify.d.ts"}},"typesVersions":{"*":{"*":["./dist/*","./dist/index.d.ts"]}}},"engines":{"node":">=16"},"devDependencies":{"@types/body-parser":"^1.19.2","@types/express":"^4.17.17","@types/jsonwebtoken":"^9.0.2","@types/ms":"^0.7.31","@types/node":"^20.5.9","@types/supertest":"^2.0.12","@vitest/coverage-istanbul":"^0.31.4","body-parser":"^1.20.2","express":"^4.18.2","fastify":"^4.17.0","npm-run-all":"^4.1.5","prettier":"^2.8.8","rimraf":"^5.0.1","supertest":"^6.3.3","tslib":"^2.5.2","tsup":"^6.7.0","typescript":"^5.1.3","vite":"^4.3.9","vitepress":"1.0.0-beta.1","vitest":"^0.31.4"},"dependencies":{"jsonwebtoken":"^9.0.0","ms":"^2.1.3","uri-js":"^4.4.1"},"tsup":{"entry":{"index":"./src/index.ts","express":"./src/adapters/express.ts","fastify":"./src/adapters/fastify.ts"},"format":["cjs","esm"],"target":"node16","clean":true,"dts":true,"splitting":false,"sourcemap":true},"gitHead":"f45e891de06845ed3d36e7e4c125f704d455e19f","description":"[![GitHub Workflow Status]( https://img.shields.io/github/actions/workflow/status/jasonraimondi/ts-oauth2-server/build-and-test.yml?branch=main&style=flat-square)](https://github.com/jasonraimondi/ts-oauth2-server) [![Test Coverage](https://img.shields.io","_id":"@barfittc/ts-oauth2-server@3.0.3","_nodeVersion":"16.20.2","_npmVersion":"8.19.4","dist":{"integrity":"sha512-NiKnv+ZHGoNpU0CbRzuLEtKaEym5fYZhPdwCiBxB+3CLSZpNkjCpufZE0QNeu8Xf92H7l+bxntv88cgt3WjTYQ==","shasum":"965581c73288766b2f4d9c562c7652d78be5bd74","tarball":"https://registry.npmjs.org/@barfittc/ts-oauth2-server/-/ts-oauth2-server-3.0.3.tgz","fileCount":57,"unpackedSize":428248,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICgm6Hb+t90Dnai65LPRXsZVWMrFeX1/9Yr1qc2COnlGAiB7zUHBB27Ck19mEEJN+zI5E5HKmvvcoP9Hz76B034xdA=="}]},"_npmUser":{"name":"barfittc","email":"barfittc@gmail.com"},"directories":{},"maintainers":[{"name":"barfittc","email":"barfittc@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/ts-oauth2-server_3.0.3_1694055472275_0.2976899846522587"},"_hasShrinkwrap":false},"3.0.4":{"name":"@barfittc/ts-oauth2-server","version":"3.0.4","type":"module","author":{"name":"Jason Raimondi","email":"jason@raimondi.us"},"funding":"https://github.com/barfittc/ts-oauth2-server","license":"MIT","main":"src/index.ts","private":false,"scripts":{"clean":"rimraf dist","prebuild":"run-s clean","bundle":"tsup","build":"run-s clean bundle","start":"tsc -p tsconfig.build.json --watch","test":"vitest run","test:watch":"vitest","test:cov":"vitest run --coverage","format":"prettier --write \"**/*.ts\"","prepublishOnly":"run-s build test","docs:dev":"vitepress dev docs","docs:build":"vitepress build docs","docs:preview":"vitepress preview docs"},"exports":{".":"./src/index.ts","./express":"./src/adapters/express.ts","./fastify":"./src/adapters/fastify.ts"},"publishConfig":{"access":"public","main":"./dist/index.js","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"import":"./dist/index.js","require":"./dist/index.cjs","types":"./dist/index.d.ts"},"./express":{"import":"./dist/express.js","require":"./dist/express.cjs","types":"./dist/express.d.ts"},"./fastify":{"import":"./dist/fastify.js","require":"./dist/fastify.cjs","types":"./dist/fastify.d.ts"}},"typesVersions":{"*":{"*":["./dist/*","./dist/index.d.ts"]}}},"engines":{"node":">=16"},"devDependencies":{"@types/bcrypt":"^5.0.0","@types/body-parser":"^1.19.2","@types/express":"^4.17.17","@types/jsonwebtoken":"^9.0.2","@types/ms":"^0.7.31","@types/node":"^20.5.9","@types/supertest":"^2.0.12","@vitest/coverage-istanbul":"^0.31.4","body-parser":"^1.20.2","express":"^4.18.2","fastify":"^4.17.0","npm-run-all":"^4.1.5","prettier":"^2.8.8","rimraf":"^5.0.1","supertest":"^6.3.3","tslib":"^2.5.2","tsup":"^6.7.0","typescript":"^5.1.3","vite":"^4.3.9","vitepress":"1.0.0-beta.1","vitest":"^0.31.4"},"dependencies":{"jsonwebtoken":"^9.0.0","ms":"^2.1.3","random-bytes":"^1.0.0","uri-js":"^4.4.1"},"tsup":{"entry":{"index":"./src/index.ts","express":"./src/adapters/express.ts","fastify":"./src/adapters/fastify.ts"},"format":["cjs","esm"],"target":"node16","clean":true,"dts":true,"splitting":false,"sourcemap":true},"gitHead":"5dff6964ca359fa31ddf8f312fea4ddb655bb799","description":"[![GitHub Workflow Status]( https://img.shields.io/github/actions/workflow/status/jasonraimondi/ts-oauth2-server/build-and-test.yml?branch=main&style=flat-square)](https://github.com/jasonraimondi/ts-oauth2-server) [![Test Coverage](https://img.shields.io","_id":"@barfittc/ts-oauth2-server@3.0.4","_nodeVersion":"16.20.2","_npmVersion":"8.19.4","dist":{"integrity":"sha512-JbVF7buPh1sJfRn+EubHJ4jVTZ3lBm5rGc5APxiHER1a0FGf3I0Ndv0zXpxDUJTCXavDsyMmHqUFXulNC826qg==","shasum":"2982cec7655f41262ecf76eef8b478610c2e8e5d","tarball":"https://registry.npmjs.org/@barfittc/ts-oauth2-server/-/ts-oauth2-server-3.0.4.tgz","fileCount":57,"unpackedSize":428300,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFsXcAo+lmeuCoJXz99wtBpSmtiLQKzUQWTeJE8SS7iBAiB9wKVfj2DJbgxc/5ovk7NrJKi212bm9fDoTEr6h2KCig=="}]},"_npmUser":{"name":"barfittc","email":"barfittc@gmail.com"},"directories":{},"maintainers":[{"name":"barfittc","email":"barfittc@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/ts-oauth2-server_3.0.4_1694057157017_0.8416970576830294"},"_hasShrinkwrap":false}},"time":{"created":"2023-09-07T02:25:07.429Z","3.0.2":"2023-09-07T02:25:07.793Z","modified":"2023-09-07T03:25:57.589Z","3.0.3":"2023-09-07T02:57:52.550Z","3.0.4":"2023-09-07T03:25:57.316Z"},"maintainers":[{"name":"barfittc","email":"barfittc@gmail.com"}],"description":"[![GitHub Workflow Status]( https://img.shields.io/github/actions/workflow/status/jasonraimondi/ts-oauth2-server/build-and-test.yml?branch=main&style=flat-square)](https://github.com/jasonraimondi/ts-oauth2-server) [![Test Coverage](https://img.shields.io","author":{"name":"Jason Raimondi","email":"jason@raimondi.us"},"license":"MIT","readme":"# TypeScript OAuth2.0 Server\n\n[![GitHub Workflow Status]( https://img.shields.io/github/actions/workflow/status/jasonraimondi/ts-oauth2-server/build-and-test.yml?branch=main&style=flat-square)](https://github.com/jasonraimondi/ts-oauth2-server)\n[![Test Coverage](https://img.shields.io/codeclimate/coverage/jasonraimondi/typescript-oauth2-server?style=flat-square)](https://codeclimate.com/github/jasonraimondi/typescript-oauth2-server/test_coverage)\n[![GitHub package.json version](https://img.shields.io/github/package-json/v/jasonraimondi/ts-oauth2-server?style=flat-square)](https://github.com/jasonraimondi/ts-oauth2-server/releases/latest)\n[![NPM Downloads](https://img.shields.io/npm/dt/@jmondi/oauth2-server?label=npm%20downloads&style=flat-square)](https://www.npmjs.com/package/@jmondi/oauth2-server)\n\n`@jmondi/oauth2-server` is a standards compliant implementation of an OAuth 2.0 authorization server for Node, written in TypeScript. \n\nRequires `node >= 16`\n\nThe following RFCs are implemented:\n\n- [RFC6749 “OAuth 2.0”](https://tools.ietf.org/html/rfc6749)\n- [RFC6750 “The OAuth 2.0 Authorization Framework: Bearer Token Usage”](https://tools.ietf.org/html/rfc6750)\n- [RFC7009 “OAuth 2.0 Token Revocation”](https://tools.ietf.org/html/rfc7009)\n- [RFC7519 “JSON Web Token (JWT)”](https://tools.ietf.org/html/rfc7519)\n- [RFC7636 “Proof Key for Code Exchange by OAuth Public Clients”](https://tools.ietf.org/html/rfc7636)\n\nOut of the box it supports the following grants:\n\n- [Authorization code grant](#authorization-code-grant-w-pkce)\n- [Client credentials grant](#client-credentials-grant)\n- [Refresh grant](#refresh-token-grant)\n- [Implicit grant](#implicit-grant) // not recommended \n- [Resource owner password credentials grant](#password-grant) // not recommended\n\nAdapters are included for the following frameworks:\n\n- [Express](https://jasonraimondi.github.io/ts-oauth2-server/adapters/#express)\n- [Fastify](https://jasonraimondi.github.io/ts-oauth2-server/adapters/#fastify)\n\nExample implementations:\n\n- [Simple](./example)\n- [Advanced](https://github.com/jasonraimondi/ts-oauth2-server-example)\n\nThe included adapters are just helper functions, really any framework should be supported. Take a look at the adapter implementations for [express](https://github.com/jasonraimondi/ts-oauth2-server/blob/main/src/adapters/express.ts) and [fastify](https://github.com/jasonraimondi/ts-oauth2-server/blob/main/src/adapters/fastify.ts) to learn how you can implement one for your favorite tool!\n\n## Getting Started\n\nSave some eye strain, **use the [documentation site](https://jasonraimondi.github.io/ts-oauth2-server/)**\n\n### Install\n\n```bash\npnpm add @jmondi/oauth2-server\n```\n\n| Version         | Latest Version | Security Updates |\n|-----------------|----------------|------------------|\n| [3.x][version3] | :tada:         | :tada:           |\n| [2.x][version2] |                | :tada:           |\n\n[version3]: https://github.com/jasonraimondi/ts-oauth2-server/tree/main\n[version2]: https://github.com/jasonraimondi/ts-oauth2-server/tree/2.x\n\n### Endpoints \n\nThe server uses two endpoints, `GET /authorize` and `POST /token`. \n\nThe [Token Endpoint](#the-token-endpoint) is a back channel endpoint that issues a use-able access token. \n\nThe [Authorize Endpoint](#the-authorize-endpoint) is a front channel endpoint that issues an authorization code. The \nauthorization code can then be exchanged to the `AuthorizationServer` endpoint for a use-able access token.\n\n#### The Token Endpoint\n\n```typescript\nimport {\n handleExpressResponse,\n handleExpressError,\n} from \"@jmondi/oauth2-server/express\";\n\napp.post(\"/token\", async (req: Express.Request, res: Express.Response) => {\n const request = requestFromExpress(req);\n try {\n  const oauthResponse = await authorizationServer.respondToAccessTokenRequest(request);\n  return handleExpressResponse(res, oauthResponse);\n } catch (e) {\n  handleExpressError(e, res);\n  return;\n }\n});\n```\n\n#### Authorize Endpoint\n\nThe `/authorize` endpoint is a front channel endpoint that issues an authorization code. The authorization code can then be exchanged to the `AuthorizationServer` endpoint for a useable access token.\n\nThe endpoint should redirect the user to login, and then to accept the scopes requested by the application, and only when the user accepts, should it send the user back to the clients redirect uri.\n\nWe are able to add in scope acceptance and 2FA into our authentication flow.\n\n```typescript\nimport { requestFromExpress } from \"@jmondi/oauth2-server/express\";\n\napp.get(\"/authorize\", async (req: Express.Request, res: Express.Response) => {\n  const request = requestFromExpress(req);\n\n  try {\n    // Validate the HTTP request and return an AuthorizationRequest.\n    const authRequest = await authorizationServer.validateAuthorizationRequest(request);\n\n    // You will probably redirect the user to a login endpoint. \n    if (!req.user) {\n      res.redirect(\"/login\")\n      return;\n    }\n    // After login, the user should be redirected back with user in the session.\n    // You will need to manage the authorization query on the round trip.\n    // The auth request object can be serialized and saved into a user's session.\n\n    // Once the user has logged in set the user on the AuthorizationRequest\n    authRequest.user = req.user;\n    \n    // Once the user has approved or denied the client update the status\n    // (true = approved, false = denied)\n    authRequest.isAuthorizationApproved = getIsAuthorizationApprovedFromSession();\n\n    // If the user has not approved the client's authorization request, \n    // the user should be redirected to the approval screen.\n    if (!authRequest.isAuthorizationApproved) {\n      // This form will ask the user to approve the client and the scopes requested.\n      // \"Do you authorize Jason to: read contacts? write contacts?\"\n      res.redirect(\"/scopes\")\n      return;\n    }\n\n    // At this point the user has approved the client for authorization.\n    // Any last authorization requests such as Two Factor Authentication (2FA) can happen here.\n\n\n    // Redirect back to redirect_uri with `code` and `state` as url query params.\n    const oauthResponse = await authorizationServer.completeAuthorizationRequest(authRequest);\n    return handleExpressResponse(res, oauthResponse);\n  } catch (e) {\n    handleExpressError(e, res);\n  }\n});\n```\n\n### Authorization Server\n\nThe AuthorizationServer depends on [the repositories](#repositories). By default, no grants are enabled; each grant is opt-in and must be enabled when creating the AuthorizationServer.\n\nYou can enable any grant types you would like to support.\n\n```typescript\nconst authorizationServer = new AuthorizationServer(\n  clientRepository,\n  accessTokenRepository,\n  scopeRepository,\n  new JwtService(\"secret-key\"),\n);\n\n// Enable as many or as few grants as you'd like.\nauthorizationServer.enableGrantTypes(\n  \"client_credentials\",\n  \"refresh_token\",\n);\n\n// with custom token TTL\nauthorizationServer.enableGrantTypes(\n  [\"client_credentials\", new DateInterval(\"1d\")],\n  [\"refresh_token\", new DateInterval(\"1d\")],\n);\n```\n\n### Repositories\n\nThere are a few repositories you are going to need to implement in order to create an `AuthorizationServer`.\n\n[Auth Code Repository](https://jasonraimondi.github.io/ts-oauth2-server/repositories/#auth-code-repository)\n\n[Client Repository](https://jasonraimondi.github.io/ts-oauth2-server/repositories/#client-repository)\n\n[Scope Repository](https://jasonraimondi.github.io/ts-oauth2-server/repositories/#scope-repository) \n\n[Token Repository](https://jasonraimondi.github.io/ts-oauth2-server/repositories/#token-repository)\n\n[User Repository](https://jasonraimondi.github.io/ts-oauth2-server/repositories/#user-repository)\n\n### Entities\n\nAnd a few entities.\n\n[Auth Code Entity](https://jasonraimondi.github.io/ts-oauth2-server/entities/#auth-code-entity)\n\n[Client Entity](https://jasonraimondi.github.io/ts-oauth2-server/entities/#client-entity)\n\n[Scope Entity](https://jasonraimondi.github.io/ts-oauth2-server/entities/#scope-entity)\n\n[Token Entity](https://jasonraimondi.github.io/ts-oauth2-server/entities/#token-entity)\n\n[User Entity](https://jasonraimondi.github.io/ts-oauth2-server/entities/#user-entity)\n\n## Grants\n\nGrants are different ways a [client](https://jasonraimondi.github.io/ts-oauth2-server/glossary/README.md#client) can obtain an `access_token` that will authorize it to use the [resource server](https://jasonraimondi.github.io/ts-oauth2-server/glossary/README.md#resource-server).\n\n### Which Grant?\n\nDeciding which grant to use depends on the type of client the end user will be using.\n\n```\n+-------+\n| Start |\n+-------+\n    V\n    |\n    \n    |\n+------------------------+              +-----------------------+\n| Have a refresh token?  |>----Yes----->|  Refresh Token Grant  |\n+------------------------+              +-----------------------+\n    V\n    |\n    No\n    |\n+---------------------+                \n|     Who is the      |                  +--------------------------+\n| Access token owner? |>---A Machine---->| Client Credentials Grant |\n+---------------------+                  +--------------------------+\n    V\n    |\n    |\n   A User\n    |\n    |\n+----------------------+                \n| What type of client? |   \n+----------------------+     \n    |\n    |                                 +---------------------------+\n    |>-----------Server App---------->| Auth Code Grant with PKCE |\n    |                                 +---------------------------+\n    |\n    |                                 +---------------------------+\n    |>-------Browser Based App------->| Auth Code Grant with PKCE |\n    |                                 +---------------------------+\n    |\n    |                                 +---------------------------+\n    |>-------Native Mobile App------->| Auth Code Grant with PKCE |\n                                      +---------------------------+\n\n```\n\n### Client Credentials Grant\n\n[Full Docs](https://jasonraimondi.github.io/ts-oauth2-server/grants/client_credentials.html)\n\nWhen applications request an access token to access their own resources, not on behalf of a user.\n\n#### Flow\n\nThe client sends a **POST** to the `/token` endpoint with the following body:\n\n- **grant_type** must be set to `client_credentials`\n- **client_id** is the client identifier you received when you first created the application\n- **client_secret** is the client secret\n- **scope** is a string with a space delimited list of requested scopes. The requested scopes must be valid for the client.\n\nThe authorization server will respond with the following response.\n\n- **token_type** will always be `Bearer`\n- **expires_in** is the time the token will live in seconds\n- **access_token** is a JWT signed token and can be used to authenticate into the resource server\n- **scope** is a space delimited list of scopes the token has access to\n\n### Authorization Code Grant (w/ PKCE)\n   \nA temporary code that the client will exchange for an access token. The user authorizes the application, they are redirected back to the application with a temporary code in the URL. The application exchanges that code for the access token. \n\n#### Flow\n\n##### Part One\n\nThe client redirects the user to the `/authorize` with the following query parameters:\n\n- **response_type** must be set to `code`\n- **client_id** is the client identifier you received when you first created the application\n- **redirect_uri** indicates the URL to return the user to after authorization is complete, such as org.example.app://redirect\n- **state** is a random string generated by your application, which you’ll verify later\n- **code_challenge** must match the The code challenge as generated below, \n- **code_challenge_method** – Either `plain` or `S256`, depending on whether the challenge is the plain verifier string or the SHA256 hash of the string. If this parameter is omitted, the server will assume plain.\n\nThe user will be asked to login to the authorization server and approve the client and requested scopes.\n\nIf the user approves the client, they will be redirected from the authorization server to the provided `redirect_uri` with the following fields in the query string:\n\n- **code** is the authorization code that will soon be exchanged for a token\n- **state** is the random string provided and should be compared against the initially provided state\n\n##### Part Two\n\nThe client sends a **POST** to the `/token` endpoint with the following body:\n\n- **grant_type** must be set to `authorization_code`\n- **client_id** is the client identifier you received when you first created the application\n- **client_secret** (optional) is the client secret and should only be provided if the client is confidential\n- **redirect_uri**\n- **code_verifier**\n- **code** is the authorization code from the query string\n\nThe authorization server will respond with the following response\n\n- **token_type** will always be `Bearer`\n- **expires_in** is the time the token will live in seconds\n- **access_token** is a JWT signed token and is used to authenticate into the resource server\n- **refresh_token** is a JWT signed token and can be used in with the [refresh grant](#refresh-token-grant) \n- **scope** is a space delimited list of scopes the token has access to\n\n#### Code Verifier\n\nThe `code_verifier` is part of the extended [“PKCE”](https://tools.ietf.org/html/rfc7636) and helps mitigate the threat of having authorization codes intercepted.\n\nBefore initializing [Part One](#part-one) of the authorization code flow, the client first creats a `code_verifier`. This is a cryptographically random string using the characters A-Z, a-z, 0-9, and the punctuation characters `-._~` (hyphen, period, underscore, and tilde), between 43 and 128 characters long.\n\nWe can do this in Node using the native crypto package and a `base64urlencode` function:\n\n```typescript\nimport crypto from \"crypto\";\n\nconst code_verifier = crypto.randomBytes(43).toString(\"hex\");\n```\n\nhttps://www.oauth.com/oauth2-servers/pkce/authorization-request/\n\n#### Code Challenge\n\nNow we need to create a `code_challenge` from our `code_verifier`. \n\nFor devices that can perform a SHA256 hash, the code challenge is a BASE64-URL-encoded string of the SHA256 hash of the code verifier. \n\n```typescript\nconst code_challenge = base64urlencode(\n  crypto.createHash(\"sha256\")\n    .update(code_verifier)\n    .digest()\n);\n```\n\nClients that do not have the ability to perform a SHA256 hash are permitted to use the plain `code_verifier` string as the `code_challenge`.\n\n```typescript\nconst code_challenge = code_verifier;\n```\n\n### Refresh Token Grant \n\nAccess tokens eventually expire. The refresh token grant enables the client to obtain a new access_token from an existing refresh_token.\n\n#### Flow\n\nA complete refresh token request will include the following parameters:\n\n- **grant_type** must be set to `refresh_token`\n- **client_id** is the client identifier you received when you first created the application\n- **client_secret** if the client is confidential (has a secret), this must be provided\n- **refresh_token** must be the signed token previously issued to the client\n- **scope** (optional) the requested scope must not include any additional scopes that were not previously issued to the original token\n\n\nThe authorization server will respond with the following response\n\n- **token_type** will always be `Bearer`\n- **expires_in** is the time the token will live in seconds\n- **access_token** is a JWT signed token and is used to authenticate into the resource server\n- **refresh_token** is a JWT signed token and can be used in with the [refresh grant](#refresh-token-grant) \n- **scope** is a space delimited list of scopes the token has access to\n\n### Password Grant\n\nThe Password Grant is for first party clients that are able to hold secrets (ie not Browser or Native Mobile Apps)\n\n#### Flow\n\nA complete refresh token request will include the following parameters:\n\n- **grant_type** must be set to `password`\n- **client_id** is the client identifier you received when you first created the application\n- **client_secret** if the client is confidential (has a secret), this must be provided\n- **username**\n- **password**\n- **scope** (optional) \n\nThe authorization server will respond with the following response\n\n- **token_type** will always be `Bearer`\n- **expires_in** is the time the token will live in seconds\n- **access_token** is a JWT signed token and is used to authenticate into the resource server\n- **refresh_token** is a JWT signed token and can be used in with the [refresh grant](#refresh-token-grant) \n- **scope** is a space delimited list of scopes the token has access to\n\n\n### Implicit Grant\n\nThis grant is supported in the AuthorizationServer, but not recommended to use and thus is not documented. Industry best practice recommends using the Authorization Code Grant w/ PKCE for clients such as native and browser-based apps.\n\nPlease look at these great resources:\n\n- [OAuth 2.0 Implicit Grant](https://oauth.net/2/grant-types/implicit/)\n- VIDEO: [What's Going On with the Implicit Flow?](https://www.youtube.com/watch?v=CHzERullHe8) by Aaron Parecki\n- [Is the OAuth 2.0 Implicit Flow Dead?](https://developer.okta.com/blog/2019/05/01/is-the-oauth-implicit-flow-dead) by Aaron Parecki (developer.okta.com)\n\n## Revoke Token\n\nNote: Implementing this endpoint is optional.\n\nThe `/token/revoke` endpoint is a back channel endpoint that revokes an existing token. Implementing this endpoint is optional.\n\n```typescript\napp.post(\"/token/revoke\", async (req: Express.Request, res: Express.Response) => {\n  try {\n    const oauthResponse = await authorizationServer.revoke(req);\n    return handleExpressResponse(res, oauthResponse);\n  } catch (e) {\n    handleExpressError(e, res);\n    return;\n  }\n});\n```\n\n## Migration Guide\n\n- [v1 to v2](https://github.com/jasonraimondi/ts-oauth2-server/releases/tag/v2.0.0)\n- [v2 to v3](https://jasonraimondi.github.io/ts-oauth2-server/migration/v2_to_v3.html) \n\n## Thanks\n\nThis project is inspired by the [PHP League's OAuth2 Server](https://oauth2.thephpleague.com/). Check out the [PHP\n League's other packages](https://thephpleague.com/#packages) for some other great PHP projects.\n\n## Star History\n\n[![Star History Chart](https://api.star-history.com/svg?repos=jasonraimondi/ts-oauth2-server&type=Timeline)](https://star-history.com/#jasonraimondi/ts-oauth2-server&Timeline)\n","readmeFilename":"README.md"}