AbsoluteJS

Absolute Auth

A comprehensive TypeScript-based authentication system built for Elysia applications. Complete OAuth 2.0 solution with optional OpenID Connect capabilities and end-to-end type safety.

View on GitHub

#Key Features

Multi-Provider Support

78 OAuth 2.0 providers with OpenID Connect support

Type-Safe

Full TypeScript support with comprehensive type definitions

Session Management

Built-in session handling with automatic expiration

Token Management

Automatic token refresh and revocation support

Route Protection

Easy-to-use route protection with typed callbacks

Event Hooks

Customizable handlers for all authentication flows

PKCE Support

Automatic PKCE implementation for supported providers

Security

Secure cookie handling and CSRF protection built-in

1
UserLogin click
The user clicks a login button in your app.
2
Your AppRedirect to the auth route
Your app redirects the browser to the AbsoluteAuth route.
/oauth2/auth/:provider
3
AbsoluteAuthGenerate state + PKCE
AbsoluteAuth generates the state and PKCE values for the request.
4
AbsoluteAuthRedirect to provider
The browser is redirected to the provider authorization page.
5
ProviderUser login
The user authenticates directly with the provider (external).
6
ProviderCallback + code
The provider redirects back to AbsoluteAuth with an authorization code.
7
AbsoluteAuthExchange code for token
AbsoluteAuth exchanges the code with the provider for an access token.
8
AbsoluteAuthSession created
A session is created and the user is redirected back to your app.
AbsoluteAuth handles steps 2-8 automatically.

#Installation

BASH
bun install @absolutejs/auth

#Basic Setup

TS
import { Elysia } from 'elysia';
import { absoluteAuth } from '@absolutejs/auth';
import { getEnv } from '@absolutejs/absolute';

const app = new Elysia()
  // No `<User>` type argument needed — `User` is INFERRED from `getUser`.
  .use(absoluteAuth({
    // `getUser` resolves your app user from a session subject. It is required,
    // and it is the single source of truth for `User`: the type is inferred
    // from what you return here, and every other user-typed callback
    // (createUser, getUserByEmail, onCallbackSuccess, …) is checked against
    // that same shape — so a mismatched user is a compile error, not a bug.
    getUser: (sub) => findUserBySub(sub),
    providersConfiguration: {
      google: {
        credentials: {
          clientId: getEnv('GOOGLE_CLIENT_ID'),
          clientSecret: getEnv('GOOGLE_CLIENT_SECRET'),
          redirectUri: 'http://localhost:3000/oauth2/callback'
        },
        scope: ['openid', 'profile', 'email']
      }
    }
  }))
  .listen(3000);

getUser is the one required hook, and it carries its weight: it resolves your app user from a session subject and is the single source of truth for your User type. The type is inferred from what getUser returns — so you rarely need a <User> argument — and every other user-typed callback (createUser, getUserByEmail, onCallbackSuccess) is checked against that same shape. A user that doesn't line up is a compile error, not a runtime surprise.

#Protect Routes

The protectRoute helper function protects routes that require authentication. It accepts two callbacks with fully typed parameters: the user object matches your exact user shape, and the error object is one of the specific authentication errors, giving you complete type safety for both success and failure paths.

TS
app.get('/protected', ({ status, protectRoute }) =>
  protectRoute(
    (user) => {
      return `Hello, ${user.name}!`;
    },
    (error) => status(error.code, error.message)
  )
);

#Handle Authentication Flow

When you use the Absolute Auth plugin, it automatically creates all the authentication routes you need. You do not implement your own login, status, or sign-out routes, you simply call the ones already provided.

Start the OAuth flow

TS
// Option 1: Use an anchor element
<a href="/oauth2/google/authorization">Sign in with Google</a>

// Option 2: Redirect the user to the provider's authorization URL
redirect('/oauth2/google/authorization');

This triggers the built-in authorization route, which handles:

1
Generating state + PKCE (if required)
2
Storing the provider name
3
Storing the origin URL
4
Building and redirecting to the provider's authorization URL

Check whether the user is logged in

TS
import { server } from '/src/frontend/utils/edenTreaty';

const { data, error } = await server.oauth2.status.get();

if (error) {
  console.error('Not authenticated:', error);
} else {
  console.log('User:', data.user);
}

Sign the user out

TS
import { server } from '/src/frontend/utils/edenTreaty';

const { data, error } = await server.oauth2.signout.delete();

if (error) {
  console.error('Signout failed:', error);
} else {
  console.log('Successfully signed out');
}

This calls the built-in sign-out route, which:

1
Runs your onSignOut hook (if provided)
2
Deletes the user session
3
Clears authentication cookies

#Authentication Routes

The library automatically creates the following routes:

RouteMethodDescription
/oauth2/:provider/authorizationGETInitiate OAuth flow with specified provider
/oauth2/callbackGETHandle OAuth callback and token exchange
/oauth2/statusGETCheck current user authentication status
/oauth2/profileGETFetch user profile from OAuth provider
/oauth2/tokensPOSTRefresh access token using refresh token
/oauth2/revocationPOSTRevoke access or refresh token
/oauth2/signoutDELETESign out user and clear session

#Session Management

Absolute Auth provides automatic session management with configurable lifetimes and cleanup. Sessions are automatically cleaned up at regular intervals, and you can also trigger cleanup manually using the derived cleanupSessions function.

Configuration

Configure session behavior using millisecond-based options:

TS
.use(await absoluteAuth<User>({
  providersConfiguration: { /* ... */ },

  // Session lifetime configuration
  sessionDurationMs: 86400000,            // 24 hours (default)
  unregisteredSessionDurationMs: 3600000, // 1 hour (default)
  cleanupIntervalMs: 300000,              // 5 minutes (default)
  maxSessions: 5,                         // Max sessions per user (default)

  // Called when sessions are cleaned up
  onSessionCleanup: async ({ removedSessions, removedUnregisteredSessions }) => {
    console.log(`Cleaned up ${removedSessions.size} expired sessions`);
  }
}))

The cleanup process:

cleanupIntervalMsCleanup runs automatically at the interval specified here
sessionDurationMsSessions older than this duration are removed
unregisteredSessionDurationMsUnregistered sessions older than this duration are removed
maxSessionsThe limit is enforced per user, removing oldest sessions first
onSessionCleanupThe hook is called with maps of removed sessions

The cleanupSessions Derived Function

When you use absoluteAuth, a cleanupSessions function is derived and made available in all your route handlers. This allows you to trigger cleanup programmatically:

TS
// The cleanupSessions function is derived from the auth middleware
// and available in all route handlers

app.post('/admin/cleanup', async ({ cleanupSessions }) => {
  // Manually trigger session cleanup
  await cleanupSessions();
  return { message: 'Sessions cleaned up' };
});

// You can also use it in scheduled tasks
app.get('/health', async ({ cleanupSessions }) => {
  // Cleanup runs automatically via cleanupIntervalMs,
  // but can be triggered manually if needed
  await cleanupSessions();
  return { status: 'healthy' };
})

The derived function follows Elysia's plugin pattern: it captures the session configuration from when absoluteAuth was initialized and provides a simple async function you can call from any route.

#Custom User Handling

Absolute Auth does not provide database adapters. Instead, it exposes hooks throughout the OAuth lifecycle, allowing you to integrate any persistence layer or user model. These hooks provide full control over user creation, updates, and session handling while keeping the OAuth flow standardized and database-agnostic.

Core Hook: onCallbackSuccess

Called after the provider returns and tokens are exchanged. Use it to load or create users via instantiateUserSession:

TS
onCallbackSuccess: async ({ authProvider, tokenResponse, session, userSessionId }) =>
  instantiateUserSession({
    authProvider,
    tokenResponse,
    session,
    userSessionId,
    getUser: async (userIdentity) => {
      // Find user in your database
      return await db.users.findByAuthSub(userIdentity.sub);
    },
    onNewUser: async (userIdentity) => {
      // Create new user in your database
      return await db.users.create({
        authSub: userIdentity.sub,
        email: userIdentity.email,
        name: userIdentity.name
      });
    }
  })

Route Configuration Props

Customize the route paths for all authentication endpoints:

PropDefaultDescription
authorizeRoute/oauth2/:provider/authorizationCustom authorization route path
callbackRoute/oauth2/callbackCustom callback route path
statusRoute/oauth2/statusCustom status check route path
signoutRoute/oauth2/signoutCustom sign-out route path
profileRoute/oauth2/profileCustom profile fetch route path
refreshRoute/oauth2/tokensCustom token refresh route path
revokeRoute/oauth2/revocationCustom token revocation route path

Lifecycle Hooks

Hook into each stage of the OAuth flow for custom behavior:

HookDescription
onAuthorizeSuccessCalled before redirecting to provider
onAuthorizeErrorCalled when authorization URL generation fails
onCallbackSuccessCalled after successful token exchange
onCallbackErrorCalled when callback/token exchange fails
onProfileSuccessCalled after successful profile fetch
onProfileErrorCalled when profile fetch fails
onStatusCalled when checking user session status
onRefreshSuccessCalled after successful token refresh
onRefreshErrorCalled when token refresh fails
onRevocationSuccessCalled after successful token revocation
onRevocationErrorCalled when token revocation fails
onSignOutCalled before session destruction
onSessionCleanupCalled when expired sessions are removed during cleanup

Continue toward an outcome

These playbooks show where this package fits, how to verify the combined system, and what changes before production.

Current package surface

What ships today

@absolutejs/authv0.65.0 · betaAuth & IdentitynpmSource
19entry points385symbols

Import surface · click to copy

80 symbols
authvaluePermalinkSource
TS
const auth: <UserType>(configuration: AuthConfig<UserType>) => Promise<Elysia<"", {
    decorator: {};
    store: {
        session: import("./types").SessionRecord<UserType>;
        unregisteredSession: import("./types").UnregisteredSessionRecord;
    };
    derive: ({
        readonly protectRoute: <AuthReturn, AuthFailReturn = never>(handleAuth: (user: UserType) => AuthReturn | Promise<AuthReturn>, handleAuthFail?: ((error: {
            readonly code: "Bad Request";
            readonly message: "Cookies are missing";
        } | {
            readonly code: "Unauthorized";
            readonly message: "User is not authenticated";
        }) => AuthFailReturn) | undefined) => Promise<import("elysia").ElysiaCustomStatusResponse<"Bad Request", "Cookies are missing", 400> | import("elysia").ElysiaCustomStatusResponse<"Unauthorized", "User is not authenticated", 401> | AuthReturn | NonNullable<AuthFailReturn>>;
    } & {
        readonly requireRecentAuth: <AuthReturn, AuthFailReturn_1>(maxAgeMs: number, handleAuth: (user: UserType) => AuthReturn | Promise<AuthReturn>, handleAuthFail?: ((error: {
            readonly code: "Unauthorized";
            readonly message: "Recent aut
Exported from @absolutejs/auth
Use this API in an outcome:Ship a SaaS platform

Outcomes

What you can build

Complete application identity

Add multi-provider login, sessions, route protection, recent-auth checks, organizations, API keys, MFA, passwordless flows, SSO, OIDC, SAML, and WebAuthn.

Users and agents together

Bind authenticated users, delegated AI agents, authorization details, permissions, audit evidence, and credential vaults to one reusable request context.

Hardening checklist

Production guidance

Harden every identity boundaryUse durable session and credential stores, secure cookies, exact callback origins, provider-managed verification where required, bounded sessions, audit hooks, and explicit optional protocol adapters.

Follow in order

Troubleshooting path

1
Login or session failures
Start with the callback URI, provider configuration, cookie security, session expiry, and selected server entry point. Use the focused OIDC, SAML, WebAuthn, vault, and provider guides for protocol-specific failures.