API Docs Dashboard

Auth Platform API

Self-hosted authentication microservice with OAuth 2.0 Authorization Code Flow (PKCE), email OTP verification, RS256 JWT tokens, and multi-tenant application management.

OAuth 2.0 + PKCE

Secure authorization code flow with no client secrets required on the frontend

Hosted Login

Beautiful login UI rendered by the auth platform, like Google or GitHub

RS256 JWT

Asymmetric key signing with automatic token refresh

Real-Time Revocation

SSE-powered instant session invalidation when admin force-logouts a user

Multi-Tenant

Register multiple apps with isolated users and settings

Quick Start

Get up and running in 3 steps:

1

Register Your Application

Open the Admin Dashboard and create a new application. You'll receive a Client ID.

Admin Dashboard: __AUTH_SERVER__/dashboard/
2

Download the SDK

Get our JavaScript SDK to integrate authentication into your app.

Download auth-sdk.js
3

Initialize & Use

Add the SDK to your HTML and initialize with your Client ID.

<script src="auth-sdk.js"></script>
<script>
  const auth = new AuthClient({
    AUTH_SERVER: '__AUTH_SERVER__',
    CLIENT_ID: 'your-client-id',
    REDIRECT_URI: window.location.origin
  });
  await auth.handleCallback();
  if (auth.isAuthenticated()) {
    console.log('Logged in as', auth.getUser().email);
    auth.startAutoRefresh();
  } else {
    auth.login(); // Redirects to hosted login
  }
</script>

Download SDK

Our JavaScript SDK handles all OAuth 2.0 + PKCE complexity for you. Just download, configure, and integrate.

JavaScript SDK

Drop-in authentication for any frontend framework

  • OAuth 2.0 Authorization Code Flow with PKCE
  • Automatic token refresh & SSE real-time revocation
  • True encapsulation — internal state is inaccessible
  • Auth change events with logout reason
  • Zero dependencies & framework-agnostic
What's included:
  • auth-sdk.js — Complete AuthClient class with true # private fields
  • PKCE crypto utilities (code verifier, SHA-256 challenge)
  • Auto token refresh timer + SSE session stream for real-time revocation
  • Works as <script> tag (UMD) or ES module import

Integration Guide

1. Basic Setup

Configure the SDK using environment variables — never hardcode secrets or URLs in source code.

// Read from your environment / .env file
const auth = new AuthClient({
  AUTH_SERVER:  '__AUTH_SERVER__',          // or process.env.NEXT_PUBLIC_AUTH_SERVER
  CLIENT_ID:   'app_xxxxxxxxxxxx',          // from Admin Console
  REDIRECT_URI: window.location.origin,     // must match Admin Console
});

2. Handle Authentication

// On page load — check for OAuth callback
await auth.handleCallback();

if (auth.isAuthenticated()) {
  const user = auth.getUser();
  showDashboard(user);
  auth.startAutoRefresh();  // token refresh + SSE revocation stream
} else {
  showLoginButton();
}

3. Listen for Auth Changes

Get notified in real-time when the session state changes — including admin force-logouts.

auth.onAuthChange((isLoggedIn, reason) => {
  if (!isLoggedIn) {
    if (reason === 'revoked_by_admin') {
      showBanner('Your session was ended by an administrator');
    } else if (reason === 'session_expired') {
      showBanner('Session expired — please log in again');
    }
    redirectToLogin();
  }
});

4. Making Authenticated Requests

async function fetchUserData() {
  const token = auth.getAccessToken();
 
  const response = await fetch('https://your-api.com/user/profile', {
    headers: {
      'Authorization': `Bearer ${token}`,
      'Content-Type': 'application/json'
    }
  });
 
  return response.json();
}

5. Logout

document.getElementById('logoutBtn').onclick = () => {
  auth.logout();
  window.location.href = '/';
};

Environment Setup

The SDK reads its configuration from a config object — use your framework's environment variables to supply the values. Never commit real credentials to source control.

Next.js

# .env.local
NEXT_PUBLIC_AUTH_SERVER=__AUTH_SERVER__
NEXT_PUBLIC_CLIENT_ID=app_xxxxxxxxxxxx
NEXT_PUBLIC_REDIRECT_URI=http://localhost:3000/callback
// lib/config.js
export const AUTH_CONFIG = {
  AUTH_SERVER:  process.env.NEXT_PUBLIC_AUTH_SERVER,
  CLIENT_ID:   process.env.NEXT_PUBLIC_CLIENT_ID,
  REDIRECT_URI: process.env.NEXT_PUBLIC_REDIRECT_URI,
};

Vite (Vue / React / Svelte)

# .env
VITE_AUTH_SERVER=__AUTH_SERVER__
VITE_CLIENT_ID=app_xxxxxxxxxxxx
VITE_REDIRECT_URI=http://localhost:5173/callback
const auth = new AuthClient({
  AUTH_SERVER:  import.meta.env.VITE_AUTH_SERVER,
  CLIENT_ID:   import.meta.env.VITE_CLIENT_ID,
  REDIRECT_URI: import.meta.env.VITE_REDIRECT_URI,
});

Create React App

# .env
REACT_APP_AUTH_SERVER=__AUTH_SERVER__
REACT_APP_CLIENT_ID=app_xxxxxxxxxxxx
REACT_APP_REDIRECT_URI=http://localhost:3000/callback
const auth = new AuthClient({
  AUTH_SERVER:  process.env.REACT_APP_AUTH_SERVER,
  CLIENT_ID:   process.env.REACT_APP_CLIENT_ID,
  REDIRECT_URI: process.env.REACT_APP_REDIRECT_URI,
});

Plain HTML / Vanilla JS

Since plain HTML has no build step, pass values directly or use <meta> tags:

<script src="auth-sdk.js"></script>
<script>
  const auth = new AuthClient({
    AUTH_SERVER:  '__AUTH_SERVER__',
    CLIENT_ID:   'app_xxxxxxxxxxxx',
    REDIRECT_URI: window.location.origin,
  });
</script>

SDK Reference

The SDK uses true private fields (#) — internal state like tokens and streams are completely inaccessible from outside the class.

new AuthClient(config)

Create an auth client. Throws if AUTH_SERVER is missing.

const auth = new AuthClient({
  AUTH_SERVER:  process.env.NEXT_PUBLIC_AUTH_SERVER,
  CLIENT_ID:   process.env.NEXT_PUBLIC_CLIENT_ID,
  REDIRECT_URI: process.env.NEXT_PUBLIC_REDIRECT_URI,
});
AUTH_SERVERstringRequired. Auth platform URL (from env).
CLIENT_IDstringOAuth Client ID from Admin Console.
REDIRECT_URIstringCallback URL — must match Admin Console.

auth.login()

Redirect to the hosted login page (OAuth 2.0 + PKCE). Like "Sign in with Google" — your app never sees the password.

auth.login();

auth.logout(reason?)

Clear session, stop refresh timers & SSE stream, and fire the onAuthChange callback.

auth.logout();  // voluntary logout (reason = null)

auth.isAuthenticated() → boolean

Check if user has a valid, non-expired access token.

if (auth.isAuthenticated()) {
  console.log('User is logged in');
}

auth.getUser() → object | null

Get current user info decoded from the JWT. Returns a frozen object (cannot be mutated).

const user = auth.getUser();
// { email, user_id, app_id, issuer, expires_at, issued_at }

auth.getAccessToken() → string | null

Get the raw JWT for Authorization headers.

fetch('/api/data', {
  headers: { 'Authorization': `Bearer ${auth.getAccessToken()}` }
});

auth.handleCallback() → Promise<boolean>

Process the OAuth redirect. Call once on page load (or on your callback route). Returns true if tokens were received.

const handled = await auth.handleCallback();

auth.onAuthChange(callback)

Register a listener for authentication state changes. Fires on login, logout, and real-time admin revocation.

auth.onAuthChange((isLoggedIn, reason) => {
  // reason: 'revoked_by_admin' | 'session_expired' | null
  if (!isLoggedIn && reason === 'revoked_by_admin') {
    alert('Admin ended your session');
  }
});

auth.startAutoRefresh()

Enable two automatic features:

  • Token refresh — refreshes access token before it expires (checks every 15 s)
  • SSE revocation stream — opens a server-sent event connection that fires instantly when an admin force-logouts the user
if (auth.isAuthenticated()) {
  auth.startAutoRefresh();
}

auth.refreshAccessToken() → Promise<boolean>

Manually refresh the access token. Normally handled automatically by startAutoRefresh().

const ok = await auth.refreshAccessToken();

auth.verifyToken() → Promise<object | null>

Verify the current access token server-side and get the decoded payload.

const payload = await auth.verifyToken();

auth.getTimeUntilExpiry() → number

Seconds remaining until the access token expires (0 if expired or missing).

console.log(`Token expires in ${auth.getTimeUntilExpiry()}s`);

OAuth Endpoints

GET /oauth/authorize

Start OAuth authorization flow. Renders hosted login page.

Query Parameters

client_id string Your application's client ID
redirect_uri string Redirect URI after authentication
response_type string Must be "code"
state string CSRF protection token
code_challenge string PKCE code challenge (SHA256)
code_challenge_method string Must be "S256"
POST /oauth/token

Exchange authorization code for access and refresh tokens.

Request Body

{
  "grant_type": "authorization_code",
  "code": "auth_code_here",
  "client_id": "your_client_id",
  "redirect_uri": "https://your-app.com/callback",
  "code_verifier": "pkce_verifier"
}

Response

{
  "access_token": "eyJhbGc...",
  "token_type": "Bearer",
  "expires_in": 900,
  "refresh_token": "refresh_token_here"
}

Auth Endpoints

POST /auth/request-otp

Request OTP for email-based authentication.

Request Body

{
  "email": "user@example.com",
  "app_id": "app_xxxxxxxxxxxx"
}
POST /auth/verify-otp

Verify OTP and receive tokens.

Request Body

{
  "email": "user@example.com",
  "otp": "123456",
  "app_id": "app_xxxxxxxxxxxx"
}

Response

{
  "access_token": "eyJhbGc...",
  "refresh_token": "refresh_token_here"
}

Token Management

POST /token/refresh

Refresh an access token using a refresh token.

Request Body

{
  "refresh_token": "your_refresh_token"
}

Response

{
  "access_token": "eyJhbGc...",
  "token_type": "Bearer",
  "expires_in": 900
}
POST /token/verify

Verify token validity and decode payload.

Request Body

{
  "token": "eyJhbGc..."
}

Admin Endpoints

Admin endpoints require authentication with admin credentials.

GET /admin/apps

List all registered applications.

POST /admin/apps

Create a new application.

Request Body

{
  "app_name": "My App",
  "redirect_uris": ["https://myapp.com/callback"],
  "session_duration": 900,
  "refresh_token_duration": 604800
}
GET /admin/users

List all users across apps.

GET /admin/stats

Get dashboard statistics (app count, user count, active sessions).

Code Examples

React / Next.js

// .env.local
// NEXT_PUBLIC_AUTH_SERVER=__AUTH_SERVER__
// NEXT_PUBLIC_CLIENT_ID=app_xxxxxxxxxxxx
// NEXT_PUBLIC_REDIRECT_URI=http://localhost:3000/callback

import AuthClient from './auth-sdk';
import { AUTH_CONFIG } from './config';
import { useEffect, useState } from 'react';

function App() {
  const [user, setUser] = useState(null);
  const [logoutReason, setLogoutReason] = useState(null);
  const [auth] = useState(() => new AuthClient(AUTH_CONFIG));

  useEffect(() => {
    async function init() {
      await auth.handleCallback();

      auth.onAuthChange((loggedIn, reason) => {
        if (loggedIn) {
          setUser(auth.getUser());
          setLogoutReason(null);
        } else {
          setUser(null);
          setLogoutReason(reason);
        }
      });

      if (auth.isAuthenticated()) {
        setUser(auth.getUser());
        auth.startAutoRefresh();
      }
    }
    init();
  }, []);

  if (logoutReason === 'revoked_by_admin') {
    return <div className="banner">Admin ended your session</div>;
  }
  if (!user) {
    return <button onClick={() => auth.login()}>Login</button>;
  }
  return (
    <div>
      <h1>Welcome, {user.email}</h1>
      <button onClick={() => auth.logout()}>Logout</button>
    </div>
  );
}

Vue.js (Vite)

// .env
// VITE_AUTH_SERVER=__AUTH_SERVER__
// VITE_CLIENT_ID=app_xxxxxxxxxxxx
// VITE_REDIRECT_URI=http://localhost:5173/callback

import AuthClient from './auth-sdk';

export default {
  data() {
    return {
      auth: new AuthClient({
        AUTH_SERVER:  import.meta.env.VITE_AUTH_SERVER,
        CLIENT_ID:   import.meta.env.VITE_CLIENT_ID,
        REDIRECT_URI: import.meta.env.VITE_REDIRECT_URI,
      }),
      user: null,
      logoutReason: null
    };
  },
  async mounted() {
    this.auth.onAuthChange((loggedIn, reason) => {
      this.user = loggedIn ? this.auth.getUser() : null;
      this.logoutReason = reason;
    });
    await this.auth.handleCallback();
    if (this.auth.isAuthenticated()) {
      this.user = this.auth.getUser();
      this.auth.startAutoRefresh();
    }
  },
  methods: {
    login()  { this.auth.login(); },
    logout() { this.auth.logout(); }
  }
};

Protected API Route (Express)

const jwt = require('jsonwebtoken');
const fs = require('fs');
const publicKey = fs.readFileSync('public_key.pem');
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, publicKey, { algorithms: ['RS256'] }, (err, user) => {
    if (err) return res.sendStatus(403);
    req.user = user;
    next();
  });
}
app.get('/api/protected', authenticateToken, (req, res) => {
  res.json({ message: 'Protected data', user: req.user });
});

Architecture & Flow Diagrams

Interactive sequence diagrams for all authentication flows — OAuth 2.0, OTP, WebAuthn, token refresh, multi-tenant isolation, and more.