Swiftorial Logo
Home
Swift Lessons
Matchups
CodeSnaps
Tutorials
Career
Resources

Securing RESTful APIs with OAuth 2.0

Introduction

OAuth 2.0 is an authorization framework that enables applications to obtain limited access to user accounts on an HTTP service. It works by delegating user authentication to the service that hosts the user account and authorizing third-party applications to access the user account. This guide covers the basics of OAuth 2.0 and how to implement it to secure RESTful APIs.

Why Use OAuth 2.0?

OAuth 2.0 provides several benefits for securing APIs:

  • Delegated access: Allows third-party applications to access APIs on behalf of a user.
  • Granular permissions: Grants specific permissions to applications, ensuring they only access what they need.
  • Improved security: Reduces the need for applications to handle user credentials directly.
  • Standardization: Provides a standardized way to handle authorization.

OAuth 2.0 Roles

OAuth 2.0 defines four roles:

  • Resource Owner: The user who authorizes an application to access their account.
  • Client: The application requesting access to the resource owner's account.
  • Resource Server: The server hosting the protected resources, capable of accepting and responding to protected resource requests.
  • Authorization Server: The server issuing access tokens to the client after successfully authenticating the resource owner and obtaining authorization.

OAuth 2.0 Grant Types

OAuth 2.0 defines several grant types for different use cases:

  • Authorization Code: Used for server-side applications.
  • Implicit: Used for client-side applications (e.g., single-page apps).
  • Resource Owner Password Credentials: Used for highly trusted applications.
  • Client Credentials: Used for machine-to-machine communication.

Implementing OAuth 2.0

1. Setting Up the Authorization Server

The authorization server is responsible for authenticating the user and issuing access tokens. In this example, we'll use a library called OAuth2orize for a Node.js implementation.

Step 1: Install Dependencies

# Install dependencies
npm install express body-parser oauth2orize passport passport-http bcryptjs jsonwebtoken

Step 2: Create the Authorization Server

const express = require('express');
const bodyParser = require('body-parser');
const oauth2orize = require('oauth2orize');
const passport = require('passport');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');

const app = express();
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());

const server = oauth2orize.createServer();

// Dummy user store
const users = [
    { id: 1, username: 'user1', password: bcrypt.hashSync('password1', 8) },
];

// Dummy client store
const clients = [
    { id: 'client1', clientId: 'client1', clientSecret: 'secret1', redirectUris: ['http://localhost:3000/callback'] },
];

// Dummy authorization code store
let authCodes = [];

// Dummy token store
let tokens = [];

// Grant authorization code
server.grant(oauth2orize.grant.code((client, redirectUri, user, ares, done) => {
    const code = jwt.sign({ clientId: client.clientId, userId: user.id }, 'secret', { expiresIn: '10m' });
    authCodes.push({ code, clientId: client.clientId, redirectUri, userId: user.id });
    done(null, code);
}));

// Exchange authorization code for access token
server.exchange(oauth2orize.exchange.code((client, code, redirectUri, done) => {
    const authCode = authCodes.find(ac => ac.code === code);
    if (!authCode || authCode.clientId !== client.clientId || authCode.redirectUri !== redirectUri) {
        return done(null, false);
    }

    const token = jwt.sign({ clientId: client.clientId, userId: authCode.userId }, 'secret', { expiresIn: '1h' });
    tokens.push({ token, clientId: client.clientId, userId: authCode.userId });
    done(null, token);
}));

app.post('/token', passport.authenticate(['oauth2-client-password'], { session: false }), server.token(), server.errorHandler());

app.listen(4000, () => {
    console.log('Authorization server is running on port 4000');
});

2. Setting Up the Resource Server

The resource server hosts the protected resources and verifies access tokens.

Step 1: Install Dependencies

# Install dependencies
npm install express body-parser jsonwebtoken

Step 2: Create the Resource Server

const express = require('express');
const bodyParser = require('body-parser');
const jwt = require('jsonwebtoken');

const app = express();
app.use(bodyParser.json());

const users = [
    { id: 1, username: 'user1', email: 'user1@example.com' },
];

function authenticateToken(req, res, next) {
    const authHeader = req.headers['authorization'];
    const token = authHeader && authHeader.split(' ')[1];
    if (!token) return res.sendStatus(401);

    jwt.verify(token, 'secret', (err, user) => {
        if (err) return res.sendStatus(403);
        req.user = user;
        next();
    });
}

app.get('/api/users', authenticateToken, (req, res) => {
    res.json(users);
});

app.listen(3000, () => {
    console.log('Resource server is running on port 3000');
});

Using OAuth 2.0 with Your API

Once you have set up the authorization and resource servers, your clients can obtain access tokens and use them to access protected resources.

Example: Client Request for Access Token

POST /token
Host: localhost:4000
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&code=AUTH_CODE&redirect_uri=http://localhost:3000/callback&client_id=client1&client_secret=secret1

Example: Client Request with Access Token

GET /api/users
Host: localhost:3000
Authorization: Bearer ACCESS_TOKEN

Conclusion

OAuth 2.0 is a robust framework for securing RESTful APIs by allowing third-party applications to access resources on behalf of users. By implementing OAuth 2.0, you can ensure secure and controlled access to your APIs, providing a better and safer user experience.