Skip to content

Repository files navigation

identity-service

A standalone identity service (a self-hosted IdP) shared across products — one deployment is one realm with a single shared user pool (ADR-0018). It ships the service (OAuth 2.0 + OIDC token issuance), a headless SDK, an optional drop-in React <Login/>, an authenticated management plane (HTTP /admin/v1 + an MCP server for agents), and an optional operator admin console. It owns authentication (who you are); consuming products keep their own authorization (what you may do).

Project Layout

identity-service/
 ├── docker/           # Docker Compose base + overrides; backup.sh (nightly encrypted backups)
 ├── service/          # REST API + Docker assets
 │    ├── src/         # Express app, OAuth + session cores, admin plane, MCP server, models
 │    ├── Dockerfile   # Container build
 ├── sdk/              # Headless TypeScript client for the API
 │    └── src/
 ├── react/            # Optional React UI: drop-in <Login/> (@fps4/identity-service-react)
 │    └── src/
 ├── console/          # Optional operator admin console (Next.js, @fps4/identity-service-console)
 │    └── app/
 ├── config/           # seed.example.yaml → seed.yaml (gitignored): applications (+role catalogues + credentials) + users + assignments
 ├── docs/             # Two-plane docs: design/ · reference/ · guides/ · product/ (index: docs/README.md)
 └── README.md

Quick Start

  1. Copy service/.env.example to .env and set values:

    • MONGO_URI, MONGO_DB_NAME
    • AUTH_JWT_SECRET, AUTH_JWT_ISSUER, AUTH_JWT_AUDIENCE
    • OAuth settings: token TTLs, deployment limits, optional key passphrase (see comments in .env.example)
    • Optionally update SESSION_TTL_MINUTES, CORS_ORIGINS
  2. Install dependencies & build:

    cd service
    npm install
    npm run build
    npm test
    npm start
  3. (Optional) Run with Docker:

    docker compose -f docker/compose.yaml -f docker/compose.dev.yaml up --build

    Use docker compose -f docker/compose.yaml -f docker/compose.dev.yaml down to stop containers.

The service listens on PORT (default 7305). Health check at GET /health.

API Summary

  • POST /oauth2/token – client credentials grant issuing RS256 access tokens.
  • GET /.well-known/jwks.json – JWKS for verifying issued tokens.
  • POST /v1/sessions – persist session, issue legacy session JWT (in migration).
  • PATCH /v1/sessions/:sessionId – attach contact identifiers or cookie context.
  • /admin/v1/* – the authenticated management plane (ADR-0007): applications (their role catalogues, members, and credentials — ADR-0020), credentials (OAuth clients under an application), users, assignments (user↔app entitlements — ADR-0019), invites, signing keys, stats, and audit. Network-restricted, scoped per actor, append-only audited. The same operations are exposed to agents over an MCP server (npm run mcp).
  • See docs/reference/api.md for full payloads and responses.

SDK Usage

import { ComponentAuthClient } from '@fps4/identity-service-sdk';

const client = new ComponentAuthClient({
  baseUrl: 'https://auth.example.com'
});

const session = await client.createSession({ visitorId: 'visitor-001' });
await client.updateSession({ sessionId: session.sessionId, contactId: 'contact-42' });

const token = await client.requestClientCredentialsToken({
  clientId: process.env.CORE_AUTH_CLIENT_ID!,
  clientSecret: process.env.CORE_AUTH_CLIENT_SECRET!,
  scope: ['telemetry:write']
});

console.log(token.accessToken);

Browser login (RQ-0001 Google SSO / RQ-0002 local credentials)

A browser frontend drives the redirect login with PKCE and forwards the issued token as Authorization: Bearer to its own API. The SDK calls are named for Google because that was the first IdP, but the flow is provider-agnostic: /oauth2/authorize redirects to Google when the deployment configures a Google app, and otherwise serves this service's own login form. The consumer's half — beginGoogleLogin → navigate → completeGoogleLogin — is identical either way.

// 1. Begin: stash the verifier/state, then navigate to the authorization endpoint.
const { authorizationUrl, codeVerifier, state } = await client.beginGoogleLogin({
  clientId: 'client-maestro',
  redirectUri: 'https://app.example.com/auth/callback'
});
sessionStorage.setItem('pkce', JSON.stringify({ codeVerifier, state }));
window.location.assign(authorizationUrl);

// 2. On the redirect back (…/auth/callback?code=…&state=…): exchange the code.
const { codeVerifier, state } = JSON.parse(sessionStorage.getItem('pkce')!);
if (params.get('state') !== state) throw new Error('state mismatch');
const token = await client.completeGoogleLogin({
  code: params.get('code')!,
  codeVerifier,
  redirectUri: 'https://app.example.com/auth/callback',
  clientId: 'client-maestro'
});
// token.accessToken → send as `Authorization: Bearer`; token.refreshToken → client.refreshUserToken(...)

Run npm install && npm run build inside sdk/ to compile distributable assets. Consumers need a fetch implementation (Node 18+ or polyfill); the login helpers also require WebCrypto (browser or Node 18+).

React login component

For React consumers, @fps4/identity-service-react (in react/) ships a drop-in <Login/> for the local email/password IdP — so apps don't rebuild the form. It's a separate, opt-in package (React peer dependency only); the headless SDK stays UI-free.

import { Login } from '@fps4/identity-service-react';

<Login
  baseUrl="https://auth-dev.example.com"
  clientId="client-local"
  onSuccess={(token) => sessionStorage.setItem('access_token', token.accessToken)}
/>

See react/README.md for styling (Tailwind/shadcn) and the full API.

Operator admin console

For day-2 operations, @fps4/identity-service-console (in console/) is an optional Next.js app — a thin server-side client over the /admin/v1 management plane (ADR-0007): dashboards plus application (with role-catalogue, members, and credentials — ADR-0020), user, assignment, and signing-key management. The admin bearer token stays in server env and never reaches the browser; no direct database access. Distinct from the consumer-facing <Login/> widget. See console/README.md.

Docs

Docs follow a two-plane structure — see docs/README.md for the full index, or docs/overview.md for the landing.

Deployments

The service is a stateless container with MongoDB as its only persistent dependency, deployed manually over SSH to a Docker host (DOCKER_HOST=ssh://<host>). Secrets live in a gitignored docker/.env (GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET, OAUTH_KEY_PASSPHRASE, the HTTPS AUTH_JWT_ISSUER, etc.) — never committed. A public HTTPS endpoint (reverse proxy or tunnel) fronts the service; it listens on PORT (default 7305). The image build runs npm run build && npm test, so a red test fails the deploy.

See docs/guides/deployment.md for the full procedure, prerequisites, and how a consumer's verifier env lines up with what the service mints.

About

Authentication Module

Resources

Contributing

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages