diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..e1989490 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,59 @@ +--- +name: πŸ› Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + + + + +**Specify your setup** + +- Operating System: +- Node version: +- npm version: +- version of @node-oauth/oauth2-server +- which [OAuth2 workflow](https://datatracker.ietf.org/doc/html/rfc6749.html#section-1.3): +- at [which workflow step](https://datatracker.ietf.org/doc/html/rfc6749.html#section-1.2) does the error occur: + +**Describe the bug** + +A clear and concise description of what the bug is. + +**To Reproduce** + +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +Alternatively, please add a link to a GitHub repo +that reproduces the error/s. + +**Expected behavior** + +A clear and concise description of what you expected to happen. + +**Screenshots** + +If applicable, add screenshots to help explain your problem. + +**Additional context** + +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000..1fbc07bc --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,19 @@ +--- +blank_issues_enabled: false +contact_links: + - + about: Propose new features + name: πŸ’‘ Feature requests + url: https://github.com/node-oauth/node-oauth2-server/discussions/categories/ideas + - + about: Ask a question or for help + name: ❓ Question + url: https://github.com/node-oauth/node-oauth2-server/discussions/categories/q-a + - + about: Chat on our discord + name: πŸ—― Chat + url: https://discord.gg/4HTUAcTvqV + - + about: Read the documentation + name: πŸ“œ Documentation + url: https://node-oauthoauth2-server.readthedocs.io/en/latest/ diff --git a/.readthedocs.yml b/.readthedocs.yml new file mode 100644 index 00000000..d9ee7902 --- /dev/null +++ b/.readthedocs.yml @@ -0,0 +1,22 @@ +# .readthedocs.yaml +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +# Set the version of Python and other tools you might need +build: + os: ubuntu-22.04 + tools: + python: "3.11" + +# Build documentation in the docs/ directory with Sphinx +sphinx: + configuration: docs/conf.py + +# We recommend specifying your dependencies to enable reproducible builds: +# https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html +# python: +# install: +# - requirements: docs/requirements.txt \ No newline at end of file diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..92695c9f --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,128 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +into [at] jankuester [dot] com. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. diff --git a/docs/api/errors/access-denied-error.rst b/docs/api/errors/access-denied-error.rst index e561c5b0..11e61ec9 100644 --- a/docs/api/errors/access-denied-error.rst +++ b/docs/api/errors/access-denied-error.rst @@ -6,7 +6,7 @@ The resource owner or authorization server denied the request. See :rfc:`Section :: - const AccessDeniedError = require('oauth2-server/lib/errors/access-denied-error'); + const AccessDeniedError = require('@node-oauth/oauth2-server/lib/errors/access-denied-error'); -------- diff --git a/docs/api/errors/insufficient-scope-error.rst b/docs/api/errors/insufficient-scope-error.rst index be3539de..b19e2acb 100644 --- a/docs/api/errors/insufficient-scope-error.rst +++ b/docs/api/errors/insufficient-scope-error.rst @@ -6,7 +6,7 @@ The request requires higher privileges than provided by the access token. See :r :: - const InsufficientScopeError = require('oauth2-server/lib/errors/insufficient-scope-error'); + const InsufficientScopeError = require('@node-oauth/oauth2-server/lib/errors/insufficient-scope-error'); -------- diff --git a/docs/api/errors/invalid-argument-error.rst b/docs/api/errors/invalid-argument-error.rst index 650e1d9f..11b554eb 100644 --- a/docs/api/errors/invalid-argument-error.rst +++ b/docs/api/errors/invalid-argument-error.rst @@ -6,7 +6,7 @@ An invalid argument was encountered. :: - const InvalidArgumentError = require('oauth2-server/lib/errors/invalid-argument-error'); + const InvalidArgumentError = require('@node-oauth/oauth2-server/lib/errors/invalid-argument-error'); .. note:: This error indicates that the module is used incorrectly (i.e., there is a programming error) and should never be seen because of external errors (like invalid data sent by a client). diff --git a/docs/api/errors/invalid-client-error.rst b/docs/api/errors/invalid-client-error.rst index d25a4934..5ddd0a40 100644 --- a/docs/api/errors/invalid-client-error.rst +++ b/docs/api/errors/invalid-client-error.rst @@ -6,7 +6,7 @@ Client authentication failed (e.g., unknown client, no client authentication inc :: - const InvalidClientError = require('oauth2-server/lib/errors/invalid-client-error'); + const InvalidClientError = require('@node-oauth/oauth2-server/lib/errors/invalid-client-error'); -------- diff --git a/docs/api/errors/invalid-grant-error.rst b/docs/api/errors/invalid-grant-error.rst index 8f2a9ba2..79317149 100644 --- a/docs/api/errors/invalid-grant-error.rst +++ b/docs/api/errors/invalid-grant-error.rst @@ -6,7 +6,7 @@ The provided authorization grant (e.g., authorization code, resource owner crede :: - const InvalidGrantError = require('oauth2-server/lib/errors/invalid-grant-error'); + const InvalidGrantError = require('@node-oauth/oauth2-server/lib/errors/invalid-grant-error'); -------- diff --git a/docs/api/errors/invalid-request-error.rst b/docs/api/errors/invalid-request-error.rst index 119ab40e..bbb38c44 100644 --- a/docs/api/errors/invalid-request-error.rst +++ b/docs/api/errors/invalid-request-error.rst @@ -6,7 +6,7 @@ The request is missing a required parameter, includes an invalid parameter value :: - const InvalidRequestError = require('oauth2-server/lib/errors/invalid-request-error'); + const InvalidRequestError = require('@node-oauth/oauth2-server/lib/errors/invalid-request-error'); -------- diff --git a/docs/api/errors/invalid-scope-error.rst b/docs/api/errors/invalid-scope-error.rst index 801930f9..01c70d26 100644 --- a/docs/api/errors/invalid-scope-error.rst +++ b/docs/api/errors/invalid-scope-error.rst @@ -6,7 +6,7 @@ The requested scope is invalid, unknown, or malformed. See :rfc:`Section 4.1.2.1 :: - const InvalidScopeError = require('oauth2-server/lib/errors/invalid-scope-error'); + const InvalidScopeError = require('@node-oauth/oauth2-server/lib/errors/invalid-scope-error'); -------- diff --git a/docs/api/errors/invalid-token-error.rst b/docs/api/errors/invalid-token-error.rst index 21ffad8f..fc0da035 100644 --- a/docs/api/errors/invalid-token-error.rst +++ b/docs/api/errors/invalid-token-error.rst @@ -6,7 +6,7 @@ The access token provided is expired, revoked, malformed, or invalid for other r :: - const InvalidTokenError = require('oauth2-server/lib/errors/invalid-token-error'); + const InvalidTokenError = require('@node-oauth/oauth2-server/lib/errors/invalid-token-error'); -------- diff --git a/docs/api/errors/oauth-error.rst b/docs/api/errors/oauth-error.rst index c7f1d861..83be4659 100644 --- a/docs/api/errors/oauth-error.rst +++ b/docs/api/errors/oauth-error.rst @@ -6,7 +6,7 @@ Base class for all errors returned by this module. :: - const OAuthError = require('oauth2-server/lib/errors/oauth-error'); + const OAuthError = require('@node-oauth/oauth2-server/lib/errors/oauth-error'); -------- diff --git a/docs/api/errors/server-error.rst b/docs/api/errors/server-error.rst index 13f436ed..7a2dcf90 100644 --- a/docs/api/errors/server-error.rst +++ b/docs/api/errors/server-error.rst @@ -6,7 +6,7 @@ The authorization server encountered an unexpected condition that prevented it f :: - const ServerError = require('oauth2-server/lib/errors/server-error'); + const ServerError = require('@node-oauth/oauth2-server/lib/errors/server-error'); ``ServerError`` is used to wrap unknown exceptions encountered during request processing. diff --git a/docs/api/errors/unauthorized-client-error.rst b/docs/api/errors/unauthorized-client-error.rst index d04cb080..9d104cac 100644 --- a/docs/api/errors/unauthorized-client-error.rst +++ b/docs/api/errors/unauthorized-client-error.rst @@ -6,7 +6,7 @@ The authenticated client is not authorized to use this authorization grant type. :: - const UnauthorizedClientError = require('oauth2-server/lib/errors/unauthorized-client-error'); + const UnauthorizedClientError = require('@node-oauth/oauth2-server/lib/errors/unauthorized-client-error'); -------- diff --git a/docs/api/errors/unauthorized-request-error.rst b/docs/api/errors/unauthorized-request-error.rst index 495f5f8c..9ed24675 100644 --- a/docs/api/errors/unauthorized-request-error.rst +++ b/docs/api/errors/unauthorized-request-error.rst @@ -6,7 +6,7 @@ The request lacked any authentication information or the client attempted to use :: - const UnauthorizedRequestError = require('oauth2-server/lib/errors/unauthorized-request-error'); + const UnauthorizedRequestError = require('@node-oauth/oauth2-server/lib/errors/unauthorized-request-error'); According to :rfc:`Section 3.1 of RFC 6750 <6750#section-3.1>` you should just fail the request with ``401 Unauthorized`` and not send any error information in the body if this error occurs: diff --git a/docs/api/errors/unsupported-grant-type-error.rst b/docs/api/errors/unsupported-grant-type-error.rst index d2fe49f7..1e812ed7 100644 --- a/docs/api/errors/unsupported-grant-type-error.rst +++ b/docs/api/errors/unsupported-grant-type-error.rst @@ -6,7 +6,7 @@ The authorization grant type is not supported by the authorization server. See : :: - const UnsupportedGrantTypeError = require('oauth2-server/lib/errors/unsupported-grant-type-error'); + const UnsupportedGrantTypeError = require('@node-oauth/oauth2-server/lib/errors/unsupported-grant-type-error'); -------- diff --git a/docs/api/errors/unsupported-response-type-error.rst b/docs/api/errors/unsupported-response-type-error.rst index 28974eba..c9ee0fd3 100644 --- a/docs/api/errors/unsupported-response-type-error.rst +++ b/docs/api/errors/unsupported-response-type-error.rst @@ -6,7 +6,7 @@ The authorization server does not supported obtaining an authorization code usin :: - const UnsupportedResponseTypeError = require('oauth2-server/lib/errors/unsupported-response-type-error'); + const UnsupportedResponseTypeError = require('@node-oauth/oauth2-server/lib/errors/unsupported-response-type-error'); -------- diff --git a/docs/api/oauth2-server.rst b/docs/api/oauth2-server.rst index 48acf538..38cf6bd1 100644 --- a/docs/api/oauth2-server.rst +++ b/docs/api/oauth2-server.rst @@ -6,7 +6,7 @@ Represents an OAuth2 server instance. :: - const OAuth2Server = require('oauth2-server'); + const OAuth2Server = require('@node-oauth/oauth2-server'); -------- @@ -94,7 +94,7 @@ Possible errors include but are not limited to: :doc:`/api/errors/unauthorized-request-error`: The protected resource request failed authentication. -The returned ``Promise`` **must** be ignored if ``callback`` is used. +**Versions <=4.x note:** The returned ``Promise`` **must** be ignored if ``callback`` is used. **Remarks:** @@ -139,7 +139,7 @@ Authorizes a token request. +-----------------------------------------+-----------------+-----------------------------------------------------------------------------+ | [options={}] | Object | Handler options. | +-----------------------------------------+-----------------+-----------------------------------------------------------------------------+ -| [options.authenticateHandler=undefined] | Object | The authenticate handler (see remarks section). | +| [options.authenticateHandler=undefined] | Object | The authenticate handler (see remarks section below). | +-----------------------------------------+-----------------+-----------------------------------------------------------------------------+ | [options.allowEmptyState=false] | Boolean | Allow clients to specify an empty ``state``. | +-----------------------------------------+-----------------+-----------------------------------------------------------------------------+ @@ -158,7 +158,7 @@ Possible errors include but are not limited to: :doc:`/api/errors/access-denied-error` The resource owner denied the access request (i.e. ``request.query.allow`` was ``'false'``). -The returned ``Promise`` **must** be ignored if ``callback`` is used. +**Versions <=4.x note:** The returned ``Promise`` **must** be ignored if ``callback`` is used. **Remarks:** @@ -252,7 +252,7 @@ Possible errors include but are not limited to: :doc:`/api/errors/invalid-grant-error`: The access token request was invalid or not authorized. -The returned ``Promise`` **must** be ignored if ``callback`` is used. +**Versions <=4.x note:** The returned ``Promise`` **must** be ignored if ``callback`` is used. **Remarks:** diff --git a/docs/api/request.rst b/docs/api/request.rst index b8f8963a..7d5f4cad 100644 --- a/docs/api/request.rst +++ b/docs/api/request.rst @@ -6,7 +6,7 @@ Represents an incoming HTTP request. :: - const Request = require('oauth2-server').Request; + const Request = require('@node-oauth/oauth2-server').Request; -------- @@ -50,7 +50,7 @@ To convert `Express' request`_ to a ``Request`` simply pass ``req`` as ``options :: function(req, res, next) { - var request = new Request(req); + let request = new Request(req); // ... } diff --git a/docs/api/response.rst b/docs/api/response.rst index 48cc36dc..2c5d3326 100644 --- a/docs/api/response.rst +++ b/docs/api/response.rst @@ -6,7 +6,7 @@ Represents an outgoing HTTP response. :: - const Response = require('oauth2-server').Response; + const Response = require('@node-oauth/oauth2-server').Response; -------- @@ -46,7 +46,7 @@ To convert `Express' response`_ to a ``Response`` simply pass ``res`` as ``optio :: function(req, res, next) { - var response = new Response(res); + let response = new Response(res); // ... } diff --git a/docs/conf.py b/docs/conf.py index d9aae790..a621e906 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# oauth2-server documentation build configuration file, created by +# @node-oauth/oauth2-server documentation build configuration file, created by # sphinx-quickstart on Thu Nov 17 16:47:05 2016. # # This file is execfile()d with the current directory set to its containing dir. @@ -272,5 +272,5 @@ highlight_language = 'js' def setup(app): - app.add_stylesheet('custom.css') + app.add_css_file('custom.css') diff --git a/docs/docs/adapters.rst b/docs/docs/adapters.rst index c302d34e..bec9ce6f 100644 --- a/docs/docs/adapters.rst +++ b/docs/docs/adapters.rst @@ -7,9 +7,9 @@ The *oauth2-server* module is typically not used directly but through one of the .. framework-agnostic but there are several officially supported adapters available for popular HTTP server frameworks such as Express_ and Koa_. - express-oauth-server_ for Express_ -- koa-oauth-server_ for Koa_ +- koa-oauth-server_ for Koa_ (outdated, maintainer wanted!) -.. _express-oauth-server: https://npmjs.org/package/express-oauth-server +.. _express-oauth-server: https://www.npmjs.com/package/@node-oauth/express-oauth-server .. _Express: https://npmjs.org/package/express .. _koa-oauth-server: https://npmjs.org/package/koa-oauth-server .. _Koa: https://npmjs.org/package/koa diff --git a/docs/docs/getting-started.rst b/docs/docs/getting-started.rst index ff2c1156..1a55f18c 100644 --- a/docs/docs/getting-started.rst +++ b/docs/docs/getting-started.rst @@ -9,16 +9,16 @@ Installation oauth2-server_ is available via npm_. -.. _oauth2-server: https://npmjs.org/package/oauth2-server +.. _oauth2-server: https://www.npmjs.com/package/@node-oauth/oauth2-server .. _npm: https://npmjs.org .. code-block:: sh - $ npm install oauth2-server + $ npm install @node-oauth/oauth2-server -.. note:: The *oauth2-server* module is framework-agnostic but there are several officially supported adapters available for popular HTTP server frameworks such as Express_ and Koa_. If you're using one of those frameworks it is strongly recommended to use the respective adapter module instead of rolling your own. +.. note:: The *oauth2-server* module is framework-agnostic but there are several officially supported adapters available for popular HTTP server frameworks such as Express_ and Koa_ (maintainer wanted!). If you're using one of those frameworks it is strongly recommended to use the respective adapter module instead of rolling your own. -.. _Express: https://npmjs.org/package/express-oauth-server +.. _Express: https://www.npmjs.com/package/@node-oauth/express-oauth-server .. _Koa: https://npmjs.org/package/koa-oauth-server @@ -29,12 +29,14 @@ Features - Supports :ref:`authorization code `, :ref:`client credentials `, :ref:`refresh token ` and :ref:`password ` grant, as well as :ref:`extension grants `, with scopes. - Can be used with *promises*, *Node-style callbacks*, *ES6 generators* and *async*/*await* (using Babel_). +- From version 5.0.0 fully native async/await implemented - Fully :rfc:`6749` and :rfc:`6750` compliant. +- Supports PKCE (:rfc:`7636`) - Implicitly supports any form of storage, e.g. *PostgreSQL*, *MySQL*, *MongoDB*, *Redis*, etc. - Complete `test suite`_. .. _Babel: https://babeljs.io -.. _test suite: https://github.com/oauthjs/node-oauth2-server/tree/master/test +.. _test suite: https://github.com/node-oauth/node-oauth2-server/tree/master/test .. _quick-start: @@ -46,7 +48,7 @@ Quick Start :: - const OAuth2Server = require('oauth2-server'); + const OAuth2Server = require('@node-oauth/oauth2-server'); const oauth = new OAuth2Server({ model: require('./model') @@ -78,7 +80,7 @@ Quick Start :: - const AccessDeniedError = require('oauth2-server/lib/errors/access-denied-error'); + const AccessDeniedError = require('@node-oauth/oauth2-server/lib/errors/access-denied-error'); oauth.authorize(request, response) .then((code) => { diff --git a/docs/index.rst b/docs/index.rst index 4a7c3415..f88c4b47 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,13 +1,13 @@ -=============== - oauth2-server -=============== +========================== + @node-oauth/oauth2-server +========================== -oauth2-server_ is a complete, compliant and well tested module for implementing an OAuth2 server in Node.js_. The project is `hosted on GitHub`_ and the included test suite is automatically `run on Travis CI`_. +oauth2-server_ is a complete, compliant and well tested module for implementing an OAuth2 server in Node.js_. The project is `hosted on GitHub`_ and the included test suite is automatically `run on GitHub CI`_. -.. _oauth2-server: https://npmjs.org/package/oauth2-server +.. _oauth2-server: https://www.npmjs.com/package/@node-oauth/oauth2-server .. _Node.js: https://nodejs.org -.. _hosted on GitHub: https://github.com/oauthjs/node-oauth2-server -.. _run on Travis CI: https://travis-ci.org/oauthjs/node-oauth2-server +.. _hosted on GitHub: https://github.com/node-oauth/node-oauth2-server +.. _run on GitHub CI: https://github.com/node-oauth/node-oauth2-server/actions :ref:`installation` @@ -17,7 +17,7 @@ Example Usage :: - const OAuth2Server = require('oauth2-server'); + const OAuth2Server = require('@node-oauth/oauth2-server'); const Request = OAuth2Server.Request; const Response = OAuth2Server.Response; @@ -84,5 +84,6 @@ See the :doc:`/model/spec` of what is required from the model passed to :doc:`/a :hidden: misc/extension-grants + misc/pkce misc/migrating-v2-to-v3 diff --git a/docs/misc/extension-grants.rst b/docs/misc/extension-grants.rst index 1fbe55a2..4ce22bfd 100644 --- a/docs/misc/extension-grants.rst +++ b/docs/misc/extension-grants.rst @@ -6,7 +6,7 @@ Create a subclass of ``AbstractGrantType`` and create methods `handle` and `save .. code-block:: js - const OAuth2Server = require('oauth2-server'); + const OAuth2Server = require('@node-oauth/oauth2-server'); const AbstractGrantType = OAuth2Server.AbstractGrantType; const InvalidArgumentError = OAuth2Server.InvalidArgumentError; const InvalidRequestError = OAuth2Server.InvalidRequestError; diff --git a/docs/misc/migrating-v2-to-v3.rst b/docs/misc/migrating-v2-to-v3.rst index 9d03c8f2..8d1290c0 100644 --- a/docs/misc/migrating-v2-to-v3.rst +++ b/docs/misc/migrating-v2-to-v3.rst @@ -11,7 +11,7 @@ Middlewares The naming of the exposed middlewares has changed to match the OAuth2 _RFC_ more closely. Please refer to the table below: +-------------------+------------------------------------------------+ -| oauth2-server 2.x | oauth2-server 3.x | +| oauth2-server 2.x | @node-oauth/oauth2-server 3.x | +===================+================================================+ | authorise | authenticate | +-------------------+------------------------------------------------+ diff --git a/docs/misc/pkce.rst b/docs/misc/pkce.rst new file mode 100644 index 00000000..cb52f1e7 --- /dev/null +++ b/docs/misc/pkce.rst @@ -0,0 +1,141 @@ +================ + PKCE Support +================ + +Starting with release 4.3.0_ this library supports PKCE (Proof Key for Code Exchange by OAuth Public Clients) as +defined in :rfc:`7636`. + +.. _4.3.0: https://github.com/node-oauth/node-oauth2-server/releases/tag/v4.3.0 + +The PKCE integrates only with the :ref:`authorization code `. The abstract workflow looks like +the following: + +:: + + +-------------------+ + | Authz Server | + +--------+ | +---------------+ | + | |--(A)- Authorization Request ---->| | | + | | + t(code_verifier), t_m | | Authorization | | + | | | | Endpoint | | + | |<-(b)---- Authorization Code -----| | | + | | | +---------------+ | + | Client | | | + | | | +---------------+ | + | |--(C)-- Access Token Request ---->| | | + | | + code_verifier | | Token | | + | | | | Endpoint | | + | |<-(d)------ Access Token ---------| | | + +--------+ | +---------------+ | + +-------------------+ + + Figure 2: Abstract Protocol Flow + +See :rfc:`Section 1 of RFC 7636 <7636#section-1.1>`. + +1. Authorization request +======================== + +.. _PKCE#authorizationRequest: + + A. The client creates and records a secret named the "code_verifier" and derives a transformed version "t(code_verifier)" (referred to as the "code_challenge"), which is sent in the OAuth 2.0 Authorization Request along with the transformation method "t_m". + +The following shows an example of how a client could generate a `code_challenge`` and +``code_challenge_method`` for the authorizazion request. + +:: + + const base64URLEncode = str => str.toString('base64') + .replace(/\+/g, '-') + .replace(/\//g, '_') + .replace(/=/g, '') + + // This is the code_verifier, which is INITIALLY KEPT SECRET on the client + // and which is later passed as request param to the token endpoint. + // DO NOT SEND this with the authorization request! + const codeVerifier = base64URLEncode(crypto.randomBytes(32)) + + // This is the hashed version of the verifier, which is sent to the authorization endpoint. + // This is named t(code_verifier) in the above workflow + // Send this with the authorization request! + const codeChallenge = base64URLEncode(crypto.createHash('sha256').update(codeVerifier).digest()) + + // This is the name of the code challenge method + // This is named t_m in the above workflow + // Send this with the authorization request! + const codeChallengeMethod = 'S256' + + // add these to the request that is fired from the client + +In this project the authorize endpoint calls OAuth2Server.prototype.authorize which itself uses AuthorizeHandler. +If your Request body contains code_challenge and code_challenge_method then PKCE is active. + +:: + + const server = new OAuth2Server({ model }) + + // this could be added to express or other middleware + const authorizeEndpoint = function (req, res, next) { + const request = new Request(req) + req.query.code_challenge // the codeChallenge value + req.query.code_challenge_method // 'S256' + + server.authorize(request, response, options) + .then(function (code) { + // add code to response, code should not contain + // code_challenge or code_challenge_method + }) + .catch(function (err) { + // handle error condition + }) + } + +2. Authorization response +========================= + +.. _PKCE#authorizationResponse: + + B. The Authorization Endpoint responds as usual but records "t(code_verifier)" and the transformation method. + +The ``AuthorizeHandler.handle`` saves code challenge and code challenge method automatically via ``model.saveAuthorizationCode``. +Note that this calls your model with additional properties ``code.codeChallenge`` and ``code.codeChallengeMethod``. + + +3. Access Token Request +======================= + +.. _PKCE#accessTokenRequest: + + C. The client then sends the authorization code in the Access Token Request as usual but includes the "code_verifier" secret generated at (A). + +This is usually done in your token endpoint, that uses ``OAuth2Server.token``. + +:: + + const server = new OAuth2Server({ model }) + + // ...authorizeEndpoint + + // this could be added to express or other middleware + const tokenEndpoint = function (req, res, next) { + const request = new Request(req) + request.body.code_verifier // the non-hashed code verifier + server.token(request, response, options) + .then(function (code) { + // add code to response, code should contain + }) + .catch(function (err) { + // handle error condition + }) + } + +Note that your client should have kept ``code_verifier`` a secret until this step and now includes it as param for the token endpoint call. + + + D. The authorization server transforms "code_verifier" and compares it to "t(code_verifier)" from (B). Access is denied if they are not equal. + +This will call ``model.getAuthorizationCode`` to load the code. +The loaded code has to contain ``codeChallenge`` and ``codeChallengeMethod``. +If ``model.saveAuthorizationCode`` did not cover these values when saving the code then this step will deny the request. + +See :ref:`Model#saveAuthorizationCode` and :ref:`Model#getAuthorizationCode` diff --git a/docs/model/spec.rst b/docs/model/spec.rst index 953c2811..b622d9c7 100644 --- a/docs/model/spec.rst +++ b/docs/model/spec.rst @@ -2,7 +2,9 @@ Model Specification ===================== -Each model function supports *promises*, *Node-style callbacks*, *ES6 generators* and *async*/*await* (using Babel_). Note that promise support implies support for returning plain values where asynchronism is not required. +**Version>=5.x:** Callback support has been removed! Each model function supports either sync or async (``Promise`` or ``async function``) return values. + +**Version <=4.x:** Each model function supports *promises*, *Node-style callbacks*, *ES6 generators* and *async*/*await* (using Babel_). Note that promise support implies support for returning plain values where asynchronism is not required. .. _Babel: https://babeljs.io @@ -14,9 +16,9 @@ Each model function supports *promises*, *Node-style callbacks*, *ES6 generators return new Promise('works!'); }, - // Or, calling a Node-style callback. - getAuthorizationCode: function(done) { - done(null, 'works!'); + // Or sync-style values + getAuthorizationCode: function() { + return 'works!' }, // Or, using generators. @@ -32,7 +34,7 @@ Each model function supports *promises*, *Node-style callbacks*, *ES6 generators } }; - const OAuth2Server = require('oauth2-server'); + const OAuth2Server = require('@node-oauth/oauth2-server'); let oauth = new OAuth2Server({model: model}); Code examples on this page use *promises*. @@ -41,7 +43,7 @@ Code examples on this page use *promises*. .. _Model#generateAccessToken: -``generateAccessToken(client, user, scope, [callback])`` +``generateAccessToken(client, user, scope)`` ======================================================== Invoked to generate a new access token. @@ -66,8 +68,6 @@ This model function is **optional**. If not implemented, a default handler is us +------------+----------+---------------------------------------------------------------------+ | scope | String | The scopes associated with the access token. Can be ``null``. | +------------+----------+---------------------------------------------------------------------+ -| [callback] | Function | Node-style callback to be used instead of the returned ``Promise``. | -+------------+----------+---------------------------------------------------------------------+ **Return value:** @@ -85,7 +85,7 @@ A ``String`` to be used as access token. .. _Model#generateRefreshToken: -``generateRefreshToken(client, user, scope, [callback])`` +``generateRefreshToken(client, user, scope)`` ========================================================= Invoked to generate a new refresh token. @@ -109,8 +109,6 @@ This model function is **optional**. If not implemented, a default handler is us +------------+----------+---------------------------------------------------------------------+ | scope | String | The scopes associated with the refresh token. Can be ``null``. | +------------+----------+---------------------------------------------------------------------+ -| [callback] | Function | Node-style callback to be used instead of the returned ``Promise``. | -+------------+----------+---------------------------------------------------------------------+ **Return value:** @@ -128,7 +126,7 @@ A ``String`` to be used as refresh token. .. _Model#generateAuthorizationCode: -``generateAuthorizationCode(client, user, scope, [callback])`` +``generateAuthorizationCode(client, user, scope)`` ========================================= Invoked to generate a new authorization code. @@ -150,8 +148,6 @@ This model function is **optional**. If not implemented, a default handler is us +------------+----------+---------------------------------------------------------------------+ | scope | String | The scopes associated with the authorization code. Can be ``null``. | +------------+----------+---------------------------------------------------------------------+ -| [callback] | Function | Node-style callback to be used instead of the returned ``Promise``. | -+------------+----------+---------------------------------------------------------------------+ **Return value:** @@ -163,7 +159,7 @@ A ``String`` to be used as authorization code. .. _Model#getAccessToken: -``getAccessToken(accessToken, [callback])`` +``getAccessToken(accessToken)`` =========================================== Invoked to retrieve an existing access token previously saved through :ref:`Model#saveToken() `. @@ -181,8 +177,6 @@ This model function is **required** if :ref:`OAuth2Server#authenticate() `. @@ -255,8 +249,6 @@ This model function is **required** if the ``refresh_token`` grant is used. +==============+==========+=====================================================================+ | refreshToken | String | The access token to retrieve. | +--------------+----------+---------------------------------------------------------------------+ -| [callback] | Function | Node-style callback to be used instead of the returned ``Promise``. | -+--------------+----------+---------------------------------------------------------------------+ **Return value:** @@ -311,7 +303,7 @@ An ``Object`` representing the refresh token and associated data. .. _Model#getAuthorizationCode: -``getAuthorizationCode(authorizationCode, [callback])`` +``getAuthorizationCode(authorizationCode)`` ======================================================= Invoked to retrieve an existing authorization code previously saved through :ref:`Model#saveAuthorizationCode() `. @@ -329,8 +321,6 @@ This model function is **required** if the ``authorization_code`` grant is used. +===================+==========+=====================================================================+ | authorizationCode | String | The authorization code to retrieve. | +-------------------+----------+---------------------------------------------------------------------+ -| [callback] | Function | Node-style callback to be used instead of the returned ``Promise``. | -+-------------------+----------+---------------------------------------------------------------------+ **Return value:** @@ -388,7 +378,7 @@ An ``Object`` representing the authorization code and associated data. .. _Model#getClient: -``getClient(clientId, clientSecret, [callback])`` +``getClient(clientId, clientSecret)`` ================================================= Invoked to retrieve a client using a client id or a client id/client secret combination, depending on the grant type. @@ -411,8 +401,6 @@ This model function is **required** for all grant types. +--------------+----------+---------------------------------------------------------------------+ | clientSecret | String | The client secret of the client to retrieve. Can be ``null``. | +--------------+----------+---------------------------------------------------------------------+ -| [callback] | Function | Node-style callback to be used instead of the returned ``Promise``. | -+--------------+----------+---------------------------------------------------------------------+ **Return value:** @@ -460,7 +448,7 @@ The return value (``client``) can carry additional properties that will be ignor .. _Model#getUser: -``getUser(username, password, [callback])`` +``getUser(username, password)`` =========================================== Invoked to retrieve a user using a username/password combination. @@ -480,8 +468,6 @@ This model function is **required** if the ``password`` grant is used. +------------+----------+---------------------------------------------------------------------+ | password | String | The user's password. | +------------+----------+---------------------------------------------------------------------+ -| [callback] | Function | Node-style callback to be used instead of the returned ``Promise``. | -+------------+----------+---------------------------------------------------------------------+ **Return value:** @@ -500,7 +486,7 @@ An ``Object`` representing the user, or a falsy value if no such user could be f .. _Model#getUserFromClient: -``getUserFromClient(client, [callback])`` +``getUserFromClient(client)`` ========================================= Invoked to retrieve the user associated with the specified client. @@ -520,8 +506,6 @@ This model function is **required** if the ``client_credentials`` grant is used. +------------+----------+---------------------------------------------------------------------+ | client.id | String | A unique string identifying the client. | +------------+----------+---------------------------------------------------------------------+ -| [callback] | Function | Node-style callback to be used instead of the returned ``Promise``. | -+------------+----------+---------------------------------------------------------------------+ **Return value:** @@ -542,7 +526,7 @@ An ``Object`` representing the user, or a falsy value if the client does not hav .. _Model#saveToken: -``saveToken(token, client, user, [callback])`` +``saveToken(token, client, user)`` ============================================== Invoked to save an access token and optionally a refresh token, depending on the grant type. @@ -577,8 +561,6 @@ This model function is **required** for all grant types. +-------------------------------+----------+---------------------------------------------------------------------+ | user | Object | The user associated with the token(s). | +-------------------------------+----------+---------------------------------------------------------------------+ -| [callback] | Function | Node-style callback to be used instead of the returned ``Promise``. | -+-------------------------------+----------+---------------------------------------------------------------------+ **Return value:** @@ -650,7 +632,7 @@ If the ``allowExtendedTokenAttributes`` server option is enabled (see :ref:`OAut .. _Model#saveAuthorizationCode: -``saveAuthorizationCode(code, client, user, [callback])`` +``saveAuthorizationCode(code, client, user)`` ========================================================= Invoked to save an authorization code. @@ -663,27 +645,29 @@ This model function is **required** if the ``authorization_code`` grant is used. **Arguments:** -+------------------------+----------+---------------------------------------------------------------------+ -| Name | Type | Description | -+========================+==========+=====================================================================+ -| code | Object | The code to be saved. | -+------------------------+----------+---------------------------------------------------------------------+ -| code.authorizationCode | String | The authorization code to be saved. | -+------------------------+----------+---------------------------------------------------------------------+ -| code.expiresAt | Date | The expiry time of the authorization code. | -+------------------------+----------+---------------------------------------------------------------------+ -| code.redirectUri | String | The redirect URI associated with the authorization code. | -+------------------------+----------+---------------------------------------------------------------------+ -| [code.scope] | String | The authorized scope of the authorization code. | -+------------------------+----------+---------------------------------------------------------------------+ -| client | Object | The client associated with the authorization code. | -+------------------------+----------+---------------------------------------------------------------------+ -| user | Object | The user associated with the authorization code. | -+------------------------+----------+---------------------------------------------------------------------+ -| [callback] | Function | Node-style callback to be used instead of the returned ``Promise``. | -+------------------------+----------+---------------------------------------------------------------------+ - -.. todo:: Is ``code.scope`` really optional? ++----------------------------+----------+---------------------------------------------------------------------+ +| Name | Type | Description | ++----------------------------+----------+---------------------------------------------------------------------+ +| code | Object | The code to be saved. | ++----------------------------+----------+---------------------------------------------------------------------+ +| code.authorizationCode | String | The authorization code to be saved. | ++----------------------------+----------+---------------------------------------------------------------------+ +| code.expiresAt | Date | The expiry time of the authorization code. | ++----------------------------+----------+---------------------------------------------------------------------+ +| code.redirectUri | String | The redirect URI associated with the authorization code. | ++----------------------------+----------+---------------------------------------------------------------------+ +| [code.scope] | String | The authorized scope of the authorization code. | ++----------------------------+----------+---------------------------------------------------------------------+ +| [code.codeChallenge] | String | The code challenge; hash or plain. Only present in PKCE requests. | ++----------------------------+----------+---------------------------------------------------------------------+ +| [code.codeChallengeMethod] | String | One of 'plain' or 'S256'. Only present in PKCE requests. | ++----------------------------+----------+---------------------------------------------------------------------+ +| client | Object | The client associated with the authorization code. | ++----------------------------+----------+---------------------------------------------------------------------+ +| user | Object | The user associated with the authorization code. | ++----------------------------+----------+---------------------------------------------------------------------+ + +For PKCE requests, see :ref:`PKCE#authorizationRequest`. **Return value:** @@ -742,7 +726,7 @@ An ``Object`` representing the authorization code and associated data. .. _Model#revokeToken: -``revokeToken(token, [callback])`` +``revokeToken(token)`` ================================== Invoked to revoke a refresh token. @@ -772,8 +756,6 @@ This model function is **required** if the ``refresh_token`` grant is used. +-------------------------------+----------+---------------------------------------------------------------------+ | token.user | Object | The user associated with the refresh token. | +-------------------------------+----------+---------------------------------------------------------------------+ -| [callback] | Function | Node-style callback to be used instead of the returned ``Promise``. | -+-------------------------------+----------+---------------------------------------------------------------------+ **Return value:** @@ -797,7 +779,7 @@ Return ``true`` if the revocation was successful or ``false`` if the refresh tok .. _Model#revokeAuthorizationCode: -``revokeAuthorizationCode(code, [callback])`` +``revokeAuthorizationCode(code)`` ============================================= Invoked to revoke an authorization code. @@ -829,8 +811,6 @@ This model function is **required** if the ``authorization_code`` grant is used. +--------------------+----------+---------------------------------------------------------------------+ | code.user | Object | The user associated with the authorization code. | +--------------------+----------+---------------------------------------------------------------------+ -| [callback] | Function | Node-style callback to be used instead of the returned ``Promise``. | -+--------------------+----------+---------------------------------------------------------------------+ **Return value:** @@ -854,7 +834,7 @@ Return ``true`` if the revocation was successful or ``false`` if the authorizati .. _Model#validateScope: -``validateScope(user, client, scope, [callback])`` +``validateScope(user, client, scope)`` ================================================== Invoked to check if the requested ``scope`` is valid for a particular ``client``/``user`` combination. @@ -880,8 +860,6 @@ This model function is **optional**. If not implemented, any scope is accepted. +------------+----------+---------------------------------------------------------------------+ | scope | String | The scopes to validate. | +------------+----------+---------------------------------------------------------------------+ -| [callback] | Function | Node-style callback to be used instead of the returned ``Promise``. | -+------------+----------+---------------------------------------------------------------------+ **Return value:** @@ -929,12 +907,13 @@ Note that the example above will still reject completely invalid scopes, since ` .. _Model#verifyScope: -``verifyScope(accessToken, scope, [callback])`` +``verifyScope(accessToken, scope)`` =============================================== Invoked during request authentication to check if the provided access token was authorized the requested scopes. -This model function is **required** if scopes are used with :ref:`OAuth2Server#authenticate() `. +This model function is **required** if scopes are used with :ref:`OAuth2Server#authenticate() ` +but it's never called, if you provide your own ``authenticateHandler`` to the options. **Invoked during:** @@ -961,8 +940,6 @@ This model function is **required** if scopes are used with :ref:`OAuth2Server#a +------------------------------+----------+---------------------------------------------------------------------+ | scope | String | The required scopes. | +------------------------------+----------+---------------------------------------------------------------------+ -| [callback] | Function | Node-style callback to be used instead of the returned ``Promise``. | -+------------------------------+----------+---------------------------------------------------------------------+ **Return value:** @@ -989,7 +966,7 @@ Returns ``true`` if the access token passes, ``false`` otherwise. .. _Model#validateRedirectUri: -``validateRedirectUri(redirectUri, client, [callback])`` +``validateRedirectUri(redirectUri, client)`` ================================================================ Invoked to check if the provided ``redirectUri`` is valid for a particular ``client``. diff --git a/docs/npm_conf.py b/docs/npm_conf.py index f86915f5..41b03819 100644 --- a/docs/npm_conf.py +++ b/docs/npm_conf.py @@ -40,10 +40,10 @@ def get_config(): 'name': package['name'], 'version': package['version'], 'short_version': get_short_version(package['version']), - 'organization': 'oauthjs', + 'organization': '@node-oauth', 'copyright_year': get_copyright_year(2016), # TODO: Get authors from package. - 'docs_author': 'Max Truxa', - 'docs_author_email': 'dev@maxtruxa.com' + 'docs_author': 'Node-OAuth Authors', + 'docs_author_email': '' } diff --git a/package.json b/package.json index e95a0ce1..5e1edf11 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ }, "devDependencies": { "chai": "4.3.7", - "eslint": "8.42.0", + "eslint": "8.47.0", "mocha": "10.2.0", "nyc": "15.1.0", "sinon": "15.1.0"

AltStyle γ«γ‚ˆγ£γ¦ε€‰ζ›γ•γ‚ŒγŸγƒšγƒΌγ‚Έ (->γ‚ͺγƒͺγ‚ΈγƒŠγƒ«) /