Creating a Web applicationâť“
đź”·You need a login flow.
Creating a Mobile applicationâť“
đź”·You need a login flow.
Creating an AI Chatbotâť“
đź”·You need a login flow.
And of course, you would need single sign on.
Sometimes, its pretty hard to wrap your head around the Oauth Login flow for apps. More often than not you would use federated login flows from Google or Facebook. Maybe even a third party provider like Auth0.
The Traditional Oauth Authentication flow
1. User Requests Authorization
- User Initiates Login: The user clicks a login button on the client application, indicating they want to authenticate.
- Client Redirects to Authorization Server: The client redirects the user to the OAuth authorization server’s authorization endpoint with the necessary parameters such as client ID, redirect URI, response type (typically code), and scopes.
2. User Grants Authorization
- Authorization Server Presents Login/Consent: The authorization server prompts the user to log in and then asks the user to consent to the requested scopes.
- User Grants Consent: The user authenticates (if not already authenticated) and consents to the scopes requested by the client application.
3. Authorization Server Redirects Back to Client
- Authorization Code: If the user grants consent, the authorization server redirects the user back to the client’s redirect URI with an authorization code.
4. Client Requests Access Token
-
Client Sends Authorization Code: The client application sends a request to the authorization server’s token endpoint, including the authorization code received, client ID, client secret, and redirect URI.
-
Authorization Server Validates and Issues Token: The authorization server validates the authorization code, client credentials, and redirect URI, then issues an access token (and optionally a refresh token) back to the client.
5. Client Accesses Protected Resources
- Client Makes Authenticated Request: The client uses the access token to make requests to the resource server to access protected resources.
- Resource Server Validates Token: The resource server validates the access token with the auth server using middleware, and if valid, provides the requested resources to the client.
Creating an Oauth Server
Prerequisites
- Node version v16+
- npm version v8+
- npx version v8+
- react js (for building the login form)
Building the Auth Server
We will be using the node js oauth2-server to implement our Auth server.
- First, lets initialize our app. Run the following commands in your project folder
npm init -y
npm install express oauth2-server body-parser- This will create the package.json file. This is what my dependencies look like
"dependencies": {
"body-parser": "^1.20.2",
"express": "^4.19.2",
"oauth2-server": "^3.1.1"
}- Next, we code our Authentication logic in the main server.js file. This is what it looks like
//using express
const express = require('express');
const bodyParser = require('body-parser');
const OAuth2Server = require('oauth2-server');
const app = express();
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
const Request = OAuth2Server.Request;
const Response = OAuth2Server.Response;
//initialize the oauth server with a model representing the user principle
//the model servers as the entity to query users from databases or LDAP or Active Directory
app.oauth = new OAuth2Server({
model: require('./model'),
allowBearerTokensInQueryString: true
});
//function to generate the access token bases on the Oauth2Server request
app.post('/oauth/token', (req, res, next) => {
const requ = new Request(req);
const resp = new Response(res);
//generate the token
app.oauth
.token(requ, resp)
.then(token => {
res.json(token);
})
.catch(err => {
res.status(err.code || 500).json(err);
});
});
//middleware to intercept requests to secure and ensure they are authenticated
app.use('/secure', (req, res, next) => {
const request = new Request(req);
const response = new Response(res);
//authenticate the request based on the token within the request
app.oauth
.authenticate(request, response)
.then((token) => {
next();
})
.catch((err) => {
res.status(err.code || 500).json(err);
});
});
//function can only be invoked for an authenticated request
app.get('/secure', (req, res) => {
res.send('Secure data');
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});'/oauth/token' -> The app.oauth.token method is called with the wrapped request
and response objects. This method generates an OAuth2 token.
'/secure' -> This middleware is applied to all routes starting with /secure
and ensures only authenticated requests are forwared to the /secure function- This what our model looks like. It is hardcoded as of now for demonstratability but ideally you would be confirming this information with an LDAP source
const crypto = require('crypto');
const model = {};
const clients = [
{
clientId: 'client-id',
clientSecret: 'client-secret',
grants: ['password', 'refresh_token'],
},
];
const tokens = [];
const users = [
{
id: '1',
username: 'user',
password: 'password',
},
];
model.getClient = (clientId, clientSecret) => {
return clients.find(client => client.clientId === clientId && client.clientSecret === clientSecret);
};
model.saveToken = (token, client, user) => {
token.client = client;
token.user = user;
tokens.push(token);
return token;
};
model.getAccessToken = (accessToken) => {
return tokens.find(token => token.accessToken === accessToken);
};
model.getRefreshToken = refreshToken => {
return tokens.find(token => token.refreshToken === refreshToken);
};
model.revokeToken = token => {
const index = tokens.findIndex(t => t.refreshToken === token.refreshToken);
if (index !== -1) {
tokens.splice(index, 1);
return true;
}
return false;
};
model.getUser = (username, password) => {
return users.find(user => user.username === username && user.password === password);
};
model.verifyScope = (token, scope) => {
return true; // Simplified for this example
};
module.exports = model;Disclaimer: Please don’t use this code as is. Security hardening would be required
Now that our server is ready, we can run it using the command
node server.jsThe server will start on port 3000. You can change the port to another as well if you like.
