GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — GitHub - SecureAuthSystem/SecureAuth

BranchesTags
Open more actions menu

Latest commit

 

History

21 Commits
21 Commits

Folders and files

NameName
Last commit message
Last commit date
.github
.github
 
 
.well-known
.well-known
 
 
apps/example-express
apps/example-express
 
 
benchmarks
benchmarks
 
 
examples
examples
 
 
packages
packages
 
 
scripts
scripts
 
 
.env.example
.env.example
 
 
.gitignore
.gitignore
 
 
CHANGELOG.md
CHANGELOG.md
 
 
FinalScope.md
FinalScope.md
 
 
LICENSE
LICENSE
 
 
README.md
README.md
 
 
SECURITY.md
SECURITY.md
 
 
SecureAuth(1).md
SecureAuth(1).md
 
 
package-lock.json
package-lock.json
 
 
package.json
package.json
 
 
tsconfig.base.json
tsconfig.base.json
 
 

Repository files navigation

SecureAuth SDK

A batteries-included, production-ready PASETO v4 authentication engine for Node.js.

npm version License: MIT

The Problem with JWT

JSON Web Tokens (JWT) are ubiquitous, but they are fundamentally flawed. They place the burden of cryptographic decision-making on developers, leading to massive security vulnerabilities like "algorithm confusion" attacks (alg: none). Furthermore, JWTs are completely stateless by default. This makes proper token revocation (e.g., logging out a compromised device or banning a user instantly) impossible without building complex infrastructure from scratch.

SecureAuth SDK solves this. Built entirely on PASETO v4 (Platform-Agnostic Security Tokens), it completely eliminates algorithm confusion by making cryptography structural rather than configurable. More importantly, SecureAuth provides a batteries-included Redis-backed stateful layer, offering instant token revocation, HTTP-only refresh token rotation, and strict device fingerprinting out of the box.

Security Comparison

Feature Standard JWT Raw PASETO SecureAuth SDK
Cryptography Configurable (Dangerous) Fixed (Secure) Fixed (Secure)
Algorithm Confusion Vulnerable (alg: none) Immune Immune
Revocation Impossible / DIY Impossible / DIY Instant (Redis-backed)
Refresh Rotation DIY DIY Automatic & Transparent
Rate Limiting DIY DIY Atomic (Redis Lua Scripts)
Token Replay Attacks Vulnerable if stolen Vulnerable if stolen Blocked (Device Fingerprinting)

Getting Started in 5 Minutes

1. Installation

Install the SecureAuth server package and peer dependencies:

npm install secure-auth-server ioredis express cookie-parser

(Note: SecureAuth relies on Redis to manage real-time revocation and session state).

2. Generate Keys

SecureAuth uses Ed25519 asymmetric cryptography. Generate your secure key pair:

npx tsx -e "import { SecureAuth } from 'secure-auth-server'; console.log(SecureAuth.generateKeys());"

Copy the generated keys into your .env file:

PASETO_PRIVATE_KEY=302e...
PASETO_PUBLIC_KEY=302a...
REDIS_URL=redis://localhost:6379

3. Basic Implementation

Here is a complete, working example using Express:

import express from 'express';
import cookieParser from 'cookie-parser';
import { SecureAuth } from 'secure-auth-server';

const app = express();
app.use(express.json());
app.use(cookieParser()); // Required for refresh token rotation

// 1. Initialize the Engine
const auth = new SecureAuth({
  redis: { url: process.env.REDIS_URL! },
  keys: {
    private: process.env.PASETO_PRIVATE_KEY!,
    public: process.env.PASETO_PUBLIC_KEY!,
  },
  tokenTTL: 900,         // Access tokens expire in 15 mins
  refreshTTL: 604800,    // Refresh tokens expire in 7 days
  bindingMode: 'ua-only', // Binds tokens to the browser's User-Agent
  onRedisError: 'fail-open', // Allow auth when Redis is down
  cookieOptions: {
    secure: true,        // Set to false for local dev without HTTPS
    sameSite: 'strict',
    domain: '.example.com' // Optional: share cookies across subdomains
  }
});

// 2. Add Auto-Managed Routes
// Mounts POST /auth/refresh, POST /auth/logout, and POST /auth/logout/all
app.use('/auth', auth.router());

// 3. Login Endpoint (Your custom logic)
app.post('/login', async (req, res) => {
  const { username, password } = req.body;
  
  // Verify user credentials against your DB...
  if (username === 'alice' && password === 'secret') {
    const userId = 'user_123';
    const roles = ['user'];
    
    // Issue tokens: passing `res` automatically sets the refresh token
    // as an httpOnly cookie with the SDK's secure configuration.
    const tokens = await auth.issueTokens(userId, roles, req, res);
    
    res.json({
      accessToken: tokens.accessToken,
      expiresAt: tokens.accessTokenExpiresAt
    });
  } else {
    res.status(401).json({ error: 'Invalid credentials' });
  }
});

// 4. Protected Route
// auth.verify() checks signature, expiration, Redis revocation, and fingerprint
app.get('/api/protected', auth.verify(), (req, res) => {
  // Access validated user details from req.auth
  res.json({
    message: 'Welcome to the protected zone!',
    userId: req.auth!.sub,
    roles: req.auth!.roles
  });
});

app.listen(3000, () => console.log('SecureAuth server running on port 3000'));

Note: Passing res to issueTokens() is optional. If omitted, you can set the cookie manually using auth.setRefreshCookie(res, tokens.refreshToken). The auto-cookie approach is recommended to avoid duplicating cookie configuration.


How It Works

The auth.verify() middleware performs a 4-step validation chain on every request:

Bearer Token → Ed25519 Signature Check → Expiry/nbf Check → Redis Revocation Check → Fingerprint Match
  1. Signature Verification: Uses the PASETO library to verify the Ed25519 signature. A tampered token is rejected immediately.
  2. Time Claims: Checks exp (expiry) and nbf (not-before). An expired or future token is rejected.
  3. Revocation Check: Queries Redis for revoked:{jti}. If the key exists, the token has been revoked via logout().
  4. Fingerprint Match: Computes sha256(userAgent) from the request and compares to the fp claim in the token. A mismatch indicates the token was stolen and replayed from a different device.

If all checks pass, req.auth is set with the decoded identity:

interface AuthContext {
  sub: string;    // User ID
  jti: string;    // Unique token ID
  roles: string[]; // User roles
  fp: string;     // Device fingerprint
}

Configuration

Option Type Default Description
redis.url string required Redis connection URL (e.g., redis://localhost:6379)
keys.private string required Ed25519 private key (PKCS#8 DER, hex-encoded)
keys.public string required Ed25519 public key (SPKI DER, hex-encoded)
tokenTTL number 900 Access token lifetime in seconds (15 minutes)
refreshTTL number 604800 Refresh token lifetime in seconds (7 days)
bindingMode string 'ua-only' Device fingerprint binding mode (see below)
onRedisError string 'fail-open' Strategy when Redis is down: 'fail-open' (allow auth but disable revocation checks) or 'fail-closed' (block auth completely).
cookieOptions object { secure: true, ... } Override default secure cookie settings for refresh tokens (e.g. domain, secure).

Device Fingerprinting

SecureAuth prevents stolen tokens from being used on different devices. Configure the bindingMode during initialization:

Mode Fingerprint Formula Use Case
'strict' sha256(ip + userAgent) High-security apps. Rejects if IP or UA changes.
'ua-only' sha256(userAgent) Recommended. Tolerates IP changes (mobile networks).
'none' Disabled Testing or apps where replay protection is handled elsewhere.

Advanced Usage

Emergency User Revocation

If a user account is compromised, you can instantly kill all their active sessions across all devices from your backend:

await auth.revokeUser('user_123');

This revokes all access tokens (via Redis JTI blocklist) and all refresh tokens simultaneously.

Session Listing

List all active sessions for the authenticated user:

GET /auth/sessions
Authorization: Bearer <access_token>

Returns:

{
  "sessions": [
    { "jti": "abc123...", "expiresAt": "2026-07-25T15:30:00.000Z" },
    { "jti": "def456...", "expiresAt": "2026-07-25T16:00:00.000Z" }
  ]
}

Health Check

const isHealthy = await auth.healthCheck(); // true if Redis is reachable

Error Codes

All error responses follow a consistent format:

{
  "error": {
    "code": "ERROR_CODE",
    "message": "Human-readable description"
  }
}
Code HTTP Status Meaning
MISSING_TOKEN 401 No Authorization: Bearer header provided
TOKEN_INVALID 401 PASETO signature is invalid (tampered or wrong key)
TOKEN_EXPIRED 401 Access token's exp claim is in the past
TOKEN_REVOKED 401 Token's jti is in the Redis revocation blocklist
FINGERPRINT_MISMATCH 401 Request device fingerprint doesn't match token's fp claim
REFRESH_TOKEN_INVALID 401 Refresh token is missing, expired, or not found in Redis
RATE_LIMIT_EXCEEDED 429 Too many requests — includes Retry-After header
VALIDATION_ERROR 400 Request body failed Zod schema validation
SERVICE_UNAVAILABLE 503 Redis is down and onRedisError is set to 'fail-closed'
KEY_LOADING_ERROR 500 Server misconfiguration: Ed25519 keys are invalid or missing
INTERNAL_ERROR 500 Unexpected server error (details never leaked to client)

Startup-only errors: CONFIGURATION_ERROR and KEY_LOADING_ERROR are thrown synchronously by new SecureAuth(...) when the SDK is misconfigured (invalid TTLs, bad enum values, insecure cookie combinations, or non-Ed25519 keys). They are never returned as HTTP responses — the server fails fast at boot instead.


Auto-Managed Routes

When you call app.use('/auth', auth.router()), the SDK mounts these routes:

Route Method Auth Required Description
/auth/refresh POST Cookie Rotates refresh token, issues new access token
/auth/logout POST Bearer + Cookie Revokes current access + refresh token
/auth/logout/all POST Bearer Revokes all tokens for the user across all devices
/auth/sessions GET Bearer Lists all active sessions for the user

Project Structure

secureauth-sdk/
├── packages/
│   ├── core/          # Shared types, errors, and utilities
│   └── server/        # PASETO engine, Redis services, middleware
├── apps/
│   └── example-express/  # Working reference implementation
├── scripts/
│   └── keygen.ts      # Ed25519 key pair generator
└── SECURITY.md        # Security policy and design principles

License

MIT License — see LICENSE for details.

About

No description, website, or topics provided.

Resources

Readme
MIT license

Security policy

Security policy
Activity
Custom properties

Stars

3 stars

Watchers

0 watching

Forks

0 forks
Report repository

Releases

Packages

Contributors

Languages

You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.