Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ From here you can run several scripts for development purposes:
```bash
npm run test # runs the tests once
npm run test:coverage # runs the tests including coverage
npm run docs # generates the API docs
npm run docs:api # generates the API docs
```

To work on a new feature or a fix please create a new branch:
Expand Down
136 changes: 91 additions & 45 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,79 +1,125 @@

# @node-oauth/oauth2-server

Complete, compliant and well tested module for implementing an OAuth2 server in [Node.js](https://nodejs.org).

[![Tests](https://github.com/node-oauth/node-oauth2-server/actions/workflows/tests.yml/badge.svg)](https://github.com/node-oauth/node-oauth2-server/actions/workflows/tests.yml)
[![CodeQL Semantic Analysis](https://github.com/node-oauth/node-oauth2-server/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/node-oauth/node-oauth2-server/actions/workflows/codeql-analysis.yml)
[![Documentation Status](https://readthedocs.org/projects/node-oauthoauth2-server/badge/?version=latest)](https://node-oauthoauth2-server.readthedocs.io/en/latest/?badge=latest)
[![Project Status: Active – The project has reached a stable, usable state and is being actively developed.](https://www.repostatus.org/badges/latest/active.svg)](https://www.repostatus.org/#active)
[![npm Version](https://img.shields.io/npm/v/@node-oauth/oauth2-server?label=version)](https://www.npmjs.com/package/@node-oauth/oauth2-server)
[![npm Downloads/Week](https://img.shields.io/npm/dw/@node-oauth/oauth2-server)](https://www.npmjs.com/package/@node-oauth/oauth2-server)
[![GitHub License](https://img.shields.io/github/license/node-oauth/node-oauth2-server)](https://github.com/node-oauth/node-oauth2-server/blob/master/LICENSE)
[![Tests](https://github.com/node-oauth/node-oauth2-server/actions/workflows/tests.yml/badge.svg)](https://github.com/node-oauth/node-oauth2-server/actions/workflows/tests.yml)
[![Formatted with Biome](https://img.shields.io/badge/Formatted_with-Biome-60a5fa?style=flat&logo=biome)](https://biomejs.dev/)
[![CodeQL Semantic Analysis](https://github.com/node-oauth/node-oauth2-server/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/node-oauth/node-oauth2-server/actions/workflows/codeql-analysis.yml)
[![Deploy VitePress site to Pages](https://github.com/node-oauth/node-oauth2-server/actions/workflows/deploy_docs.yml/badge.svg)](https://github.com/node-oauth/node-oauth2-server/actions/workflows/deploy_docs.yml)

A complete, compliant, and well-tested module for implementing an OAuth 2.0 server in [Node.js](https://nodejs.org).
This project is a continuation of the
abandoned [oauthjs/node-oauth2-server](https://github.com/oauthjs/node-oauth2-server) project.

> [!NOTE]
> 🛡️ If you intend to submit a security issue or vulnerability, please read
> our [security policy](https://github.com/node-oauth/node-oauth2-server/security/policy) first.

## Table of Contents

<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->

- [Features](#features)
- [Installation and Usage](#installation-and-usage)
- [Express and Koa Users](#express-and-koa-users)
- [Installation](#installation)
- [Usage Guide](#usage-guide)
- [Examples](#examples)
- [Migration Notes](#migration-notes)
- [Version 5 Notes](#version-5-notes)
- [Migrating from oauthjs/node-oauth2-server and 3.x](#migrating-from-oauthjsnode-oauth2-server-and-3x)
- [Supported Node.js Versions](#supported-nodejs-versions)
- [Contributing to This Project](#contributing-to-this-project)
- [Reporting Security Vulnerabilities](#reporting-security-vulnerabilities)
- [Development, Issues, and Pull Requests](#development-issues-and-pull-requests)
- [License](#license)

<!-- END doctoc generated TOC please keep comment here to allow auto update -->

## Features

NOTE: This project has been forked from [oauthjs/node-oauth2-server](https://github.com/oauthjs/node-oauth2-server) and is a continuation due to the project appearing to be abandoned. Please see [our issue board](https://github.com/node-oauth/node-oauth2-server/issues) to talk about next steps and the future of this project.
- Supports the `authorization_code`, `client_credentials`, `refresh_token`, and `password` grants, as well as *extension
grants*, with scopes.
- Can be used with *promises*, *ES6 generators*, and *async*/*await*.
- Fully [RFC 6749](https://tools.ietf.org/html/rfc6749.html) and [RFC 6750](https://tools.ietf.org/html/rfc6750.html)
compliant.
- Storage-agnostic: supports any form of storage, including *PostgreSQL*, *MySQL*, *MongoDB*, and *Redis*.
- Supports [PKCE](https://node-oauth.github.io/node-oauth2-server/guide/pkce.html).
- Complete [test suite](https://github.com/node-oauth/node-oauth2-server/tree/master/test).

## Installation
## Installation and Usage

### Express and Koa Users

The `@node-oauth/oauth2-server` module is framework-agnostic.
Most users should refer to our [Express (active, maintained by us)](https://github.com/node-oauth/express-oauth-server)
or
[Koa (not maintained by us)](https://github.com/oauthjs/koa-oauth-server/tree/master/examples) adapters.
These adapters integrate the library with their respective frameworks.

> [!TIP]
> If you're using one of these frameworks, we strongly recommend using the corresponding wrapper module instead of
> writing your own integration.

### Installation

The base installation is intended for users who want to fully customize the integration
of this library into their stack.

```bash
npm install @node-oauth/oauth2-server
```

The `@node-oauth/oauth2-server` module is framework-agnostic but there are several officially supported wrappers available for popular HTTP server frameworks such as [Express](https://www.npmjs.com/package/@node-oauth/express-oauth-server) and [Koa (not maintained by us)](https://npmjs.org/package/koa-oauth-server).
If you're using one of those frameworks it is strongly recommended to use the respective wrapper module instead of rolling your own.

### Usage Guide

## Features
Documentation is available at https://node-oauth.github.io/node-oauth2-server/.

- Supports `authorization_code`, `client_credentials`, `refresh_token` and `password` grant, as well as *extension grants*, with scopes.
- Can be used with *promises*, *ES6 generators* and *async*/*await* (using [Babel](https://babeljs.io)).
- Fully [RFC 6749](https://tools.ietf.org/html/rfc6749.html) and [RFC 6750](https://tools.ietf.org/html/rfc6750.html) compliant.
- Implicitly supports any form of storage, e.g. *PostgreSQL*, *MySQL*, *MongoDB*, *Redis*, etc.
- Support for PKCE
- Complete [test suite](https://github.com/node-oauth/node-oauth2-server/tree/master/test).
Please open an issue if something is confusing or missing in the documentation.

## Documentation
### Examples

Documentation is hosted on Read the Docs. We have multiple versions of the docs available:
More examples can be found in our examples repository: https://github.com/node-oauth/node-oauth2-server-examples

- [stable](https://node-oauthoauth2-server.readthedocs.io/en/master/) (master branch)
- [development](https://node-oauthoauth2-server.readthedocs.io/en/development/) (development branch)
## Migration Notes

Please leave an issue if something is confusing or missing in the docs.
### Version 5 Notes

## Examples
Starting with version `5.x`, we removed support for callbacks.
This version supports only promises and async/await.

Most users should refer to our [Express (active)](https://github.com/node-oauth/express-oauth-server) or
[Koa (not maintained by us)](https://github.com/oauthjs/koa-oauth-server/tree/master/examples) examples.
In this version, we also raised the minimum required Node.js version to 16, as Node.js 14 was deprecated.

More examples can be found here: https://github.com/14gasher/oauth-example
### Migrating from [oauthjs/node-oauth2-server](https://github.com/oauthjs/node-oauth2-server) and 3.x

## Version 5 notes
Version 4.x should not introduce major breaking changes. However, it includes many improvements and fixes that may
be incompatible with specific behavior in version 3.x and earlier.

Beginning with version `5.x` we removed dual support for callbacks and promises.
With this version there is only support for Promises / async/await.
### Supported Node.js Versions

With this version we also bumped the `engine` to Node 16 as 14 is now deprecated.
We aim to maintain compatibility with older Node.js versions wherever possible.
If our minimum supported Node.js version predates the currently supported or LTS releases, this reflects our
commitment to backward compatibility and does not mean that this library is deprecated.
This approach lets us provide a stable OAuth 2.0 server for legacy systems with the same features available to modern
systems.

## Migrating from OAuthJs and 3.x
## Contributing to This Project

Version 4.x should not be hard-breaking, however, there were many improvements and fixes that may
be incompatible with specific behaviour in <= 3.x
### Reporting Security Vulnerabilities

For more info, please read the [changelog](./CHANGELOG.md) or open an issue, if you think something
is unexpectedly not working.
If you intend to submit a security issue or vulnerability, please read
our [security policy](https://github.com/node-oauth/node-oauth2-server/security/policy) first.

## Supported NodeJs versions
The policy is also available locally in [SECURITY.md](./SECURITY.md).

This project supports the node versions along the
[NodeJS LTS releases](https://nodejs.org/en/about/releases/), focusing on
### Development, Issues, and Pull Requests

- Maintenance LTS
- Active LTS
- Current
Please read our [contribution guide](./CONTRIBUTING.md) before contributing.
Please open an issue before submitting a pull request to discuss whether your proposed contribution is suitable for
the project.

## Contributing to this project
## License

Please read our [contribution guide](./CONTRIBUTING.md) before taking actions.
In any case, please open an issue before opening a pull request to find out whether your intended contribution will actually have a chance to be merged.
This library is licensed under the MIT License. See the [LICENSE file](./LICENSE).
20 changes: 20 additions & 0 deletions docs/.vitepress/config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,26 @@ import {defineConfig} from 'vitepress'

// https://vitepress.dev/reference/site-config
export default defineConfig({
srcExclude: ['plans/**'],
markdown: {
config(md) {
// VitePress normalizes Markdown fragments, but JSDoc emits raw named
// anchors. Add matching IDs while preserving the original names.
for (const rule of ['html_inline', 'html_block']) {
const render = md.renderer.rules[rule]!
md.renderer.rules[rule] = (tokens, index, options, env, self) => {
return render(tokens, index, options, env, self).replace(
/<a name="([\w.+]+)">/g,
(anchor, name: string) => {
if (!/[_.+]/.test(name)) return anchor
const id = name.replace(/[_.+]+/g, '-').toLowerCase()
return `<a id="${id}" name="${name}">`
}
)
}
}
}
},
title: "@node-oauth/oauth2-server",
description: "OAuth2 server for Node.js",
base: '/node-oauth2-server/',
Expand Down
12 changes: 6 additions & 6 deletions docs/api/errors/invalid-argument-error.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ depends on *when* it is raised:
a required method, or `request`/`response` not being instances of this
library's `Request`/`Response`) it is thrown to your code unchanged, before
any response is produced — so you can catch it while wiring up the server.
- **While handling a request** (for example, when a `model` returns malformed
token data) the `token` and `authorize` handlers normalise it to a
`ServerError` (`server_error`) before the response reaches the client,
preserving the original error as `.inner`. So when you `catch` errors from
`OAuth2Server#token` or `OAuth2Server#authorize`, expect a `ServerError`
(inspect its `.inner`) for these cases — not an `InvalidArgumentError`.
- **Inside the token/authorize normalization blocks**, programming and non-OAuth
errors become `ServerError` with the original error in `.inner`.
- **Before those blocks**, errors propagate unchanged. In particular, authorization
client and user lookup run before its catch, as do token method/content-type
checks. Authentication preserves OAuth errors, including `InvalidArgumentError`.
Integrations must handle early failures locally and avoid exposing internal errors.

**Kind**: global class
<a name="new_InvalidArgumentError_new"></a>
Expand Down
33 changes: 20 additions & 13 deletions docs/api/grant-types/abstract-grant-type.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@
* [AbstractGrantType](#AbstractGrantType)
* [new AbstractGrantType(options)](#new_AbstractGrantType_new)
* [.generateAccessToken(client, user, scope)](#AbstractGrantType+generateAccessToken) ⇒ <code>Promise.&lt;string&gt;</code>
* [.generateRefreshToken()](#AbstractGrantType+generateRefreshToken)
* [.generateRefreshToken(client, user, scope)](#AbstractGrantType+generateRefreshToken)
* [.getAccessTokenExpiresAt()](#AbstractGrantType+getAccessTokenExpiresAt)
* [.getRefreshTokenExpiresAt()](#AbstractGrantType+getRefreshTokenExpiresAt) ⇒ <code>Date</code>
* [.getScope(request)](#AbstractGrantType+getScope) ⇒ <code>string</code> \| <code>undefined</code>
* [.validateScope(user, client, scope)](#AbstractGrantType+validateScope) ⇒ <code>string</code>
* [.getScope(request)](#AbstractGrantType+getScope) ⇒ <code>Array.&lt;string&gt;</code> \| <code>undefined</code>
* [.validateScope(user, client, scope)](#AbstractGrantType+validateScope) ⇒ <code>Promise.&lt;(Array.&lt;string&gt;\|undefined)&gt;</code>

<a name="new_AbstractGrantType_new"></a>

Expand Down Expand Up @@ -40,18 +40,25 @@ Otherwise, falls back to an internal implementation from `TokenUtil.generateRand

**Kind**: instance method of [<code>AbstractGrantType</code>](#AbstractGrantType)

| Param |
| --- |
| client |
| user |
| scope |
| Param | Type |
| --- | --- |
| client | <code>ClientData</code> |
| user | <code>object</code> |
| scope | <code>Array.&lt;string&gt;</code> \| <code>undefined</code> |

<a name="AbstractGrantType+generateRefreshToken"></a>

### abstractGrantType.generateRefreshToken()
### abstractGrantType.generateRefreshToken(client, user, scope)
Generate refresh token.

**Kind**: instance method of [<code>AbstractGrantType</code>](#AbstractGrantType)

| Param | Type |
| --- | --- |
| client | <code>ClientData</code> |
| user | <code>object</code> |
| scope | <code>Array.&lt;string&gt;</code> \| <code>undefined</code> |

<a name="AbstractGrantType+getAccessTokenExpiresAt"></a>

### abstractGrantType.getAccessTokenExpiresAt()
Expand All @@ -66,7 +73,7 @@ Get refresh token expiration date (now + refresh token lifetime)
**Kind**: instance method of [<code>AbstractGrantType</code>](#AbstractGrantType)
<a name="AbstractGrantType+getScope"></a>

### abstractGrantType.getScope(request) ⇒ <code>string</code> \| <code>undefined</code>
### abstractGrantType.getScope(request) ⇒ <code>Array.&lt;string&gt;</code> \| <code>undefined</code>
Get scope from the request body.

**Kind**: instance method of [<code>AbstractGrantType</code>](#AbstractGrantType)
Expand All @@ -77,14 +84,14 @@ Get scope from the request body.

<a name="AbstractGrantType+validateScope"></a>

### abstractGrantType.validateScope(user, client, scope) ⇒ <code>string</code>
### abstractGrantType.validateScope(user, client, scope) ⇒ <code>Promise.&lt;(Array.&lt;string&gt;\|undefined)&gt;</code>
Validate requested scope.
Delegates validation to the Model's `validateScope` method,
if the model implements this method.
Otherwise, treats given scope as valid.

**Kind**: instance method of [<code>AbstractGrantType</code>](#AbstractGrantType)
**Returns**: <code>string</code> - the validated scope
**Returns**: <code>Promise.&lt;(Array.&lt;string&gt;\|undefined)&gt;</code> - the validated scope
**Throws**:

- <code>InvalidScopeError</code> if the {Model#validateScope} method returned a falsy value
Expand All @@ -94,5 +101,5 @@ Otherwise, treats given scope as valid.
| --- | --- |
| user | <code>object</code> |
| client | <code>ClientData</code> |
| scope | <code>string</code> |
| scope | <code>Array.&lt;string&gt;</code> \| <code>undefined</code> |

6 changes: 3 additions & 3 deletions docs/api/grant-types/authorization-code-grant-type.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* [AuthorizationCodeGrantType](#AuthorizationCodeGrantType)
* [new AuthorizationCodeGrantType(options)](#new_AuthorizationCodeGrantType_new)
* [.handle(request, client)](#AuthorizationCodeGrantType+handle)
* [.getAuthorizationCode(request, client)](#AuthorizationCodeGrantType+getAuthorizationCode) ⇒ <code>Promise.&lt;{user}&gt;</code>
* [.getAuthorizationCode(request, client)](#AuthorizationCodeGrantType+getAuthorizationCode) ⇒ <code>Promise.&lt;AuthorizationCodeData&gt;</code>
* [.verifyPKCE(request, code)](#AuthorizationCodeGrantType+verifyPKCE)
* [.validateRedirectUri(request, code)](#AuthorizationCodeGrantType+validateRedirectUri)
* [.revokeAuthorizationCode(code)](#AuthorizationCodeGrantType+revokeAuthorizationCode)
Expand Down Expand Up @@ -35,7 +35,7 @@ Handle authorization code grant.

<a name="AuthorizationCodeGrantType+getAuthorizationCode"></a>

### authorizationCodeGrantType.getAuthorizationCode(request, client) ⇒ <code>Promise.&lt;{user}&gt;</code>
### authorizationCodeGrantType.getAuthorizationCode(request, client) ⇒ <code>Promise.&lt;AuthorizationCodeData&gt;</code>
Get the authorization code.

**Kind**: instance method of [<code>AuthorizationCodeGrantType</code>](#AuthorizationCodeGrantType)
Expand Down Expand Up @@ -108,5 +108,5 @@ Save token.
| user | <code>object</code> |
| client | <code>ClientData</code> |
| authorizationCode | <code>string</code> |
| requestedScope | <code>string</code> |
| requestedScope | <code>Array.&lt;string&gt;</code> \| <code>undefined</code> |

30 changes: 24 additions & 6 deletions docs/api/grant-types/client-credentials-grant-type.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,44 @@ Constructor.
**Kind**: global class

* [ClientCredentialsGrantType](#ClientCredentialsGrantType)
* [.handle()](#ClientCredentialsGrantType+handle)
* [.getUserFromClient()](#ClientCredentialsGrantType+getUserFromClient)
* [.saveToken()](#ClientCredentialsGrantType+saveToken)
* [.handle(request, client)](#ClientCredentialsGrantType+handle)
* [.getUserFromClient(client)](#ClientCredentialsGrantType+getUserFromClient)
* [.saveToken(user, client, requestedScope)](#ClientCredentialsGrantType+saveToken)

<a name="ClientCredentialsGrantType+handle"></a>

### clientCredentialsGrantType.handle()
### clientCredentialsGrantType.handle(request, client)
Handle client credentials grant.

**Kind**: instance method of [<code>ClientCredentialsGrantType</code>](#ClientCredentialsGrantType)
**See**: https://tools.ietf.org/html/rfc6749#section-4.4.2

| Param | Type |
| --- | --- |
| request | <code>Request</code> |
| client | <code>ClientData</code> |

<a name="ClientCredentialsGrantType+getUserFromClient"></a>

### clientCredentialsGrantType.getUserFromClient()
### clientCredentialsGrantType.getUserFromClient(client)
Retrieve the user using client credentials.

**Kind**: instance method of [<code>ClientCredentialsGrantType</code>](#ClientCredentialsGrantType)

| Param | Type |
| --- | --- |
| client | <code>ClientData</code> |

<a name="ClientCredentialsGrantType+saveToken"></a>

### clientCredentialsGrantType.saveToken()
### clientCredentialsGrantType.saveToken(user, client, requestedScope)
Save token.

**Kind**: instance method of [<code>ClientCredentialsGrantType</code>](#ClientCredentialsGrantType)

| Param | Type |
| --- | --- |
| user | <code>object</code> |
| client | <code>ClientData</code> |
| requestedScope | <code>Array.&lt;string&gt;</code> \| <code>undefined</code> |

Loading
Loading