Skip to content

Development

Prerequisites

  • Node.js >= 18 (20+ recommended)
  • PostgreSQL database
  • Git

Local Setup

# Clone the repository
git clone https://github.com/credinvest/cred-auth.git
cd cred-auth

# Install dependencies
npm install

# Copy environment template
cp .env.example .env

# Edit .env with your local configuration (see below)

# Start in development mode (hot reload)
npm run start:dev

The server starts at http://localhost:3000. Verify with:

curl http://localhost:3000/.well-known/openid-configuration

Minimal .env for Local Development

PORT=3000
OAUTH_ISSUER=http://localhost:3000
DB_CONNECTION=postgresql://user:password@localhost:5432/oauth

Tip

In development, OAUTH_COOKIE_KEYS and OAUTH_JWKS_PRIVATE_KEY are optional — the server auto-generates ephemeral keys. For production, both are required.


Database Setup

CRED Auth requires two PostgreSQL tables.

Table: "User"

The shared users table used across the CRED platform. Required columns:

Column Type Description
id INTEGER / SERIAL Primary key
email VARCHAR(255) Unique, user's email
password VARCHAR(255) Bcrypt hashed password
isEmailVerified BOOLEAN Email verification status
isActive BOOLEAN Account active flag
role VARCHAR(50) User role
createdAt TIMESTAMP Creation timestamp
updatedAt TIMESTAMP Last update timestamp

Table: "OAuthPayload"

Stores all oidc-provider data (sessions, tokens, clients, grants, interactions):

CREATE TABLE "OAuthPayload" (
    id SERIAL PRIMARY KEY,
    "payloadId" VARCHAR(255) NOT NULL,
    type VARCHAR(50) NOT NULL,
    payload JSONB NOT NULL,
    "grantId" VARCHAR(255),
    "userCode" VARCHAR(255),
    uid VARCHAR(255),
    "userId" INTEGER REFERENCES "User"(id) ON DELETE CASCADE,
    "expiresAt" TIMESTAMP,
    "consumedAt" TIMESTAMP,
    "createdAt" TIMESTAMP DEFAULT NOW(),
    "updatedAt" TIMESTAMP DEFAULT NOW(),
    UNIQUE("payloadId", type)
);

CREATE INDEX idx_oauth_payload_lookup ON "OAuthPayload"("payloadId", type);
CREATE INDEX idx_oauth_payload_grant_id ON "OAuthPayload"("grantId") WHERE "grantId" IS NOT NULL;
CREATE INDEX idx_oauth_payload_user_code ON "OAuthPayload"("userCode") WHERE "userCode" IS NOT NULL;
CREATE INDEX idx_oauth_payload_uid ON "OAuthPayload"(uid) WHERE uid IS NOT NULL;
CREATE INDEX idx_oauth_payload_user_id ON "OAuthPayload"("userId") WHERE "userId" IS NOT NULL;
CREATE INDEX idx_oauth_payload_expires_at ON "OAuthPayload"("expiresAt") WHERE "expiresAt" IS NOT NULL;

Schema notes:

  • payloadId + type is the composite unique key used by oidc-provider
  • type values include: Session, AccessToken, AuthorizationCode, RefreshToken, Client, Grant, Interaction, InitialAccessToken, RegistrationAccessToken, and others
  • userId is auto-populated from the payload's accountId and enables cascading deletes

Registering a Test Client

Clients are stored in "OAuthPayload" with type='Client'.

Manual SQL Insert

INSERT INTO "OAuthPayload" ("payloadId", type, payload)
VALUES (
    'test-client',
    'Client',
    '{
        "client_id": "test-client",
        "client_secret": "test-secret",
        "redirect_uris": ["http://localhost:8080/callback"],
        "grant_types": ["authorization_code", "refresh_token"],
        "response_types": ["code"],
        "token_endpoint_auth_method": "client_secret_basic",
        "client_name": "Test Application"
    }'::jsonb
);

Dynamic Registration (if enabled)

curl -X POST http://localhost:3000/oauth/reg \
  -H "Content-Type: application/json" \
  -d '{
    "redirect_uris": ["http://localhost:8080/callback"],
    "client_name": "My Dev App"
  }'

NPM Scripts

Script Description
npm run start:dev Development with hot reload
npm run build Compile TypeScript to dist/
npm run start:prod Run compiled production build
npm test Run unit tests
npm run test:e2e Run end-to-end tests
npm run lint Lint the codebase

Testing

Unit Tests

Unit tests are co-located with source files (*.spec.ts):

npm test

End-to-End Tests

The E2E test suite (test/oauth.e2e-spec.ts) exercises the full OAuth flow with a mocked stack:

npm run test:e2e

DB SSL Configuration

The DB_SSL environment variable controls PostgreSQL SSL behavior:

Value Behavior
true Full SSL verification (requires valid certificate)
no-verify SSL enabled but certificate verification disabled (rejectUnauthorized: false)
false / unset No SSL

Note

Use no-verify for Cloud SQL connections through the Cloud SQL Proxy where the proxy handles TLS.


TypeScript Path Aliases

The project uses @/* as a path alias mapping to src/*:

import { UsersService } from '@/users/users.service';

Configured in tsconfig.json.


Views (Handlebars Templates)

The login/consent UI uses Handlebars templates stored in views/:

Template Purpose
views/layouts/main.hbs Base HTML layout
views/login.hbs Login form and consent screen
views/error.hbs Error display page

Templates are served by NestJS using @Render('login') on the interaction controller. Custom Handlebars helpers are registered in main.ts (e.g., eq for equality comparison).