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

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 109 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*

# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage
*.lcov

# nyc test coverage
.nyc_output

# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
jspm_packages/

# TypeScript v1 declaration files
typings/

# TypeScript cache
*.tsbuildinfo

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variables file
.env
.env.test

# parcel-bundler cache (https://parceljs.org/)
.cache

# Next.js build output
.next

# Nuxt.js build / generate output
.nuxt
# dist

# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and *not* Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public

# vuepress build output
.vuepress/dist

# Serverless directories
.serverless/

# FuseBox cache
.fusebox/

# DynamoDB Local files
.dynamodb/

# TernJS port file
.tern-port

# IDE specific
.idea
.vscode
.atom
21 changes: 19 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,19 @@
# node-oauth2-server-examples
This repository is meant to maintain a list of examples of using node-oauth2-server with different databases.
# Node OAuth2 Server Examples

This repository is meant to maintain a list of examples of using @node-oauth/oauth2-server
with different setups.

## Examples

Each example contains its own README with specific installation and run instructions.

### Server to server

Location: [/server2server](./server2server)

Minimal client-credential grant flow with express and fetch and in-memory DB.
Suitable for (Micro-) services and small apps, exchanging protected resources.

## License

All examples are MIT licenses, defined by the top-level [LICENSE](./LICENSE) file
51 changes: 51 additions & 0 deletions server2server/.eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
{
"extends": "eslint:recommended",
"env": {
"node": true,
"mocha": true,
"es6": true
},
"parserOptions": {
"ecmaVersion": 9,
"sourceType": "module",
"ecmaFeatures" : {
"globalReturn": false,
"impliedStrict": true,
"jsx": false
}
},
"rules": {
"indent": [
"error",
2
],
"linebreak-style": [
"error",
"unix"
],
"quotes": [
"error",
"single"
],
"semi": [
"error",
"always"
],
"no-var": [
"error"
],
"prefer-const": ["error", {
"destructuring": "any",
"ignoreReadBeforeAssign": false
}],
"no-unused-vars": [
"error",
{
"vars": "all",
"args": "none",
"ignoreRestSiblings": false
}
]
}
}

153 changes: 153 additions & 0 deletions server2server/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
# Server 2 server with Client Credentials Grant

## Architecture

The client credentials workflow is described in
[RFC 6749, section 4.4](https://datatracker.ietf.org/doc/html/rfc6749.html#section-4.4):

```
+---------+ +---------------+
| | | |
| |>--(A)- Client Authentication --->| Authorization |
| Client | | Server |
| |<--(B)---- Access Token ---------<| |
| | | |
+---------+ +---------------+
```

In this setup the authorization server does also contain the consumable **resources**.
Therefore, the setup looks something like this:

```
+----------+ +--------------+
| | | |
| Consumer |>--(A)- Client Authentication --->| Provider |
| | | |
| |<--(B)---- Access Token ---------<| |
| | | |
| |>--(C)---- Access resource ---------> [Resource] |
| | | | |
| |<--(D)----Resource response -----<--------< |
+----------+ +--------------+
```

### Provider dependencies
- @node-oauth/express-oauth-server (uses @node-oauth/oauth2-server)
- express
- body-parser
- dotenv

### Consumer dependencies

- node-fetch
- dotenv

## Installation and usage

If you haven't already cloned this repository, then clone it via

```shell
$ git clone https://github.com/node-oauth/node-oauth2-server-examples.git
$ cd server2server
```

### Install and run the provider

Since we have two servers you need to install dependencies in both.
First, start with the provider:

```shell
$ cd provider
$ npm install
$ npm run start
```

The provider runs on `http://localhost:8080`


### Install and run the consumer

```shell
$ cd ../consumer
$ npm install
$ npm run start
```

The consumer will now make several requests. Note, that some of them are expected to fail.
The overall output should look like so:

```shell
[Consumer]: get /public (public)
[Consumer]: => response: 200 OK moo

[Consumer]: get /read-resource (not authenticated)
[Consumer]: => response: 401 Unauthorized

[Consumer]: post /token (bad credentials)
[Consumer]: => response: 401 Unauthorized {"error":"invalid_client","error_description":"Invalid client: client is invalid"}

[Consumer]: post /token (bad credentials)
[Consumer]: => response: 200 OK {"access_token":"45f81685482d6e1337b99ddb8726b7c04355b3d427b1401cf08e5c3bea013a38","token_type":"Bearer","expires_in":3600,"scope":true}

[Consumer]: authorization token successfully retrieved!

[Consumer]: get /read-resource (authenticated, resource is not yet defined)
[Consumer]: => response: 200 OK {"resource":null}

[Consumer]: post /write-resource (authentication failed)
[Consumer]: => response: 401 Unauthorized {"error":"invalid_token","error_description":"Invalid token: access token is invalid"}

[Consumer]: post /write-resource (Invalid token)
[Consumer]: => response: 200 OK {"message":"resource created"}

[Consumer]: get /read-resource (authenticated, resource is now)
[Consumer]: => response: 200 OK {"resource":"foo-bar-moo"}
```

## How routes are protected

If you take a look at the `provider/index.js` file, you will see a mix of public and private routes.

```js
app.get('/protected-route', oauth.authenticate(), function (req, res) {
res.send({ resource: internal.resource });
})
```

If the authenticated middleware fails, there will be no `next()` call into your provided handler function.
The authentication will fail, if no **access token** is provided.

In order to receive an access token, the client needs to make a call to the **public** `/token` endpoint, first:

```js
const tokenBodyParams = new URLSearchParams();
tokenBodyParams.append('grant_type', 'client_credentials');
tokenBodyParams.append('scope', 'read');

const response = await fetch('http://localhost:8080/token', {
method: 'post',
body: tokenBodyParams,
headers: {
'content-type': 'application/x-www-form-urlencoded',
'authorization': 'Basic ' + Buffer.from(`${client.id}:${client.secret}`).toString('base64'),
}
})
const token = await response.json()
const accessToken = token.access_token
const tokenType = token.token_type
```

With this token, the client can make authenticated requests to the protected endpoints:

```js
const response = await fetch('http://localhost:8080/read-resource', {
method: 'get',
headers: {
'authorization': `${tokenType} ${accessToken}`
}
})
const resource = await response.json()
```

Since there are no refresh token involved, the requests may fail, due to expired token.
It's up to the client to re-request a new token.
2 changes: 2 additions & 0 deletions server2server/consumer/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
CLIENT_ID="this-client-id-is-for-demo-only"
CLIENT_SECRET="this-secret-id-is-for-demo-only"
Loading