Oauth Access Tokens

Getting started with using Oauth to secure your application

OAuth Authentication Flows - A Guide

Section 1: Understanding OAuth 2.0 Fundamentals

What is OAuth 2.0?

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. What follows here is some high-level information about working with Oauth, and some of this may be ground well covered for many API users, but nonetheless best practices and some basics are best repeated, especially for those new the to this kind of authentication flow. In this guide we'll go through setting up the tokens needed to have the NS core module function as an auth provider for your application.

Key Components

Resource Owner: The user who authorizes an application to access their account. In this case an end user of your application you are building using the NS API.
Client: The application that wants to access the user's account. This would be your application you've built itself that your end user is interacting with.
Resource Server: The server hosting the protected user accounts. This is the NetSapiens Core where your main call and configuration functions are housed.
Authorization Server: The server that authenticates the user and issues access tokens. In this case either the NS Core or a third party authentication provider.

Core Concepts

Access Token: A credential used to access protected resources
Refresh Token: A credential used to obtain new access tokens (long-lived, stored securely)
Scopes: Define the level of access that the application is requesting
State Parameter: A random value used to prevent CSRF attacks

Token Lifecycle

  1. Initial Authorization: User grants permission, receives access + refresh tokens
  2. API Access: Use access token to make authenticated requests
  3. Token Refresh: When access token expires, use refresh token to get new tokens
  4. Token Revocation: Invalidate tokens when user logs out or revokes access

Section 2: OAuth Flow Types and Implementation

Authorization Code Flow (Recommended)

Best for: Server-side web applications that can securely store client secrets (usual format)

# Step 1: Redirect user to authorization server
GET /authorize?
  response_type=code&
  client_id=CLIENT_ID&
  redirect_uri=REDIRECT_URI&
  scope=openid profile email&
  state=RANDOM_STATE_VALUE
# Step 2: Exchange authorization code for tokens
POST /token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&
code=AUTHORIZATION_CODE&
redirect_uri=REDIRECT_URI&
client_id=CLIENT_ID&
client_secret=CLIENT_SECRET

Implementation Example (Node.js):

// Step 1: Initiate OAuth flow by getting a token from 

import netsapiensApi from '@api/netsapiens-api';

const authUrl = netsapiensApi.buildAuthUrl({
  response_type: 'code',
  client_id: 'your-client-id',
  redirect_uri: 'your-callback-url',
  scope: 'api:read api:write'
});

// Redirect user to authUrl, then handle the callback, next:


// Step 2: Handle callback and exchange code
app.get('/callback', async (req, res) => {
  const { code, state } = req.query;
  
  // Verify state parameter
  if (state !== req.session.oauthState) {
    return res.status(400).send('Invalid state parameter');
  }
  
  try {
    
    // Make HTTP call to NetSapiens
    const tokenResponse = await fetch('https://your-server/ns-api/v2/tokens', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        grant_type: 'authorization_code',
        code,
        redirect_uri: REDIRECT_URI,
        client_id: CLIENT_ID,
        client_secret: CLIENT_SECRET
      })
    });
    const tokens = await tokenResponse.json();
    
    // Store tokens securely
    req.session.accessToken = tokens.access_token;
    req.session.refreshToken = tokens.refresh_token;
    
    res.redirect('/dashboard');
  } catch (error) {
    console.error('Token exchange failed:', error);
    res.status(500).send('Token exchange failed');
  }
});

3. Secure Token Storage

For Most Web Apps:

// ✅ Store in secure HTTP-only cookies
app.use(session({
  secret: process.env.SESSION_SECRET,
  httpOnly: true,
  secure: true, // HTTPS only
  sameSite: 'strict'
}));

// ❌ Don't store in localStorage or sessionStorage for sensitive tokens

For SPAs (if you must store client-side):

// Use short-lived tokens and automatic refresh
const TOKEN_REFRESH_THRESHOLD = 5 * 60 * 1000; // 5 minutes before expiring for instance

function scheduleTokenRefresh(expiresIn) {
  const refreshTime = (expiresIn * 1000) - TOKEN_REFRESH_THRESHOLD;
  setTimeout(refreshAccessToken, refreshTime);
}

4. Token Validation and Verification

// Validate JWT tokens
const jwt = require('jsonwebtoken');

function validateAccessToken(token) {
  try {
    const decoded = jwt.verify(token, PUBLIC_KEY, {
      issuer: 'https://your-auth-url.com',
      audience: CLIENT_ID,
      algorithms: ['RS256']
    });
    
    // Check token expiration
    if (decoded.exp < Date.now() / 1000) {
      throw new Error('Token expired');
    }
    
    return decoded;
  } catch (error) {
    throw new Error('Invalid token');
  }
}

Common Pitfalls to Avoid

❌ Don't: Store tokens in URL parameters or query strings
❌ Don't: Skip state parameter validation
❌ Don't: Store sensitive tokens in localStorage
❌ Don't: Use HTTP for OAuth endpoints
❌ Don't: Ignore token expiration

✅ Do: Use Authorization Code
✅ Do: Implement proper token refresh logic
✅ Do: Validate all OAuth parameters
✅ Do: Use secure, HTTP-only cookies when possible
✅ Do: Implement proper error handling and logging
✅ Do: Follow the principle of least privilege for scopes

Testing Your OAuth Implementation

// Example test cases
describe('OAuth Implementation', () => {
  test('should reject invalid state parameter', async () => {
    const response = await request(app)
      .get('/callback?code=valid_code&state=invalid_state')
      .expect(400);
  });
  
  test('should refresh tokens before expiration', async () => {
    // Mock expired token scenario
    const result = await refreshTokens();
    expect(result.access_token).toBeDefined();
  });
  
  test('should securely store tokens', () => {
    // Verify tokens are not in localStorage or URL
    expect(localStorage.getItem('access_token')).toBeNull();
  });
});

This guide provides a foundation for implementing secure OAuth 2.0 authentication, in particular getting a token from a user name and password, to start the process. Nextly we will cover generating a refresh token for secure token management.