npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@nightmar3/uauth-core

v1.0.6

Published

Universal authentication SDK that works with any backend

Readme

@nightmar3/uauth-core

Core authentication SDK that works with any backend implementing the Universal Auth contract.

[!IMPORTANT] Backend Required: This SDK requires a backend API. See Backend Requirements for the complete API contract, or use our Backend Implementation Guide for step-by-step instructions.

Setup Guide

Step 1: Install the Package

npm install @nightmar3/uauth-core

Step 2: Create Auth Instance

import { createAuth } from '@nightmar3/uauth-core'

const auth = createAuth({
  baseURL: 'https://api.yourapp.com/auth',
  storage: localStorage,  // or sessionStorage, AsyncStorage, custom
})

Step 3: Sign In

const result = await auth.signIn('password', {
  email: '[email protected]',
  password: 'secret123',
})

if (result.ok) {
  console.log('User:', result.data.user)
  console.log('Tokens stored automatically')
}

Step 4: Get Session

const session = await auth.session()

if (session.ok) {
  console.log('Current user:', session.data.user)
}

Step 5: Sign Out

await auth.signOut()

That's it! You have full authentication support.


Use Cases

Email/Password Authentication

// Sign in
const result = await auth.signIn('password', {
  email: '[email protected]',
  password: 'secret',
})

// Sign up
const result = await auth.signUp({
  email: '[email protected]',
  password: 'secure123',
  name: 'New User',
})

OAuth2 Authentication (Optional)

OAuth is completely optional. If you don't need OAuth, skip this section - no extra network requests will be made.

To add OAuth support:

1. Install the OAuth2 plugin:

import { createAuth, createOAuth2Plugin } from '@nightmar3/uauth-core'

const auth = createAuth({
  baseURL: 'https://api.yourapp.com/auth',
  storage: localStorage,
})

// Install OAuth2 plugin
const oauth2Plugin = createOAuth2Plugin()
await auth.plugin('oauth2', oauth2Plugin)

2. Get available providers:

// Load providers from backend
const providers = await oauth2Plugin.loadProviders()

console.log(providers)
// [{ name: 'google', displayName: 'Google' }, { name: 'github', displayName: 'GitHub' }]

3. Sign in with OAuth:

// Popup flow (recommended for SPAs)
const result = await oauth2Plugin.signInWithPopup({ provider: 'google' })

if (result.ok) {
  console.log('User:', result.data.user)
}

// Redirect flow (for SSR or when popups are blocked)
oauth2Plugin.signInWithRedirect({ provider: 'google' })

4. Handle OAuth callback (for redirect flow):

// On your callback page
const result = await oauth2Plugin.handleCallback()

if (result.ok) {
  console.log('User:', result.data.user)
  window.location.href = '/dashboard'
}

React/Next.js Usage

When using with @nightmar3/uauth-react or @nightmar3/uauth-next, pass plugins to the AuthProvider:

import { createAuth, createOAuth2Plugin } from '@nightmar3/uauth-core'
import { AuthProvider, useOAuth } from '@nightmar3/uauth-react'

const auth = createAuth({ baseURL: '...' })
const plugins = [createOAuth2Plugin()]

function App() {
  return (
    <AuthProvider auth={auth} plugins={plugins}>
      <YourApp />
    </AuthProvider>
  )
}

function LoginPage() {
  const { providers, signInWithOAuth } = useOAuth()

  return (
    <div>
      {providers.map(p => (
        <button key={p.name} onClick={() => signInWithOAuth(p.name)}>
          Continue with {p.displayName}
        </button>
      ))}
    </div>
  )
}

Token Management

// Get access token
const token = await auth.getToken()

// Check authentication status
const isAuth = await auth.isAuthenticated()

// Refresh tokens manually
await auth.refresh()

Custom Storage Adapters

// React Native with AsyncStorage
import AsyncStorage from '@react-native-async-storage/async-storage'

const auth = createAuth({
  baseURL: '...',
  storage: AsyncStorage,  // Works directly!
})

// Custom storage
const customStorage = {
  getItem: (key) => myDB.get(key),
  setItem: (key, value) => myDB.set(key, value),
  removeItem: (key) => myDB.delete(key),
}

const auth = createAuth({
  baseURL: '...',
  storage: customStorage,
})

Backend Requirements

The SDK expects a backend API that implements the following endpoints. You can use our FastAPI reference implementation or build your own.

Endpoints

| Method | Path | Description | Request Body | Response Data | |--------|------|-------------|--------------|---------------| | POST | /sign-in/password | Sign in with email/password | { email, password } | { user, tokens } | | POST | /sign-in/oauth2 | Exchange OAuth code for tokens | { provider, code, redirect_uri } | { user, tokens } | | POST | /sign-up | Create new account | { email, password, name? } | { user, tokens } | | DELETE| /session | Sign out (revoke tokens) | - | { ok: boolean } | | GET | /session | Get current user session | - | { user } | | POST | /token/refresh | Refresh access token | { refresh_token } | { tokens } | | GET | /providers | List available OAuth providers | - | { providers } |

Response Format

All API responses must follow this envelope structure:

interface ApiResponse<T> {
  ok: boolean
  data: T | null
  error: {
    code: string
    message: string
    details?: any
  } | null
}

Token Structure

The backend must return tokens in this format:

interface AuthTokens {
  access_token: string
  refresh_token: string
  expires_in: number // seconds
}

OAuth Provider Response (Optional)

If implementing OAuth support, the /providers endpoint must return:

interface OAuth2Provider {
  name: string
  displayName: string
  clientId: string
  authorizationUrl: string
  scope?: string
}

Example:

{
  "ok": true,
  "data": {
    "providers": [
      {
        "name": "google",
        "displayName": "Google",
        "clientId": "your-google-client-id",
        "authorizationUrl": "https://accounts.google.com/o/oauth2/v2/auth",
        "scope": "openid email profile"
      }
    ]
  },
  "error": null
}

API Reference

createAuth(config)

Create an auth instance.

interface AuthConfig {
  baseURL: string                        // Required: Your auth backend URL
  storage?: StorageAdapter               // Optional: Storage implementation
  fetch?: typeof fetch                   // Optional: Custom fetch function
  storageKeyPrefix?: string              // Optional: Prefix for storage keys
  onTokenRefresh?: (tokens) => void      // Optional: Called when tokens refresh
  onAuthError?: (error) => void          // Optional: Called on auth errors
}

const auth = createAuth({
  baseURL: 'https://api.yourapp.com/auth',
  storage: localStorage,
  storageKeyPrefix: 'myapp_',
  onTokenRefresh: (tokens) => {
    console.log('Tokens refreshed')
  },
  onAuthError: (error) => {
    console.error('Auth error:', error.message)
  },
})

auth.signIn(method, payload)

Sign in with various methods.

// Password authentication
await auth.signIn('password', {
  email: '[email protected]',
  password: 'secret',
})

// OAuth2 (with code from callback)
await auth.signIn('oauth2', {
  provider: 'google',
  code: 'auth_code',
})

Returns: Promise<ApiResponse<SignInData>>

auth.signUp(payload)

Create a new account.

const result = await auth.signUp({
  email: '[email protected]',
  password: 'secure123',
  name: 'New User',
})

Returns: Promise<ApiResponse<SignInData>>

auth.signOut()

Sign out and clear tokens.

await auth.signOut()

Returns: Promise<ApiResponse<{ ok: boolean }>>

auth.session()

Get the current session.

const result = await auth.session()

if (result.ok) {
  console.log('User:', result.data.user)
}

Returns: Promise<ApiResponse<SessionData>>

auth.refresh()

Manually refresh tokens.

const result = await auth.refresh()

if (result.ok) {
  console.log('New tokens:', result.data.tokens)
}

Note: Tokens are automatically refreshed on 401 responses.

auth.isAuthenticated()

Check if user is authenticated.

const isAuth = await auth.isAuthenticated()

Returns: Promise<boolean>

auth.getToken()

Get the current access token.

const token = await auth.getToken()

if (token) {
  fetch('/api/data', {
    headers: { Authorization: `Bearer ${token}` },
  })
}

Returns: Promise<string | null>

auth.plugin(name, plugin)

Install a plugin.

await auth.plugin('oauth2', createOAuth2Plugin())

Low-Level Utilities

refreshTokenRequest(baseURL, refreshToken, fetchFn?)

Low-level utility to make a token refresh request. This is used internally by auth.refresh() but is exported for advanced use cases (e.g., server-side refresh).

import { refreshTokenRequest } from '@nightmar3/uauth-core'

const result = await refreshTokenRequest(
  'https://api.yourapp.com/auth',
  'refresh_token_value'
)

if (result.ok) {
  console.log('New tokens:', result.data.tokens)
}

Parameters:

  • baseURL - The base URL of the auth API
  • refreshToken - The refresh token to use
  • fetchFn - Optional custom fetch function (defaults to global fetch)

Returns: Promise<ApiResponse<{ tokens: AuthTokens }>>


OAuth2 Plugin API

createOAuth2Plugin()

Create the OAuth2 plugin.

import { createOAuth2Plugin } from '@nightmar3/uauth-core'

const oauth2Plugin = createOAuth2Plugin()
await auth.plugin('oauth2', oauth2Plugin)

oauth2Plugin.loadProviders()

Load available OAuth providers from backend.

const providers = await oauth2Plugin.loadProviders()
// [{ name: 'google', displayName: 'Google' }, ...]

Returns: Promise<OAuth2Provider[]>

oauth2Plugin.signInWithPopup(options)

Sign in using popup window.

const result = await oauth2Plugin.signInWithPopup({
  provider: 'google',
  redirectUri: '/auth/callback',  // Optional
})

if (result.ok) {
  console.log('User:', result.data.user)
}

Returns: Promise<ApiResponse<SignInData>>

oauth2Plugin.signInWithRedirect(options)

Sign in using redirect.

oauth2Plugin.signInWithRedirect({
  provider: 'github',
  redirectUri: '/auth/callback',  // Optional
})
// Page redirects to OAuth provider

oauth2Plugin.handleCallback()

Handle OAuth callback (for redirect flow).

// On your callback page
const result = await oauth2Plugin.handleCallback()

if (result.ok) {
  window.location.href = '/dashboard'
}

Returns: Promise<ApiResponse<SignInData>>


Response Format

All methods return a standard response envelope:

interface ApiResponse<T> {
  ok: boolean
  data: T | null
  error: ApiError | null
}

interface ApiError {
  code: string
  message: string
  details?: Record<string, any>
}

Success:

{
  ok: true,
  data: {
    user: { id: '...', email: '...', name: '...' },
    tokens: { access_token: '...', refresh_token: '...', expires_in: 3600 }
  },
  error: null
}

Error:

{
  ok: false,
  data: null,
  error: {
    code: 'INVALID_CREDENTIALS',
    message: 'Email or password is incorrect'
  }
}

Error Handling

const result = await auth.signIn('password', { email, password })

if (!result.ok) {
  switch (result.error?.code) {
    case 'INVALID_CREDENTIALS':
      alert('Wrong email or password')
      break
    case 'NETWORK_ERROR':
      alert('Connection failed')
      break
    default:
      alert(result.error?.message)
  }
}

Automatic Token Refresh

The SDK automatically refreshes tokens when:

  1. Access token is expired (checked before requests)
  2. A request returns 401 Unauthorized

How it works:

  1. Request fails with 401
  2. SDK calls /token/refresh with refresh token
  3. New tokens stored automatically
  4. Original request retried with new token
  5. If refresh fails, user is logged out

You don't need to handle this manually.


Storage Adapters

Built-in Adapters

import {
  LocalStorageAdapter,
  SessionStorageAdapter,
  MemoryStorageAdapter,
} from '@nightmar3/uauth-core'

// localStorage (default)
const auth = createAuth({
  baseURL: '...',
  storage: new LocalStorageAdapter(),
})

// sessionStorage (cleared on tab close)
const auth = createAuth({
  baseURL: '...',
  storage: new SessionStorageAdapter(),
})

// In-memory (for testing or SSR)
const auth = createAuth({
  baseURL: '...',
  storage: new MemoryStorageAdapter(),
})

Custom Storage Adapter

interface StorageAdapter {
  getItem(key: string): Promise<string | null> | string | null
  setItem(key: string, value: string): Promise<void> | void
  removeItem(key: string): Promise<void> | void
}

// Example: AsyncStorage for React Native
import AsyncStorage from '@react-native-async-storage/async-storage'

const auth = createAuth({
  baseURL: '...',
  storage: AsyncStorage,  // Works directly!
})

Plugin System

Extend the SDK with custom plugins.

const customPlugin = {
  name: 'my-plugin',
  version: '1.0.0',
  install({ client, core, sdk }) {
    // Add methods to SDK
    sdk.customMethod = async () => {
      return client.req('/custom-endpoint')
    }
  },
}

await auth.plugin('my-plugin', customPlugin)

// Now available
await auth.customMethod()

TypeScript Support

Full TypeScript support with generic user types:

interface MyUser {
  id: string
  email: string
  role: 'admin' | 'user'
  customField: string
}

const auth = createAuth<MyUser>({
  baseURL: '...',
})

const result = await auth.session()

if (result.ok) {
  // result.data.user is typed as MyUser
  console.log(result.data.user.role)
}

React Native

Works out of the box with React Native:

import AsyncStorage from '@react-native-async-storage/async-storage'
import { createAuth } from '@nightmar3/uauth-core'

const auth = createAuth({
  baseURL: 'https://api.yourapp.com/auth',
  storage: AsyncStorage,
})

License

MIT