Documentation/SDKs/JavaScript/TypeScript

JavaScript/TypeScript SDK

Official OAuth42 SDK for JavaScript and TypeScript. Works in Node.js, browsers, and modern frameworks.

Installation

Install the OAuth42 SDK using npm, yarn, or pnpm:

npm
npm install @oauth42/client
yarn
yarn add @oauth42/client
pnpm
pnpm add @oauth42/client

TypeScript Support

The SDK is written in TypeScript and includes full type definitions. No additional @types packages needed.

Quick Start

1. Initialize the Client

import { OAuth42Client } from '@oauth42/client';

const client = new OAuth42Client({
  clientId: 'your_client_id',
  clientSecret: 'your_client_secret', // Optional, for server-side only
  redirectUri: 'https://yourapp.com/callback',
  issuer: 'https://oauth42.com', // Optional, defaults to oauth42.com
});

2. Start Authorization Flow

// Generate authorization URL with PKCE
const { url, codeVerifier, state } = await client.authorize({
  scope: ['openid', 'profile', 'email'],
  responseType: 'code',
});

// Store codeVerifier and state in session
sessionStorage.setItem('code_verifier', codeVerifier);
sessionStorage.setItem('state', state);

// Redirect user to authorization URL
window.location.href = url;

3. Handle Callback

// Parse callback URL
const params = new URLSearchParams(window.location.search);
const code = params.get('code');
const state = params.get('state');

// Verify state
const storedState = sessionStorage.getItem('state');
if (state !== storedState) {
  throw new Error('Invalid state parameter');
}

// Exchange code for tokens
const codeVerifier = sessionStorage.getItem('code_verifier');
const tokens = await client.exchangeCodeForTokens({
  code,
  codeVerifier,
});

// tokens.accessToken, tokens.refreshToken, tokens.idToken
console.log('Access token:', tokens.accessToken);

Configuration

The OAuth42Client constructor accepts the following options:

clientIdrequired

Your application's client ID from the OAuth42 dashboard.

redirectUrirequired

The redirect URI registered in your OAuth42 application settings.

clientSecretoptional

Your application's client secret. Only use on server-side! Never expose in browser code.

issueroptional

The OAuth42 issuer URL. Defaults to https://oauth42.com

scopeoptional

Default scopes to request. Can be overridden per authorization.

pkceoptional

Enable PKCE (default: true). Strongly recommended for all clients.

Authentication Flows

Authorization Code Flow with PKCE (Recommended)

Recommended for Single-Page Applications (React, Vue, Angular) and mobile apps.

// Start authorization
const { url, codeVerifier, state } = await client.authorize({
  scope: ['openid', 'profile', 'email'],
  responseType: 'code',
  state: 'random_state_value', // Optional, auto-generated if not provided
});

// Store for later
sessionStorage.setItem('code_verifier', codeVerifier);
sessionStorage.setItem('state', state);

// Redirect user
window.location.href = url;

// --- After redirect callback ---

// Exchange code for tokens
const tokens = await client.exchangeCodeForTokens({
  code: params.get('code'),
  codeVerifier: sessionStorage.getItem('code_verifier'),
});

Client Credentials Flow

For server-to-server communication. Requires client secret.

// Server-side only
const client = new OAuth42Client({
  clientId: process.env.OAUTH42_CLIENT_ID,
  clientSecret: process.env.OAUTH42_CLIENT_SECRET,
});

const tokens = await client.clientCredentials({
  scope: ['api:read', 'api:write'],
});

// Use access token for API calls
console.log(tokens.accessToken);

Refresh Token Flow

// Refresh expired access token
const newTokens = await client.refreshTokens({
  refreshToken: storedRefreshToken,
});

// Store new tokens
console.log('New access token:', newTokens.accessToken);
console.log('New refresh token:', newTokens.refreshToken);

User Management

Get User Info

const userInfo = await client.getUserInfo(accessToken);

console.log('User ID:', userInfo.sub);
console.log('Name:', userInfo.name);
console.log('Email:', userInfo.email);
console.log('Email verified:', userInfo.email_verified);

Update User Profile

await client.updateUser(accessToken, {
  name: 'John Doe',
  picture: 'https://example.com/avatar.jpg',
});

Change Password

await client.changePassword(accessToken, {
  currentPassword: 'old_password',
  newPassword: 'new_secure_password',
});

Token Management

Validate Token

const isValid = await client.validateToken(accessToken);

if (isValid) {
  console.log('Token is valid');
} else {
  console.log('Token is expired or invalid');
}

Introspect Token

const tokenInfo = await client.introspectToken(accessToken);

console.log('Active:', tokenInfo.active);
console.log('Expires at:', tokenInfo.exp);
console.log('Scopes:', tokenInfo.scope);
console.log('Client ID:', tokenInfo.client_id);

Revoke Token

// Revoke access token or refresh token
await client.revokeToken(token);

console.log('Token revoked successfully');

Multi-Factor Authentication

Setup MFA

const mfaSetup = await client.setupMFA(accessToken);

console.log('Secret:', mfaSetup.secret);
console.log('QR Code:', mfaSetup.qrCode); // Data URL for QR code
console.log('Backup codes:', mfaSetup.backupCodes);

// User scans QR code with authenticator app
// Then verify with first code
await client.verifyMFASetup(accessToken, {
  code: '123456', // From authenticator app
});

Verify MFA Code

// During login, after receiving MFA challenge
const tokens = await client.verifyMFA({
  mfaToken: mfaToken, // From authorization response
  code: '123456', // From authenticator app
});

console.log('Access token:', tokens.accessToken);

Disable MFA

await client.disableMFA(accessToken, {
  password: 'user_password', // Required for security
});

Error Handling

The SDK throws typed errors that you can catch and handle appropriately:

import { OAuth42Client, OAuth42Error } from '@oauth42/client';

try {
  const tokens = await client.exchangeCodeForTokens({
    code: authCode,
    codeVerifier: verifier,
  });
} catch (error) {
  if (error instanceof OAuth42Error) {
    console.error('OAuth42 Error:', error.message);
    console.error('Error code:', error.code);
    console.error('Status:', error.status);

    switch (error.code) {
      case 'invalid_grant':
        // Authorization code expired or invalid
        console.error('Authorization code is invalid');
        break;
      case 'invalid_client':
        // Client credentials are wrong
        console.error('Client authentication failed');
        break;
      case 'access_denied':
        // User denied authorization
        console.error('User denied access');
        break;
      default:
        console.error('Unknown error:', error.code);
    }
  } else {
    // Network or other error
    console.error('Unexpected error:', error);
  }
}

Common Error Codes

invalid_grant

Authorization code or refresh token is invalid or expired

invalid_client

Client authentication failed (wrong credentials)

invalid_request

Request is missing required parameters or malformed

unauthorized_client

Client is not authorized for this grant type

access_denied

User denied the authorization request

invalid_scope

Requested scope is invalid or not allowed

Framework Integration

React

import { useState, useEffect } from 'react';
import { OAuth42Client } from '@oauth42/client';

const client = new OAuth42Client({
  clientId: import.meta.env.VITE_OAUTH42_CLIENT_ID,
  redirectUri: `${window.location.origin}/callback`,
});

export function useAuth() {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const token = localStorage.getItem('access_token');
    if (token) {
      client.getUserInfo(token)
        .then(setUser)
        .catch(() => localStorage.removeItem('access_token'))
        .finally(() => setLoading(false));
    } else {
      setLoading(false);
    }
  }, []);

  const login = async () => {
    const { url, codeVerifier, state } = await client.authorize({
      scope: ['openid', 'profile', 'email'],
    });
    sessionStorage.setItem('code_verifier', codeVerifier);
    sessionStorage.setItem('state', state);
    window.location.href = url;
  };

  const logout = () => {
    localStorage.removeItem('access_token');
    setUser(null);
  };

  return { user, loading, login, logout };
}

// Usage in component
function App() {
  const { user, loading, login, logout } = useAuth();

  if (loading) return <div>Loading...</div>;

  return (
    <div>
      {user ? (
        <>
          <p>Welcome, {user.name}!</p>
          <button onClick={logout}>Logout</button>
        </>
      ) : (
        <button onClick={login}>Login</button>
      )}
    </div>
  );
}

Express.js

import express from 'express';
import session from 'express-session';
import { OAuth42Client } from '@oauth42/client';

const app = express();
const client = new OAuth42Client({
  clientId: process.env.OAUTH42_CLIENT_ID,
  clientSecret: process.env.OAUTH42_CLIENT_SECRET,
  redirectUri: 'http://localhost:3000/callback',
});

app.use(session({
  secret: process.env.SESSION_SECRET,
  resave: false,
  saveUninitialized: false,
}));

app.get('/login', async (req, res) => {
  const { url, codeVerifier, state } = await client.authorize({
    scope: ['openid', 'profile', 'email'],
  });
  req.session.codeVerifier = codeVerifier;
  req.session.state = state;
  res.redirect(url);
});

app.get('/callback', async (req, res) => {
  const { code, state } = req.query;

  if (state !== req.session.state) {
    return res.status(400).send('Invalid state');
  }

  const tokens = await client.exchangeCodeForTokens({
    code,
    codeVerifier: req.session.codeVerifier,
  });

  req.session.accessToken = tokens.accessToken;
  req.session.refreshToken = tokens.refreshToken;

  res.redirect('/dashboard');
});

app.get('/dashboard', async (req, res) => {
  if (!req.session.accessToken) {
    return res.redirect('/login');
  }

  const user = await client.getUserInfo(req.session.accessToken);
  res.json({ user });
});

app.listen(3000);

Best Practices

Always Use PKCE

PKCE is enabled by default and should always be used for SPAs and mobile apps to prevent authorization code interception attacks.

Never Expose Client Secret

Client secrets should only be used server-side. Never include them in browser JavaScript or mobile app code.

Implement Token Refresh

Automatically refresh access tokens before they expire to provide seamless user experience without re-authentication.

Validate State Parameter

Always verify the state parameter matches what you sent to prevent CSRF attacks during authorization.

Use Secure Storage

Store tokens securely. Use httpOnly cookies for server-side or secure session storage for client-side. Avoid localStorage for sensitive tokens.

Handle Errors Gracefully

Catch and handle OAuth42Error instances to provide meaningful error messages to users and implement appropriate retry logic.

Next Steps