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+typeis the composite unique key used by oidc-providertypevalues include:Session,AccessToken,AuthorizationCode,RefreshToken,Client,Grant,Interaction,InitialAccessToken,RegistrationAccessToken, and othersuserIdis auto-populated from the payload'saccountIdand 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).