Node.js client library for OAuth2 (this library supports both callbacks or promises for async flow).
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.
- Authorization Code Flow (for apps with servers that can store persistent information).
- Password Credentials (when previous flow can't be used or during development).
- Client Credentials Flow (the client can request an access token using only its client credentials)
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.
Node client library is tested against the latest minor Node versions: 4, 5 and 6.
To use in older node version, please use simple-oauth2@0.x.
Install the client library using npm:
$ npm install --save simple-oauth2
Simple OAuth2 accepts an object with the following valid params.
-
client
- required object with the following properties:id
- Service registered client id. Required.secret
- Service registered client secret. 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 onauth.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 (request-js).- Any key is allowed here. Default to
headers.Accept = application/json
.
- Any key is allowed here. Default to
-
options
optional object to setup the module.bodyFormat
- Format of data sent in the request body. Valid values areform
orjson
. Defaults to form.useBodyAuth
- Whether or not the client.id/client.secret params are sent in the request body. Defaults to true.useBasicAuthorizationHeader
- Whether or not the Basic Authorization header should be sent at the token request.
// Set the configuration settings
const credentials = {
client: {
id: '<client-id>',
secret: '<client-secret>'
},
auth: {
tokenHost: 'https://api.oauth.com'
}
};
// Initialize the OAuth2 Library
const oauth2 = require('simple-oauth2').create(credentials);
See the example folder.
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);
// Authorization oauth2 URI
const authorizationUri = oauth2.authorizationCode.authorizeURL({
redirect_uri: 'http://localhost:3000/callback',
scope: '<scope>',
state: '<state>'
});
// Redirect example using Express (see http://expressjs.com/api.html#res.redirect)
res.redirect(authorizationUri);
// Get the access token object (the authorization code is given from the previous step).
const tokenConfig = {
code: '<code>',
redirect_uri: 'http://localhost:3000/callback'
};
// Callbacks
// Save the access token
oauth2.authorizationCode.getToken(tokenConfig, (error, result) => {
if (error) {
return console.log('Access Token Error', error.message);
}
const accessToken = oauth2.accessToken.create(result);
});
// Promises
// Save the access token
oauth2.authorizationCode.getToken(tokenConfig)
.then((result) => {
const accessToken = oauth2.accessToken.create(result);
})
.catch((error) => {
console.log('Access Token Error', error.message);
});
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);
// Get the access token object.
const tokenConfig = {
username: 'username',
password: 'password'
};
// Callbacks
// Save the access token
oauth2.ownerPassword.getToken(tokenConfig, (error, result) => {
if (error) {
return console.log('Access Token Error', error.message);
}
const accessToken = oauth2.accessToken.create(result);
});
// Promises
// Save the access token
oauth2.ownerPassword
.getToken(tokenConfig)
.then((result) => {
const accessToken = oauth2.accessToken.create(result);
return accessToken;
});
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 = {};
// Callbacks
// Get the access token object for the client
oauth2.clientCredentials.getToken(tokenConfig, (error, result) => {
if (error) {
return console.log('Access Token Error', error.message);
}
const accessToken = oauth2.accessToken.create(result);
});
// Promises
// Get the access token object for the client
oauth2.clientCredentials
.getToken(tokenConfig)
.then((result) => {
const accessToken = oauth2.accessToken.create(result);
})
.catch((error) => {
console.log('Access Token error', error.message);
});
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.
// Sample of a JSON access token (you got it through previous steps)
const tokenObject = {
'access_token': '<access-token>',
'refresh_token': '<refresh-token>',
'expires_in': '7200'
};
// Create the access token wrapper
let accessToken = oauth2.accessToken.create(tokenObject);
// Check if the token is expired. If expired it is refreshed.
if (accessToken.expired()) {
// Callbacks
accessToken.refresh((error, result) => {
accessToken = result;
})
// Promises
accessToken.refresh()
.then((result) => {
accessToken = result;
});
}
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:
// Provide a window of time before the actual expiration to refresh the token
const EXPIRATION_WINDOW_IN_SECONDS = 300;
const { token } = accessToken;
const expirationTimeInSeconds = token.expires_at.getTime() / 1000;
const expirationWindowStart = expirationTimeInSeconds - EXPIRATION_WINDOW_IN_SECONDS;
// If the start of the window has passed, refresh the token
const nowInSeconds = (new Date()).getTime() / 1000;
const shouldRefresh = nowInSeconds >= expirationWindowStart;
if (shouldRefresh) {
// Callbacks
accessToken.refresh((error, result) => {
accessToken = result;
})
// Promises
accessToken.refresh()
.then((result) => {
accessToken = result;
});
}
When you've done with the token or you want to log out, you can revoke the access token and refresh token.
// Callbacks
// Revoke only the access token
accessToken.revoke('access_token', (error) => {
// Session ended. But the refresh_token is still valid.
// Revoke the refresh_token
accessToken.revoke('refresh_token', (error) => {
console.log('token revoked.');
});
});
// Promises
// Revoke only the access token
accessToken.revoke('access_token')
.then(() => {
// Revoke the refresh token
return accessToken.revoke('refresh_token');
})
.then(() => {
console.log('Token revoked');
})
.catch((error) => {
console.log('Error revoking token.', error.message);
});
Exceptions are raised when a 4xx or 5xx status code is returned.
HTTPError
Through the error message attribute you can access the JSON representation
based on HTTP status
and error message
.
// Callbacks
oauth2.authorizationCode.getToken({}, (error, token) => {
if (error) {
return console.log(error.message);
}
});
// Promises
oauth2.authorizationCode
.getToken({})
.catch((error) => {
console.log(error.message);
});
// => { "status": "401", "message": "Unauthorized" }
See CONTRIBUTING
Special thanks to the following people for submitting patches.
See CHANGELOG
Simple OAuth 2.0 is licensed under the Apache License, Version 2.0