Skip to main content
Alpha

Add auth and a session

You have a running workspace, but anyone can hit it. This chapter gives it an identity layer: you add the official auth plugin, choose an authentication backend, run its database migration, and verify a live session through the auth-api service on :8094. The lesson underneath the steps: auth in NetScript is a pluggable backend plus a session — you pick the backend with one environment variable, and the contract is identical no matter which one you pick.

  1. 1 · Scaffold
  2. 2 · Auth
  3. 3 · Workspace data
  4. 4 · Provision job
  5. 5 · Route authz
  6. 6 · Deploy

What you will build

A working sign-in surface for my-workspace/: the auth plugin installed, the interactive kv-oauth backend selected, the auth.prisma migration applied, and the auth-api service answering on :8094. By the end you can hit GET /api/v1/auth/session and GET /api/v1/auth/me and watch the service correctly report "no session yet" before login — proof the backend is composed and the session endpoints are wired.

Before you begin

You need the workspace from chapter 1 with aspire startning — the auth plugin contributes a service and a Prisma schema that Aspire and netscript db reach through the running graph. Confirm the base is up:

# In my-workspace/, with `aspire start` up in another terminal
curl http://localhost:3001/health   # the workspace service from chapter 1

Step 1 — Add the auth plugin

The auth plugin is a first-class official plugin, installed the same way as workers, sagas, and triggers. From the workspace root:

netscript plugin add @netscript/plugin-auth

This scaffolds the unified @netscript/plugin-auth plugin into plugins/, registers it, and contributes three things to your workspace: a Prisma schema (auth.prisma), a service entry that becomes the auth-api service, and the /api/v1/auth/* routes. Confirm it landed:

netscript plugin list

Step 2 — Choose a backend with NETSCRIPT_AUTH_BACKEND

The active backend is selected by the NETSCRIPT_AUTH_BACKEND environment variable (or the auth.backend appsettings key). Three backends are valid; the default is kv-oauth, the only one that drives an interactive sign-in.

Auth backends — capability matrix (NETSCRIPT_AUTH_BACKEND)
NameTypeDescription
kv-oauth interactive (default) Full OAuth/OIDC redirect flow. Real signin + callback, KV-backed sessions with refresh-on-read, signout. The only backend that implements InteractiveFlowPort. Package @netscript/auth-kv-oauth.
workos non-interactive WorkOS AuthKit sealed wos-session cookie. Validates an existing session; signin/callback return AUTH_PROVIDER_ERROR. Package @netscript/auth-workos.
better-auth non-interactive better-auth over Prisma. Validates an existing session; signin/callback return AUTH_PROVIDER_ERROR. Package @netscript/auth-better-auth.

A team workspace needs users to log in, so this track uses the interactive default. Make the choice explicit in your environment:

export NETSCRIPT_AUTH_BACKEND=kv-oauth

Step 3 — Run the auth database migration

The auth plugin contributes a Prisma schema, plugins/auth/database/auth.prisma, that aggregates into your primary Postgres at db generate. With aspire startning, run the standard database loop from the workspace root:

netscript db init --name init    # first time only — create the migration
netscript db generate            # generate the Prisma client + Zod schemas
netscript db seed                # optional seed data
netscript db status              # confirm the migration is applied

Step 4 — Configure the kv-oauth backend

The kv-oauth backend needs a real OAuth/OIDC provider (a client id, secret, and redirect URI) plus a key for the session token at rest. Set these in your environment before starting the service:

# Selects the interactive backend
export NETSCRIPT_AUTH_BACKEND=kv-oauth

# Provider credentials (e.g. a Google or GitHub OAuth app)
export NETSCRIPT_AUTH_CLIENT_ID=your-client-id
export NETSCRIPT_AUTH_CLIENT_SECRET=your-client-secret
export NETSCRIPT_AUTH_REDIRECT_URI=http://localhost:8094/api/v1/auth/callback

# Required for kv-oauth: missing key material is a startup error
export NETSCRIPT_AUTH_KV_OAUTH_KEY=<base64url-encoded-32-byte-secret>

export PORT=8094

Step 5 — Compose the backend in code

When you wire the backend yourself — for a custom service entry or a test — the interactive kv-oauth backend is one await call. You pass a provider preset from providers.*, and you get back something that satisfies the AuthBackendPort seam that @netscript/plugin-auth-core defines (and, because it is kv-oauth, the optional InteractiveFlowPort too):

// services/auth/src/backend.ts
import { createKvOAuthBackend, getRequiredEnv, providers } from '@netscript/auth-kv-oauth';

// providers.google(...) is a preset that fills the OIDC endpoints for you.
export const backend = await createKvOAuthBackend({
  provider: providers.google({
    clientId: getRequiredEnv('NETSCRIPT_AUTH_CLIENT_ID'),
    clientSecret: getRequiredEnv('NETSCRIPT_AUTH_CLIENT_SECRET'),
    redirectUri: getRequiredEnv('NETSCRIPT_AUTH_REDIRECT_URI'),
  }),
});

// backend implements AuthBackendPort AND the optional InteractiveFlowPort,
// so the auth-api signin + callback endpoints are live on this backend.
console.log(backend.name); // 'kv-oauth'
// Same factory, but define the OIDC provider yourself when your IdP is
// not one of the built-in presets.
import { createKvOAuthBackend, defineOAuthProvider, getRequiredEnv } from '@netscript/auth-kv-oauth';

const provider = defineOAuthProvider({
  id: 'my-idp',
  clientId: getRequiredEnv('NETSCRIPT_AUTH_CLIENT_ID'),
  clientSecret: getRequiredEnv('NETSCRIPT_AUTH_CLIENT_SECRET'),
  authorizationEndpoint: getRequiredEnv('NETSCRIPT_AUTH_AUTHORIZATION_ENDPOINT'),
  tokenEndpoint: getRequiredEnv('NETSCRIPT_AUTH_TOKEN_ENDPOINT'),
  userInfoEndpoint: getRequiredEnv('NETSCRIPT_AUTH_USERINFO_ENDPOINT'),
  redirectUri: getRequiredEnv('NETSCRIPT_AUTH_REDIRECT_URI'),
  scopes: ['openid', 'profile', 'email'],
});

export const backend = await createKvOAuthBackend({ provider });

The core seam is small and contract-only. @netscript/plugin-auth-core defines the AuthBackendPort and the registry helpers that resolve one backend from many — these are the types the auth plugin composes against, confirmed on the package's public surface:

@netscript/plugin-auth-core — the seam the backend satisfies
NameTypeDescription
AuthBackendPort type The contract every backend implements — the auth-api surface is identical across all three backends because they all satisfy this port.
createAuthBackendRegistry / resolveBackend function Build a registry of named backends and resolve the single active one (DEFAULT_AUTH_BACKEND_NAME is the fallback).
AuthSession type The normalized session the store persists — id, subject, state, scopes, claims, issuedAt / expiresAt.
createHmacSessionTokenCrypto function HMAC-signs the opaque session token so the cookie value cannot be forged.
authContractV1 contract The five-route auth contract: signin, signout, callback, session, me.

Step 6 — Verify a session

With aspire startning, the auth-api service binds port 8094 and mounts the five auth endpoints under /api/v1/auth/*. On a fresh, unauthenticated request, session reports no active session — which proves the endpoint is wired even before you complete a login:

# Service is alive
curl http://localhost:8094/health/ready

# No session yet — confirms the endpoint is mounted and reachable
curl http://localhost:8094/api/v1/auth/session

# Identity endpoint: { "authenticated": false } (HTTP 200) before login
curl http://localhost:8094/api/v1/auth/me

To exercise the full interactive flow on kv-oauth, drive the redirect from a browser: open POST /api/v1/auth/signin (the service issues the provider redirect), authenticate with your provider, let it call back to /api/v1/auth/callback, then re-check the session with the cookie the flow set:

# After completing the browser sign-in, the __Host-ns_session cookie is set.
curl -b cookies.txt http://localhost:8094/api/v1/auth/session
curl -b cookies.txt http://localhost:8094/api/v1/auth/me

A successful GET /api/v1/auth/me after sign-in returns { authenticated: true, user, session }. That round trip is the proof the backend is composed, the migration is applied, and the provider credentials are correct.

  • [ ] netscript plugin list shows the auth plugin.
  • [ ] netscript db status reports the auth.prisma migration applied.
  • [ ] NETSCRIPT_AUTH_BACKEND=kv-oauth and the provider env vars are set.
  • [ ] curl http://localhost:8094/health/ready succeeds.
  • [ ] curl http://localhost:8094/api/v1/auth/me returns { "authenticated": false } before login.

What you built

Your workspace now has an identity layer: the auth plugin composed onto the interactive kv-oauth backend, the auth.prisma migration applied, and the auth-api service answering on :8094 with the five-route contract. You proved the session endpoints are wired. Next you give the workspace its own data to protect — a separate database for workspace records.