Skip to content

oauth authentication javascript auth login nodejs react nextjs security zenuxs

Notifications You must be signed in to change notification settings

zenuxs-dev/zenuxs-oauth

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

42 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸ” Zenuxs OAuth

Universal OAuth 2.0 + PKCE Client for Modern Applications

npm version License: MIT Bundle Size Downloads

A comprehensive, production-ready OAuth 2.0 + PKCE client library that works seamlessly across Browser, Node.js, React Native, and Web Workers. Built with security, developer experience, and universal compatibility in mind.


πŸš€ Why Zenuxs OAuth?

Universal Platform Support

Unlike most OAuth libraries that lock you into a specific environment, Zenuxs OAuth works everywhere:

  • βœ… Browser (Chrome, Firefox, Safari, Edge)
  • βœ… Node.js (Server-side authentication)
  • βœ… React Native (iOS & Android)
  • βœ… Web Workers (Background authentication)

Enterprise-Grade Security

  • πŸ”’ PKCE (RFC 7636) - Protection against authorization code interception
  • πŸ›‘οΈ CSRF Protection - Built-in state parameter validation
  • πŸ” Secure Token Storage - Flexible storage options (Memory, Session, Local)
  • ⚑ Automatic Token Refresh - Seamless token renewal before expiration
  • 🚫 Token Revocation - Properly invalidate tokens on logout

Developer-First Experience

  • πŸ“¦ Zero Dependencies - Lightweight and fast
  • 🎯 TypeScript Support - Full type definitions included
  • πŸ”Œ Multiple Auth Flows - Redirect, Popup, and Manual flows
  • πŸ“‘ Event System - React to authentication state changes
  • 🎨 Framework Agnostic - Works with React, Vue, Angular, Svelte, or vanilla JS
  • πŸ“š Comprehensive Documentation - Clear examples and API reference

πŸ“¦ Installation

Browser (CDN)

<script src="https://unpkg.com/zenuxs-oauth@2.3.1/dist/zenux-oauth.min.js"></script>

NPM / Yarn

npm install zenuxs-oauth
# or
yarn add zenuxs-oauth

ES6 Module

import ZenuxOAuth from 'zenuxs-oauth';

CommonJS

const ZenuxOAuth = require('zenuxs-oauth');

🎯 Quick Start

Browser - Popup Flow

const oauth = new ZenuxOAuth({
    clientId: "your-client-id",
    authServer: "https://api.auth.zenuxs.in",
    redirectUri: window.location.origin + "/callback.html",
    scopes: "openid profile email",
    storage: "sessionStorage"
});

// Login with popup
async function login() {
    try {
        const tokens = await oauth.login({ popup: true });
        console.log("Logged in!", tokens);
    } catch (error) {
        console.error("Login failed:", error);
    }
}

// Get user info
async function getUserInfo() {
    const user = await oauth.getUserInfo();
    console.log("User:", user);
}

// Logout
async function logout() {
    await oauth.logout({ revokeTokens: true });
}

Node.js - Server-Side

const ZenuxOAuth = require('zenuxs-oauth');

const oauth = new ZenuxOAuth({
    clientId: process.env.CLIENT_ID,
    authServer: "https://api.auth.zenuxs.in",
    redirectUri: "https://yourapp.com/callback",
    scopes: "openid profile email",
    storage: "memory",
    fetchFunction: require('node-fetch')
});

// Express.js route
app.get('/auth/login', async (req, res) => {
    const authData = await oauth.login();
    req.session.state = authData.state;
    req.session.codeVerifier = authData.codeVerifier;
    res.redirect(authData.url);
});

app.get('/auth/callback', async (req, res) => {
    const tokens = await oauth.handleCallback(req.url);
    req.session.tokens = tokens;
    res.redirect('/dashboard');
});

React Native

import ZenuxOAuth from 'zenuxs-oauth';
import { Linking } from 'react-native';

const oauth = new ZenuxOAuth({
    clientId: "your-client-id",
    authServer: "https://api.auth.zenuxs.in",
    redirectUri: "myapp://callback",
    scopes: "openid profile email",
    storage: "memory"
});

async function login() {
    const authData = await oauth.login();
    await Linking.openURL(authData.url);
    
    // Listen for callback
    Linking.addEventListener('url', async (event) => {
        if (event.url.startsWith('myapp://callback')) {
            const tokens = await oauth.handleCallback(event.url);
            console.log("Tokens:", tokens);
        }
    });
}

🎨 Features Overview

🌐 Multiple Authentication Flows

1. Redirect Flow (Traditional)

// Redirects the entire page
oauth.login();

2. Popup Flow (Modern)

// Opens authentication in a popup window
const tokens = await oauth.login({ 
    popup: true,
    popupWidth: 600,
    popupHeight: 700
});

3. Manual Flow (Non-Browser)

// Get authorization URL for manual handling
const authData = await oauth.login();
console.log("Redirect user to:", authData.url);
// Handle callback manually with authData.state and authData.codeVerifier

πŸ”„ Automatic Token Refresh

const oauth = new ZenuxOAuth({
    clientId: "your-client-id",
    authServer: "https://api.auth.zenuxs.in",
    autoRefresh: true,           // Enable auto-refresh
    refreshThreshold: 300        // Refresh 5 minutes before expiry
});

// Listen to refresh events
oauth.on('tokenRefresh', (newTokens) => {
    console.log("Tokens automatically refreshed!");
});

πŸ“‘ Comprehensive Event System

// Authentication events
oauth.on('login', (tokens) => {
    console.log("User logged in");
});

oauth.on('logout', () => {
    console.log("User logged out");
});

// Token management events
oauth.on('tokenRefresh', (newTokens) => {
    console.log("Tokens refreshed");
});

oauth.on('tokenExpired', () => {
    console.log("Token expired");
});

// Error handling
oauth.on('error', (error) => {
    console.error("OAuth error:", error);
});

// State changes
oauth.on('stateChange', (change) => {
    console.log("State changed:", change);
});

πŸ›‘οΈ Built-in Callback Handler

Create a beautiful, functional callback page with zero effort:

<!DOCTYPE html>
<html>
<head>
    <title>OAuth Callback</title>
</head>
<body>
    <div id="zenux-oauth-callback-container"></div>
    <script src="https://unpkg.com/zenuxs-oauth@2.3.1/dist/zenux-oauth.min.js"></script>
    <script>
        // Automatically handles OAuth callback and closes popup
        window.zenuxOAuthCallback = new ZenuxOAuthCallbackHandler({
            debug: true,
            autoClose: true,
            autoCloseDelay: 2000,
            successMessage: "Authentication successful! Redirecting...",
            errorMessage: "Authentication failed. Please try again."
        });
    </script>
</body>
</html>

πŸ’Ύ Flexible Storage Options

// Session Storage (default) - survives page reload, cleared on tab close
storage: "sessionStorage"

// Local Storage - persists across browser sessions
storage: "localStorage"

// Memory Storage - cleared on page reload (best for Node.js/React Native)
storage: "memory"

// Custom prefix for storage keys
storagePrefix: "myapp_auth_"

πŸ” Token Management

// Check authentication status
if (oauth.isAuthenticated()) {
    console.log("User is authenticated");
}

// Get current tokens
const tokens = oauth.getTokens();

// Check if token is expired
if (oauth.isTokenExpired()) {
    await oauth.refreshTokens();
}

// Manually refresh tokens
const newTokens = await oauth.refreshTokens();

// Revoke specific token
await oauth.revokeToken(tokens.access_token, 'access_token');

// Revoke all tokens on logout
await oauth.logout({ revokeTokens: true });

// Introspect token validity
const tokenInfo = await oauth.introspectToken();
console.log("Token active:", tokenInfo.active);

πŸ‘€ User Information

// Get user profile from userinfo endpoint
const user = await oauth.getUserInfo();
console.log(user.name, user.email, user.picture);

// Multiple userinfo endpoints supported
const oauth = new ZenuxOAuth({
    clientId: "your-client-id",
    authServer: "https://api.auth.zenuxs.in",
    userinfoEndpoint: "/oauth/userinfo"  // or custom endpoint
});

πŸ“€ Session Export/Import

// Export session (for cross-device sync or persistence)
const sessionData = oauth.exportSession();
localStorage.setItem('oauth_backup', JSON.stringify(sessionData));

// Import session (restore authentication state)
const savedSession = JSON.parse(localStorage.getItem('oauth_backup'));
oauth.importSession(savedSession);

🎯 Authenticated Fetch

// Get pre-configured fetch with automatic token injection
const authFetch = oauth.getAuthenticatedFetch();

// Use it like regular fetch
const response = await authFetch('https://api.yourapp.com/protected', {
    method: 'GET'
});

// Automatically adds Authorization header and handles token refresh

πŸ”§ Advanced Configuration

Complete Configuration Object

const oauth = new ZenuxOAuth({
    // Required
    clientId: "your-client-id",
    
    // Server Configuration
    authServer: "https://api.auth.zenuxs.in",
    authorizeEndpoint: "/oauth/authorize",
    tokenEndpoint: "/oauth/token",
    userinfoEndpoint: "/oauth/userinfo",
    revokeEndpoint: "/oauth/revoke",
    introspectEndpoint: "/oauth/introspect",
    
    // OAuth Parameters
    redirectUri: window.location.origin + "/callback.html",
    scopes: "openid profile email offline_access",
    responseType: "code",
    
    // Security
    usePKCE: true,              // Enable PKCE
    useCSRF: true,              // Enable CSRF protection (browser only)
    validateState: true,         // Validate state parameter
    
    // Storage
    storage: "sessionStorage",   // sessionStorage | localStorage | memory
    storagePrefix: "zenux_oauth_",
    
    // Token Management
    autoRefresh: true,           // Enable automatic token refresh
    refreshThreshold: 300,       // Refresh 5 minutes before expiry
    
    // UI Configuration (Browser only)
    popupWidth: 600,
    popupHeight: 700,
    popupFeatures: "toolbar=no,location=no,status=no,menubar=no",
    
    // Lifecycle Callbacks
    onBeforeLogin: (config) => {
        console.log("About to login");
    },
    onAfterLogin: (tokens) => {
        console.log("Login successful");
    },
    onBeforeLogout: () => {
        console.log("About to logout");
    },
    onAfterLogout: () => {
        console.log("Logout complete");
    },
    
    // Additional Parameters
    extraAuthParams: {
        prompt: "login",
        display: "popup"
    },
    extraTokenParams: {
        client_secret: "secret"  // Only for confidential clients
    },
    
    // Environment
    environment: "browser",      // Auto-detected: browser | node | react-native | worker
    fetchFunction: fetch,        // Custom fetch implementation
    debug: true                  // Enable debug logging
});

🎭 Framework Integration Examples

React Hook

import { useState, useEffect } from 'react';
import ZenuxOAuth from 'zenuxs-oauth';

function useZenuxAuth(config) {
    const [isAuthenticated, setIsAuthenticated] = useState(false);
    const [user, setUser] = useState(null);
    const [loading, setLoading] = useState(true);
    const [oauth] = useState(() => new ZenuxOAuth(config));

    useEffect(() => {
        setIsAuthenticated(oauth.isAuthenticated());
        
        oauth.on('login', async (tokens) => {
            setIsAuthenticated(true);
            const userInfo = await oauth.getUserInfo();
            setUser(userInfo);
        });

        oauth.on('logout', () => {
            setIsAuthenticated(false);
            setUser(null);
        });

        setLoading(false);

        return () => {
            oauth.off('login');
            oauth.off('logout');
        };
    }, [oauth]);

    return {
        isAuthenticated,
        user,
        loading,
        login: (options) => oauth.login(options),
        logout: (options) => oauth.logout(options),
        getTokens: () => oauth.getTokens()
    };
}

// Usage in component
function App() {
    const { isAuthenticated, user, loading, login, logout } = useZenuxAuth({
        clientId: "your-client-id",
        authServer: "https://api.auth.zenuxs.in",
        redirectUri: window.location.origin + "/callback.html",
        scopes: "openid profile email"
    });

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

    return (
        <div>
            {isAuthenticated ? (
                <div>
                    <h1>Welcome, {user?.name}!</h1>
                    <button onClick={() => logout({ revokeTokens: true })}>
                        Logout
                    </button>
                </div>
            ) : (
                <button onClick={() => login({ popup: true })}>
                    Login with Zenuxs
                </button>
            )}
        </div>
    );
}

Vue 3 Composable

import { ref, onMounted, onUnmounted } from 'vue';
import ZenuxOAuth from 'zenuxs-oauth';

export function useZenuxAuth(config) {
    const isAuthenticated = ref(false);
    const user = ref(null);
    const loading = ref(true);
    
    let oauth;

    onMounted(() => {
        oauth = new ZenuxOAuth(config);
        isAuthenticated.value = oauth.isAuthenticated();
        
        oauth.on('login', async (tokens) => {
            isAuthenticated.value = true;
            user.value = await oauth.getUserInfo();
        });

        oauth.on('logout', () => {
            isAuthenticated.value = false;
            user.value = null;
        });

        loading.value = false;
    });

    onUnmounted(() => {
        if (oauth) {
            oauth.off('login');
            oauth.off('logout');
        }
    });

    return {
        isAuthenticated,
        user,
        loading,
        login: (options) => oauth.login(options),
        logout: (options) => oauth.logout(options)
    };
}

Angular Service

import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
import ZenuxOAuth from 'zenuxs-oauth';

@Injectable({
    providedIn: 'root'
})
export class AuthService {
    private oauth: any;
    private isAuthenticatedSubject = new BehaviorSubject<boolean>(false);
    private userSubject = new BehaviorSubject<any>(null);

    public isAuthenticated$: Observable<boolean> = this.isAuthenticatedSubject.asObservable();
    public user$: Observable<any> = this.userSubject.asObservable();

    constructor() {
        this.oauth = new ZenuxOAuth({
            clientId: 'your-client-id',
            authServer: 'https://api.auth.zenuxs.in',
            redirectUri: window.location.origin + '/callback.html',
            scopes: 'openid profile email'
        });

        this.isAuthenticatedSubject.next(this.oauth.isAuthenticated());

        this.oauth.on('login', async (tokens: any) => {
            this.isAuthenticatedSubject.next(true);
            const user = await this.oauth.getUserInfo();
            this.userSubject.next(user);
        });

        this.oauth.on('logout', () => {
            this.isAuthenticatedSubject.next(false);
            this.userSubject.next(null);
        });
    }

    async login(options?: any): Promise<void> {
        await this.oauth.login(options);
    }

    async logout(options?: any): Promise<void> {
        await this.oauth.logout(options);
    }

    getTokens() {
        return this.oauth.getTokens();
    }
}

πŸ“Š Comparison with Other OAuth Libraries

Feature Zenuxs OAuth Auth0-SPA Firebase Auth Hello.js OAuth2-Client
Universal Support βœ… All platforms ❌ Browser only ⚠️ Limited ❌ Browser only ⚠️ Node only
PKCE Support βœ… Built-in βœ… Yes βœ… Yes ❌ No ⚠️ Manual
Popup Flow βœ… Native βœ… Yes ❌ No βœ… Yes ❌ No
Auto Token Refresh βœ… Configurable βœ… Yes βœ… Yes ❌ No ⚠️ Manual
Event System βœ… Comprehensive ⚠️ Limited βœ… Good ❌ No ❌ No
Zero Dependencies βœ… Yes ❌ No ❌ No βœ… Yes ❌ No
TypeScript βœ… Full support βœ… Yes βœ… Yes ❌ No βœ… Yes
Bundle Size 🟒 ~15KB 🟑 ~50KB πŸ”΄ ~150KB 🟒 ~10KB 🟑 ~30KB
React Native βœ… Native ❌ No βœ… Separate pkg ❌ No ❌ No
Web Workers βœ… Yes ❌ No ❌ No ❌ No ❌ No
Custom Storage βœ… Flexible ⚠️ Limited ❌ Fixed ❌ Fixed ⚠️ Limited
Token Revocation βœ… Built-in βœ… Yes ⚠️ Limited ❌ No ⚠️ Manual
Session Export βœ… Yes ❌ No ❌ No ❌ No ❌ No
Learning Curve 🟒 Low 🟑 Medium 🟑 Medium 🟒 Low πŸ”΄ High
Provider Lock-in βœ… None πŸ”΄ Auth0 only πŸ”΄ Firebase only ⚠️ Multiple βœ… None
License βœ… MIT βœ… MIT ⚠️ Proprietary βœ… MIT βœ… MIT

Key Advantages

🎯 Zenuxs OAuth stands out with:

  1. True Universal Support - One library for browser, Node.js, React Native, and Web Workers
  2. Zero Dependencies - No bloat, just pure OAuth functionality
  3. Developer Experience - Intuitive API with comprehensive events
  4. Flexibility - Works with any OAuth 2.0 provider, not locked to a specific service
  5. Modern Architecture - Built with PKCE, CSRF protection, and auto-refresh from the ground up
  6. Session Portability - Export/import sessions for cross-device authentication
  7. Lightweight - Only ~15KB minified + gzipped

πŸ”’ Security Best Practices

1. Always Use PKCE

const oauth = new ZenuxOAuth({
    clientId: "your-client-id",
    usePKCE: true  // Always enabled by default
});

2. Enable CSRF Protection

const oauth = new ZenuxOAuth({
    clientId: "your-client-id",
    useCSRF: true,      // Browser only
    validateState: true  // Verify state parameter
});

3. Use Secure Storage

// For web apps: Use sessionStorage (cleared on tab close)
storage: "sessionStorage"

// For SPAs with persistence: Use localStorage with caution
storage: "localStorage"

// For server-side: Always use memory storage
storage: "memory"

4. Revoke Tokens on Logout

await oauth.logout({ 
    revokeTokens: true  // Properly invalidate tokens
});

5. Handle Token Refresh Gracefully

oauth.on('tokenRefresh', (newTokens) => {
    // Update your application state
    updateAuthState(newTokens);
});

oauth.on('error', async (error) => {
    if (error.code === 'TOKEN_REFRESH_FAILED') {
        // Force re-login if refresh fails
        await oauth.logout();
        redirectToLogin();
    }
});

6. Implement Timeout for Popups

try {
    const tokens = await oauth.login({ 
        popup: true,
        timeout: 300000  // 5 minutes timeout
    });
} catch (error) {
    if (error.code === 'LOGIN_TIMEOUT') {
        console.log('Login took too long');
    }
}

πŸ› Error Handling

Error Codes Reference

try {
    await oauth.login({ popup: true });
} catch (error) {
    switch (error.code) {
        case 'INVALID_CONFIG':
            // Configuration validation failed
            break;
        case 'FETCH_UNAVAILABLE':
            // Fetch API not available
            break;
        case 'POPUP_BLOCKED':
            // Browser blocked popup window
            alert('Please allow popups for this site');
            break;
        case 'AUTH_CANCELLED':
            // User closed popup or cancelled authentication
            console.log('User cancelled login');
            break;
        case 'LOGIN_TIMEOUT':
            // Login process exceeded timeout
            console.log('Login timeout');
            break;
        case 'STATE_MISMATCH':
            // CSRF protection: state parameter mismatch
            console.error('Security error detected');
            break;
        case 'NO_AUTH_CODE':
            // Authorization code not received
            break;
        case 'TOKEN_EXCHANGE_FAILED':
            // Failed to exchange code for tokens
            break;
        case 'TOKEN_REFRESH_FAILED':
            // Failed to refresh access token
            await oauth.logout();
            break;
        case 'NO_REFRESH_TOKEN':
            // No refresh token available
            break;
        case 'NO_ACCESS_TOKEN':
            // No access token available
            break;
        case 'USERINFO_FAILED':
            // Failed to fetch user information
            break;
        case 'REVOKE_FAILED':
            // Token revocation failed
            break;
        case 'INTROSPECT_FAILED':
            // Token introspection failed
            break;
        default:
            console.error('Unknown error:', error);
    }
}

Custom Error Handling

// Global error handler
oauth.on('error', (error) => {
    console.error('OAuth Error:', {
        code: error.code,
        message: error.message,
        details: error.details,
        environment: error.environment,
        timestamp: error.timestamp
    });
    
    // Send to error tracking service
    trackError(error);
});

πŸ“š API Reference

Constructor

new ZenuxOAuth(config)

Methods

Authentication

  • login(options?) - Start OAuth flow
  • handleCallback(url) - Process OAuth callback
  • logout(options?) - Logout user

Token Management

  • getTokens() - Get current tokens
  • isAuthenticated() - Check authentication status
  • isTokenExpired() - Check if token is expired
  • refreshTokens() - Manually refresh tokens
  • revokeToken(token, tokenType) - Revoke specific token

User Information

  • getUserInfo() - Fetch user profile
  • introspectToken(token?) - Validate token

Session Management

  • getSessionState() - Get current session state
  • exportSession() - Export session data
  • importSession(data) - Import session data

Events

  • on(event, handler) - Add event listener
  • off(event, handler) - Remove event listener

Utilities

  • getAuthenticatedFetch() - Get authenticated fetch function
  • updateConfig(config) - Update configuration
  • destroy() - Cleanup resources

Static Methods

  • ZenuxOAuth.create(config) - Create new instance
  • ZenuxOAuth.getInstance(config) - Get singleton instance
  • ZenuxOAuth.destroyInstance() - Destroy singleton
  • ZenuxOAuth.createCallbackHandler(config) - Create callback handler

πŸ§ͺ Testing

Unit Testing with Jest

import ZenuxOAuth from 'zenuxs-oauth';

describe('ZenuxOAuth', () => {
    let oauth;

    beforeEach(() => {
        oauth = new ZenuxOAuth({
            clientId: 'test-client-id',
            authServer: 'https://test-auth.example.com',
            storage: 'memory'
        });
    });

    afterEach(() => {
        oauth.destroy();
    });

    test('should initialize correctly', () => {
        expect(oauth).toBeDefined();
        expect(oauth.isAuthenticated()).toBe(false);
    });

    test('should handle login flow', async () => {
        const authData = await oauth.login();
        expect(authData).toHaveProperty('url');
        expect(authData).toHaveProperty('state');
        expect(authData).toHaveProperty('codeVerifier');
    });

    test('should emit login event on successful authentication', (done) => {
        oauth.on('login', (tokens) => {
            expect(tokens).toHaveProperty('access_token');
            done();
        });

        // Simulate login...
    });
});

🀝 Contributing

We welcome contributions! Please see our Contributing Guide for details.

Development Setup

# Clone repository
git clone https://github.com/developers-rs5/zenuxs-oauth.git
cd zenuxs-oauth

# Install dependencies
npm install

# Run tests
npm test

# Build library
npm run build

# Run examples
npm run dev

πŸ“„ License

MIT License Β© 2025 Zenuxs Team

Developed by Rishabh Sharma (rs)


πŸ”— Links


πŸ’¬ Support

Need help? We're here for you:


πŸŽ‰ Acknowledgments

Special thanks to all contributors and the OAuth 2.0 community for making secure authentication accessible to everyone.


Made with ❀️ by the Zenuxs Team

About

oauth authentication javascript auth login nodejs react nextjs security zenuxs

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •