Simple OAuth2

Node.js client library for OAuth2.
OAuth2 lets users grant the access to the desired resources to third party applications,
giving them the possibility to enable and disable those accesses whenever they want.
Simple OAuth2 supports the following flows.
Thanks to Open Source
Simple OAuth 2.0 come to life thanks to the work I've made in Lelylan, an open source microservices architecture for the Internet of Things. If this project helped you in any way, think about giving us a star on Github.
Table of Contents
Requirements
The node client library is tested against Node 8 LTS and newer versions. Older node versions are unsupported.
Getting started
Installation
Install the client library using npm:
npm install --save simple-oauth2
Options
Simple OAuth2 accepts an object with the following valid params.
-
client
- required object with the following properties:
id
- Service registered client id. When required by the spec this value will be automatically encoded. Required.
secret
- Service registered client secret. When required by the spec this value will be automatically encoded. Required.
secretParamName
- Parameter name used to send the client secret. Default to client_secret.
idParamName
- Parameter name used to send the client id. Default to client_id.
-
auth
- required object with the following properties.
tokenHost
- String used to set the host to request the tokens to. Required.
tokenPath
- String path to request an access token. Default to /oauth/token.
revokePath
- String path to revoke an access token. Default to /oauth/revoke.
authorizeHost
- String used to set the host to request an "authorization code". Default to the value set on auth.tokenHost
.
authorizePath
- String path to request an authorization code. Default to /oauth/authorize.
-
http
optional object used to set global options to the internal http library (wreck).
- All options except baseUrl are allowed.
headers.authorization
will always be overriden by the library to properly send the required credentials on each scenario. Default to headers.Accept = application/json
.
-
options
optional object to setup the module.
bodyFormat
- Format of data sent in the request body. Valid options are form
or json
. Defaults to form.
authorizationMethod
- Indicates the method used to send the client.id/client.secret authorization params at the token request. Valid options are header
or body
. If set to body, the bodyFormat option will be used to format the credentials. Defaults to header.
const credentials = {
client: {
id: '<client-id>',
secret: '<client-secret>'
},
auth: {
tokenHost: 'https://api.oauth.com'
}
};
const oauth2 = require('simple-oauth2').create(credentials);
Example of Usage
See the example folder.
OAuth2 Supported flows
Authorization Code flow
The Authorization Code flow is made up from two parts. At first your application asks to
the user the permission to access their data. If the user approves the OAuth2 server sends
to the client an authorization code. In the second part, the client POST the authorization code
along with its client secret to the oauth server in order to get the access token.
const oauth2 = require('simple-oauth2').create(credentials);
const authorizationUri = oauth2.authorizationCode.authorizeURL({
redirect_uri: 'http://localhost:3000/callback',
scope: '<scope>',
state: '<state>'
});
res.redirect(authorizationUri);
const tokenConfig = {
code: '<code>',
redirect_uri: 'http://localhost:3000/callback',
scope: '<scope>',
};
const httpOptions = {};
try {
const result = await oauth2.authorizationCode.getToken(tokenConfig, httpOptions);
const accessToken = oauth2.accessToken.create(result);
} catch (error) {
console.log('Access Token Error', error.message);
}
Password Credentials Flow
This flow is suitable when the resource owner has a trust relationship with the
client, such as its computer operating system or a highly privileged application.
Use this flow only when other flows are not viable or when you need a fast way to
test your application.
const oauth2 = require('simple-oauth2').create(credentials);
const tokenConfig = {
username: 'username',
password: 'password',
scope: '<scope>',
};
const httpOptions = {};
try {
const result = await oauth2.ownerPassword.getToken(tokenConfig, httpOptions);
const accessToken = oauth2.accessToken.create(result);
} catch (error) {
console.log('Access Token Error', error.message);
}
Client Credentials Flow
This flow is suitable when client is requesting access to the protected resources under its control.
const oauth2 = require('simple-oauth2').create(credentials);
const tokenConfig = {
scope: '<scope>',
};
const httpOptions = {};
try {
const result = await oauth2.clientCredentials.getToken(tokenConfig, httpOptions);
const accessToken = oauth2.accessToken.create(result);
} catch (error) {
console.log('Access Token error', error.message);
}
Helpers
Access Token object
When a token expires we need to refresh it. Simple OAuth2 offers the
AccessToken class that add a couple of useful methods to refresh the
access token when it is expired.
const tokenObject = {
'access_token': '<access-token>',
'refresh_token': '<refresh-token>',
'expires_in': '7200'
};
let accessToken = oauth2.accessToken.create(tokenObject);
if (accessToken.expired()) {
try {
const params = {
scope: '<scope>',
};
accessToken = await accessToken.refresh(params);
} catch (error) {
console.log('Error refreshing access token: ', error.message);
}
}
The expired
helper is useful for knowing when a token has definitively
expired. However, there is a common race condition when tokens are near
expiring. If an OAuth 2.0 token is issued with a expires_in
property (as
opposed to an expires_at
property), there can be discrepancies between the
time the OAuth 2.0 server issues the access token and when it is received.
These come down to factors such as network and processing latency. This can be
worked around by preemptively refreshing the access token:
const EXPIRATION_WINDOW_IN_SECONDS = 300;
const { token } = accessToken;
const expirationTimeInSeconds = token.expires_at.getTime() / 1000;
const expirationWindowStart = expirationTimeInSeconds - EXPIRATION_WINDOW_IN_SECONDS;
const nowInSeconds = (new Date()).getTime() / 1000;
const shouldRefresh = nowInSeconds >= expirationWindowStart;
if (shouldRefresh) {
try {
accessToken = await accessToken.refresh();
} catch (error) {
console.log('Error refreshing access token: ', error.message);
}
}
When you've done with the token or you want to log out, you can
revoke the access token and refresh token.
try {
await accessToken.revoke('access_token');
await accessToken.revoke('refresh_token');
} catch (error) {
console.log('Error revoking token: ', error.message);
}
As a convenience method, you can also revoke both tokens in a single call:
try {
await accessToken.revokeAll();
} catch (error) {
console.log('Error revoking token: ', error.message);
}
Errors
Errors are returned when a 4xx or 5xx status code is received.
BoomError
As a standard boom error you can access any of the boom error properties. The total amount of information varies according to the generated status code.
try {
await oauth2.authorizationCode.getToken();
} catch(error) {
console.log(error);
}
Contributing
See CONTRIBUTING
Authors
Andrea Reginato
Contributors
Special thanks to the following people for submitting patches.
Changelog
See CHANGELOG
License
Simple OAuth 2.0 is licensed under the Apache License, Version 2.0