Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions apps/custom-issuer/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Custom Issuer Service Environment Variables
# Copy this file to .env and update with your actual values

# =============================================================================
# REQUIRED - Application will not start without these
# =============================================================================

# Base64-encoded RSA private key used for signing issued tokens
# To encode a key file: cat signing-key.pem | base64
# Example: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcFFJQkFBS0NBUUVB...
KEY_BASE64=

# Firebase URL where the validation public key can be retrieved
# Firebase certificate format: https://www.googleapis.com/robot/v1/metadata/x509/{service-account-email}
# Example: https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk@project.iam.gserviceaccount.com
VALIDATION_PUBLIC_KEY_URL=

# Expected issuer URL that incoming JWTs must have in their 'iss' claim
# This should match the issuer URL of the tokens you want to accept
# Example: https://securetoken.google.com/{project-id}
VALIDATION_ISSUER_URL=

# URL of the issuer service (will be included as 'iss' claim in issued tokens)
# Example: http://localhost:3000
ISSUER_URL=

# =============================================================================
# OPTIONAL - Application will use defaults if not set
# =============================================================================

# Port number for the HTTP server (default: 3000)
PORT=3000

# Comma-separated list of allowed CORS origins
# If not set, CORS will be disabled (no cross-origin requests allowed)
# Example: http://localhost:3000,https://example.com,https://app.example.com
ALLOWED_ORIGINS=
59 changes: 59 additions & 0 deletions apps/custom-issuer/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# compiled output
/dist
/node_modules
/build

# Logs
logs
*.log
npm-debug.log*
pnpm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*

# OS
.DS_Store

# Tests
/coverage
/.nyc_output

# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace

# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json

# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local

# temp directory
.temp
.tmp

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json

# Keys
keys/
4 changes: 4 additions & 0 deletions apps/custom-issuer/.prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"singleQuote": true,
"trailingComma": "all"
}
99 changes: 99 additions & 0 deletions apps/custom-issuer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Custom Issuer Service

A NestJS service that validates incoming JWTs and issues new tokens with a different signing key.

## Quick Start

### 1. Install Dependencies

```bash
pnpm install
```

### 2. Generate Test Keys (Optional)

```bash
# Generate RSA key pair for testing
mkdir -p keys
openssl genrsa -out keys/signing-key.pem 2048
openssl rsa -in keys/signing-key.pem -pubout -out keys/validation-public-key.pem
chmod 600 keys/signing-key.pem
chmod 644 keys/validation-public-key.pem
```

### 3. Configure Environment Variables

Copy `.env.example` to `.env` and fill in the required values:

```bash
cp .env.example .env
```

Required variables:
- `KEY_BASE64` - Base64-encoded RSA private key for signing tokens (encode with: `cat signing-key.pem | base64`)
- `VALIDATION_PUBLIC_KEY_URL` - Firebase URL where the validation public key can be retrieved (e.g., `https://www.googleapis.com/robot/v1/metadata/x509/{service-account-email}`)
- `VALIDATION_ISSUER_URL` - Expected issuer URL that incoming JWTs must have in their `iss` claim (e.g., `https://securetoken.google.com/{project-id}`)
- `ISSUER_URL` - URL of the issuer service (e.g., `http://localhost:3000`)

Optional variables:
- `PORT` - Server port (default: 3000)
- `ALLOWED_ORIGINS` - Comma-separated CORS origins

### 4. Start the Service

```bash
# Development mode (with hot reload)
pnpm start:dev

# Production mode
pnpm build
pnpm start:prod
```

The service will be available at `http://localhost:3000` (or your configured port).

## Usage

### Issue a Token

```bash
POST /issuer/issue
Content-Type: application/json

{
"jwt": "your-validated-jwt-token"
}
```

The service will:
1. Validate the input JWT using the validation public key
2. Extract `sub`, `exp`, and `nbf` claims
3. Issue a new token signed with the signing private key
4. Include the `iss` claim with the issuer URL

### Generate Test JWTs

```bash
# Generate and send a test JWT to the service
pnpm generate:jwt

# Generate with custom options
pnpm generate:jwt -- --sub "user@example.com" --exp 7200
```

## Scripts

- `pnpm start:dev` - Start in development mode with hot reload
- `pnpm build` - Build for production
- `pnpm start:prod` - Start production server
- `pnpm test` - Run tests
- `pnpm generate:jwt` - Generate test JWT tokens

## Security

- Input validation with `class-validator`
- Request size limits (10KB)
- CORS protection
- Path traversal protection for key files
- Sensitive data redaction in error logs
- Rate limiting recommended for production
35 changes: 35 additions & 0 deletions apps/custom-issuer/eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// @ts-check
import eslint from '@eslint/js';
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
import globals from 'globals';
import tseslint from 'typescript-eslint';

export default tseslint.config(
{
ignores: ['eslint.config.mjs'],
},
eslint.configs.recommended,
...tseslint.configs.recommendedTypeChecked,
eslintPluginPrettierRecommended,
{
languageOptions: {
globals: {
...globals.node,
...globals.jest,
},
sourceType: 'commonjs',
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check package.json for module type and tsconfig for module settings
cat apps/custom-issuer/package.json | jq '.type // "not set"'
cat apps/custom-issuer/tsconfig.json | jq '.compilerOptions.module // "not set"'

Repository: Peersyst/fast-auth

Length of output: 82


🏁 Script executed:

#!/bin/bash
# Search for import/export statements in the custom-issuer app
echo "=== Checking for import statements ==="
rg "^import\s+" apps/custom-issuer/src --type ts --type js -c | head -20

echo -e "\n=== Checking for export statements ==="
rg "^export\s+" apps/custom-issuer/src --type ts --type js -c | head -20

echo -e "\n=== Checking current sourceType setting ==="
cat apps/custom-issuer/eslint.config.mjs | grep -A 2 -B 2 "sourceType"

Repository: Peersyst/fast-auth

Length of output: 1439


Change sourceType from 'commonjs' to 'module'.

The codebase extensively uses ES module syntax (import/export statements) throughout the source files, but ESLint's sourceType is configured as 'commonjs'. This mismatch causes the parser to analyze code with incorrect syntax expectations, which can lead to missed linting errors or incorrect rule behavior. Update to sourceType: 'module' to align with the project's actual module system.

🤖 Prompt for AI Agents
In @apps/custom-issuer/eslint.config.mjs at line 20, Update the ESLint config's
parser option by changing the sourceType value from 'commonjs' to 'module' in
the eslint.config.mjs file (the sourceType property currently set to
'commonjs'); this aligns ESLint parsing with the project's ES module syntax and
ensures the parser and rules correctly handle import/export statements.

parserOptions: {
projectService: true,
tsconfigRootDir: import.meta.dirname,
},
},
},
{
rules: {
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-floating-promises': 'warn',
'@typescript-eslint/no-unsafe-argument': 'warn',
"prettier/prettier": ["error", { endOfLine: "auto" }],
},
},
);
8 changes: 8 additions & 0 deletions apps/custom-issuer/nest-cli.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"$schema": "https://json.schemastore.org/nest-cli",
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"deleteOutDir": true
}
}
85 changes: 85 additions & 0 deletions apps/custom-issuer/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
{
"name": "custom-issuer",
"version": "0.0.1",
"description": "",
"author": "",
"private": true,
"license": "UNLICENSED",
"scripts": {
"build": "nest build",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "nest start",
"start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
"generate:jwt": "node scripts/generate-test-jwt.js"
},
"dependencies": {
"@nestjs/common": "^11.0.1",
"@nestjs/config": "^3.1.1",
"@nestjs/core": "^11.0.1",
"@nestjs/platform-express": "^11.0.1",
"class-transformer": "0.5.1",
"class-validator": "0.14.0",
"reflect-metadata": "^0.2.2",
"rxjs": "^7.8.1"
},
"devDependencies": {
"@eslint/eslintrc": "^3.2.0",
"@eslint/js": "^9.18.0",
"@nestjs/cli": "^11.0.0",
"@nestjs/schematics": "^11.0.0",
"@nestjs/testing": "^11.0.1",
"@types/express": "^5.0.0",
"@types/jest": "^30.0.0",
"@types/jsonwebtoken": "^9.0.5",
"@types/node": "^22.10.7",
"@types/supertest": "^6.0.2",
"eslint": "^9.18.0",
"eslint-config-prettier": "^10.0.1",
"eslint-plugin-prettier": "^5.2.2",
"globals": "^16.0.0",
"jest": "^30.0.0",
"jsonwebtoken": "^9.0.2",
Comment on lines +39 to +40
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

rg -n "require\(['\"]jsonwebtoken['\"]|from ['\"]jsonwebtoken['\"]" apps/custom-issuer/src/

Repository: Peersyst/fast-auth

Length of output: 156


Move jsonwebtoken from devDependencies to dependencies.

jsonwebtoken is imported at runtime in apps/custom-issuer/src/modules/issuer/issuer.service.ts and will be unavailable in production builds when placed in devDependencies.

🤖 Prompt for AI Agents
In @apps/custom-issuer/package.json around lines 42 - 43, The package.json
currently lists "jsonwebtoken" in devDependencies but it's required at runtime
by apps/custom-issuer/src/modules/issuer/issuer.service.ts; move the
"jsonwebtoken" entry from devDependencies to dependencies in package.json so
production builds include it, then run npm/yarn install to update lockfile and
ensure the runtime import of jsonwebtoken in issuer.service.ts resolves in
production.

"prettier": "^3.4.2",
"source-map-support": "^0.5.21",
"supertest": "^7.0.0",
"ts-jest": "^29.2.5",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat apps/custom-issuer/package.json | grep -E '"jest":|"ts-jest":'

Repository: Peersyst/fast-auth

Length of output: 122


🌐 Web query:

ts-jest 29 compatibility Jest 30

💡 Result:

Yes — ts-jest added Jest 30 support in the 29.x line. Upgrade to ts-jest >= 29.4.0 to use it with Jest 30. [1][2]

Sources:
[1] ts-jest v29.4.0 release notes (feat: support Jest 30). [2] ts-jest version/compatibility docs (29.x).


Upgrade ts-jest to 29.4.0 or higher for Jest 30 compatibility.

ts-jest 29.2.5 does not support Jest 30. Jest 30 support was added in ts-jest 29.4.0. Upgrade to ts-jest >= 29.4.0 to resolve compatibility issues.

🤖 Prompt for AI Agents
In @apps/custom-issuer/package.json at line 47, Update the ts-jest dependency in
package.json from "ts-jest": "^29.2.5" to at least "ts-jest": "^29.4.0" (or
newer) to ensure Jest 30 compatibility, then reinstall dependencies
(npm/yarn/pnpm) to update lockfiles and run the test suite/CI to verify no
regressions.

"ts-loader": "^9.5.2",
"ts-node": "^10.9.2",
"tsconfig-paths": "^4.2.0",
"typescript": "^5.7.3",
"typescript-eslint": "^8.20.0"
},
"jest": {
"preset": "ts-jest",
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"roots": [
"<rootDir>/src",
"<rootDir>/test"
],
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": [
"ts-jest",
{
"tsconfig": {
"module": "commonjs"
}
}
]
},
"collectCoverageFrom": [
"src/**/*.(t|j)s"
],
"coverageDirectory": "../coverage",
"testEnvironment": "node",
"moduleNameMapper": {
"^src/(.*)$": "<rootDir>/src/$1"
}
}
}
Loading
Loading