This SDK allows you to use the OAuth 2.0 providing features of OAuth.io.
OAuth 2.0 allows you to add an authentication layer to your API, according the the RFC 6749.
Express Framework is required
This SDK has been made to work on top of Express. Other frameworks are not supported during the BETA.
Create a provider on OAuth.io/oauthd
First, you need to create a provider on OAuth.io, or in your oauthd instance.
There you will be able to configure the provider with a name, and a complete authorization items list (with which clients will define their scope).
You'll be able to retrieve the provider_id and the provider_secret, which will enable you to initialize the SDK on your node.js API server.
User management
Your server must have some kind of user management, as client applications will use your API on behalf of your users.
To install the SDK, just run the following command in your API server application:
$ node install oauthio-server
The OAuth 2.0 framework allows you to secure your API endpoints with an authentication layer. This lets apps call the endpoints on behalf of your users, after having asked for a scope of permissions.
The authentication is done thanks to an access token, which is given with any call to the API.
The token is recognized, and the associated user, associated app and the available permissions scope are found so the API is able to decide if it will send resource or not.
To obtain the access token, the following processs is used:
- First, the app (the client) registers on the API provider's developer portal, to obtain a client id and a client secret.
- Then, a user (already subscribed to the API provider's service), launches an action on the client's website, which needs resources from the provider's API.
- The user is redirected on the provider's website (usually on the
/authorize
endpoint) with information about who the client is - The user logs in to the provider's website, and responds to a decision form, in which he can see what permissions the client app wants on his account on the provider's API
- Upon acceptation, the user is redirected to the client's website, on a specific callback URL, with an authentication code
- The client then exchanges the authentication code for an access token by calling the
/token
endpoint on the provider's API.
Implementing all this, be it from the client's point of view, or from the provider's, is quite long and tedious.
For the client part, check out OAuth.io and oauthd's client services, which will enable you to integrate over 100 APIs in your application in a matter of minutes.
Once you have created a provider on OAuth.io or oauthd, and installed the SDK via npm, you need to initialize the SDK with your provider_id and provider_secret:
OAuthProvider = require('oauthio-server');
OAuthProvider.initialize('your_provider_id', 'your_provider_secret');
Once that's done, you will be able to create the endpoints for the OAuth 2.0 dance, and use the client management methods to create and edit your client apps from your developer portal.
The first thing you need to do is to provide the SDK a way to recognize your currently logged in user from a connect request object, by overriding the OAuth2.getUserId
method. For example, if the user id is stored in the session , you can do something like this:
OAuthProvider.OAuth2.getUserId = (req) => {
return req.session.user.id
}
Then you'll need to create two endpoints for the /authorize
URL. You can put another URL if you want, but /authorize
is recommended by the OAuth 2.0 RFC.
GET /authorize
First, you need to create an endpoint for the GET
method, which will redirect the user to a login page if he is not logged in, and then, show the decision form.
If the user is not logged in, you need to redirect him to the log in page with the same GET parameters, and the location of the authorization endpoint. Once the user is logged in, he should be redirected once more to the authorization endpoint with the original get parameters.
Here's an example of how you could do it:
// authorization endpoint login middleware
var isLoggedIn = function (req, res, next) {
if (req.session.user) {
// if the user is connected, continue to the authorization endpoint
next();
} else {
// otherwise, redirect him to the login endpoint
// the backUrl variable will enable the login endpoint
// to redirect to the authorization page once the user is logged in
var backUrl = req.path;
// appends the backUrl to the GET parameters
req.query.backUrl = backUrl;
// the qs stringify transforms an object in
// a url ready parameters string
var parameters = qs.stringify(req.query);
res.redirect('/login?' + parameters);
}
};
// Log in page
app.get('/login', function (req, res, next) {
// serve the login page, for example:
res.sendFile(__dirname + '/templates/login.html');
});
// Login form action
app.post('/login', function (req, res, next) {
// login the user (of course this is simplified ;) )
if (req.body.username == 'theuser' and req.body.password == 'thepassword') {
req.session.user = {
id: 'theuserid',
username: 'theuser'
}
// redirect to backUrl, with the same GET parameters
var backUrl = req.query.backUrl;
delete req.query.backUrl;
var parameters = qs.stringify(req.query);
res.redirect('/authorize?' + parameters);
} else {
res.status(500);
res.send("Wrond username or password");
}
});
The authorization endpoint uses the isLoggedIn
middleware. The endpoint must then respond to the user with a decision form.
You can completely customize this form by giving a template path. The template will be parsed using ejs. By default, the template has access to the following variables:
client
: An object representing the client, containing the field name,scope
: An array containing the different permissions that the provider requests
You can add custom values to the template as well.
To provider the endpoint with the template and custom data, you need to add a middleware, and override the req.template
field with the template path, and the req.data
field with an object containing values that will be usable in the template. Finally, you need to pass the result of OAuthProvider.OAuth2.getauthorize()
, which finishes the endpoint response:
app.get('/authorization', isLoggedIn, function (req, res, next) {
req.template = Path.join(__dirname + '/path/to/decision.html');
req.data = {
user: req.session.user
};
next();
}, OAuthProvider.OAuth2.getauthorize());
The template should contain a form with a field decision
valued 0 or 1 according to the user's response, for example:
<!DOCTYPE html>
<html>
<head><title>Decision page</title></head>
<body>
<p>
Hello, <%= user.username %>!
</p>
<p>
<%= client.name %> requests the following permissions to access your account on [Provider Name]:
<ul>
<% for (var k in scope) {%>
<li><%= scope[k] %></li>
<% } %>
</ul>
</p>
<!-- The forms should have no 'action' attribute, as it must call the same endpoint '/authorization' with the same GET parameters-->
<form method="POST">
<input type="hidden" name="decision" value="1" />
<input type="submit" value="Authorize" />
</form>
<form method="POST">
<input type="hidden" name="decision" value="0" />
<input type="submit" value="Cancel" />
</form>
</body>
</html>
POST /authorize
From here, things are a lot easier. You need to create the POST /authorize
and the POST /token
endpoints:
app.post('/authorize', OAuthProvider.OAuth2.postauthorize());
app.post('/token', OAuthProvider.OAuth2.token());
The POST /authorize
endpoint catches the response of the user to the decision form, and proxies it to OAuth.io's authentication server. This saves a set containing user's, id, the client and the scope, and associates it with an code. It redirects the user to the client's redirect URL with the code.
The client can then call the /token
endpoint with the code, the client id and client secret to get the access token server side.
And that's it. Now you can secure your API endpoints, filtering the calls thanks to the scope and user id associated with the access token.
Here's how you can secure your API endpoints with this SDK's middleware, OAuthProvider.OAuth2.check()
:
// Using the OAuthProvider.OAuth2.check() middleware, you get
// all the information about the access token that was sent
// alongside the request
app.get('/someendpoint', OAuthProvider.OAuth2.check({
scope: ['list', 'of', 'required', 'permissions']
}), function (req, res, next) {
// if the provided access token does not have all the required permissions
// a 403 error is sent, otherwise, the endpoint is called
// Here you can get
// the user id
var user_id = req.OAuth2.userId;
// the client id
var client = req.OAuth2.clientId;
// the scope
var scope = req.OAuth2.scope;
response = {
key: 'value'
};
// if you need to check additional permissions
// you can use the scope variable:
if (scope.indexOf('additional_permission') !== -1) {
// add other information to the response
response.protectedKey = 'protectedValue';
}
// Finally send the response
res.status(200);
res.send(response);
});
The SDK gives you methods that simplify the access to the Client Management API that goes along the OAuth 2.0 server.
This way, you can create your own developer portal, so that developers can register their apps and use your platform.
All the client methods are contained in OAuthProvider.clients
.
To create a client, you need to call the OAuthProvider.clients.create()
method, with a client's information.
Required fields
name
: The client's nameredirectUri
: The callback URI, on the client's domain, that will intercept the code to later exchange it for an access token
Optional fields
description
: The client's description
Example
OAuthProvider.clients.create({
req.body
})
.then(function (client) {
// client created
// here you'll also have client.client_id and client.client_secret
})
.fail(function (error) {
// an error occured
});
To retrieve all your clients, you need to call the OAuthProvider.clients.getAll()
method.
Example
OAuthProvider.clients.getAll()
.then(function (clients){
// 'clients' contains all your clients
})
.fail(function (error) {
// an error occured
});
To retrieve a specific client, you need to call the OAuthProvider.clients.get()
method with the client id of the requested client.
Example
OAuthProvider.clients.get(client_id)
.then(function (client){
// Here you'll have all the client's information
// in the 'client' variable
})
.fail(function (error) {
// an error occured
});
To update a client, you need to call the OAuthProvider.clients.update()
with the client's updated data.
Required fields
client_id
: The id of the client to update
Optional fields
name
: the client's namedescription
: the client's description
Example
OAuthProvider.clients.update(client)
.then(function (client) {
// client was updated
})
.fail(function (error) {
// an error occured
});
To reset a client's keys, you need to call the OAuthProvider.clients.regenerateKeys()
method with the client's client_id
.
Example
OAuthProvider.clients.regenerateKeys(client_id)
.then(function (client) {
// client keys were updated
})
.fail(function (error) {
// an error occured
});
To delete a client, you need to call the OAuthProvider.clients.delete()
method with the client's client_id
.
Example
OAuthProvider.clients.delete(client_id)
.then(function (client) {
// client keys were updated
})
.fail(function (error) {
// an error occured
});
To contribute to this SDK, you can:
- open issues on Github to report bugs and make feature requests
- fork the repository and make pull requests
This SDK is published under the Apache 2.0 license.