npm package: @loop8id/auth-node-js
Description: Core Node.js OIDC/OAuth2 client for https://auth.l8p8.com — shared base for Express, Next.js and Nuxt adapters
Latest version: 1.0.4

# @loop8id/auth-node-js

Core Node.js OIDC/OAuth 2.0 client for [https://auth.l8p8.com](https://auth.l8p8.com).

This is the **shared base** used by the framework adapters. Use it directly only if you need a custom integration. Otherwise use one of:

| Framework | Package |
|---|---|
| Express | `@loop8id/auth-express` |
| Next.js | `@loop8id/auth-nextjs` |
| Nuxt 3  | `@loop8id/auth-nuxtjs` |

---

## Features

- Authorization Code + **PKCE** (S256) — the most secure server-side SPA flow
- **OIDC discovery** — auto-fetches `/.well-known/openid-configuration`
- **Token exchange** — code → access/id/refresh tokens
- **Token refresh** — refresh token rotation
- **UserInfo** endpoint support
- **Token revocation**
- **Cookie-based sessions** — signed HMAC-SHA256, no server-side store required
- `expires_in` fallback — derives expiry from JWT `exp` when the server omits `expires_in`
- Zero runtime dependencies — uses only Node.js built-ins (`node:crypto`, `fetch`)

---

## Installation

```bash
npm install @loop8id/auth-node-js
```

---

## Core Concepts

### Session cookie
Sessions are stored as signed JSON in an HTTP-only cookie (`__loop8id_session`).
The signature uses HMAC-SHA256 with your `sessionSecret`. No Redis or DB required.

### Transaction cookie
PKCE state (code verifier, nonce, state) is stored in a short-lived cookie (`__loop8id_tx`, 5-minute TTL) that survives the browser round-trip to the auth server.

---

## API

### `createLoop8AuthClient(options)`

```ts
import { createLoop8AuthClient } from '@loop8id/auth-node-js';

const client = createLoop8AuthClient({
  clientId:      'YOUR_CLIENT_ID',             // required
  sessionSecret: 'a-secret-at-least-32-chars', // required
  redirectUri:   'http://localhost:3000/auth/callback', // required
  authority:     'https://auth.l8p8.com',      // default
  scope:         'openid profile email',       // default
  clientSecret:  'YOUR_CLIENT_SECRET',         // if required by your app
  audience:      'https://api.example.com',    // optional API audience
  sessionCookieName: '__loop8id_session',      // default
  sessionMaxAge:  86400,                       // default: 24 hours
});
```

### `client.buildLoginRedirect(returnTo?)`
Returns `{ redirectUrl, transactionCookie }`. Redirect the user and set the cookie.

### `client.handleCallback(callbackUrl, cookieHeader)`
Exchanges the authorization code. Returns `{ sessionCookie, clearTransactionCookie, returnTo, user, tokenSet }`.

### `client.buildLogoutRedirect(cookieHeader?)`
Revokes tokens and returns `{ redirectUrl, clearSessionCookie }`.

### `client.getSessionFromCookies(cookieHeader)`
Parses and verifies the session cookie. Returns `Loop8Session | null`.

### `client.getUserFromCookies(cookieHeader)`
Returns `UserClaims | null`.

### `client.isAuthenticatedFromCookies(cookieHeader)`
Returns `boolean`. Checks token expiry with leeway.

### `client.refreshSession(cookieHeader)`
Uses the stored refresh token. Returns a fresh `sessionCookie` or `null`.

---

## Environment Variables

```env
L8P8_CLIENT_ID=your-client-id
L8P8_CLIENT_SECRET=your-client-secret   # if required
SESSION_SECRET=min-32-char-random-string
```

---

## License

MIT © Loop8ID