Skip to main content

Overview

Permission Mongo uses JSON Web Tokens (JWT) for authentication. All API endpoints (except health checks) require a valid JWT token in the Authorization header.

Authentication Flow

  1. Obtain a JWT token from your identity provider
  2. Include the token in the Authorization header with Bearer prefix
  3. The server validates the token signature and claims
  4. Requests are authorized based on JWT claims (tenant, roles, etc.)

Header Format

Include the JWT token in the Authorization header:
Authorization: Bearer <your_jwt_token>

Example Request

curl -X GET “http://localhost:8080/users
-H “Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9…”

JWT Configuration

Permission Mongo supports two JWT signing algorithms:

RS256 (RSA with SHA-256)

Recommended for production. Uses asymmetric public/private key pairs. auth: algorithm: RS256 public_key_file: /path/to/public_key.pem

OR

public_key: | -----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA… -----END PUBLIC KEY----- issuer: “https://your-auth-provider.com” audience: “permission-mongo-api”

HS256 (HMAC with SHA-256)

Simpler configuration using a shared secret. Suitable for development. auth: algorithm: HS256 secret: “your-secret-key-here” issuer: “https://your-auth-provider.com” audience: “permission-mongo-api”

JWT Claims

Permission Mongo validates and extracts these JWT claims:
sub
string
required
Subject - unique user identifier
tenant_id
string
required
Tenant identifier for multi-tenancy isolation
roles
array
Array of role strings (e.g., ["admin", "editor"])
iss
string
Issuer - must match configured issuer
aud
string | array
Audience - must include configured audience
exp
number
required
Expiration time (Unix timestamp)
iat
number
Issued at time (Unix timestamp)

Example JWT Payload

{
  "sub": "user_123456",
  "tenant_id": "tenant_abc",
  "roles": ["admin", "editor"],
  "department": "engineering",
  "iss": "https://auth.example.com",
  "aud": "permission-mongo-api",
  "exp": 1735689600,
  "iat": 1735686000
}

Token Validation

The server validates:
  1. Signature: Verifies token was signed by trusted issuer
  2. Algorithm: Ensures algorithm matches configuration (RS256 or HS256)
  3. Expiration: Checks token hasn’t expired (exp claim)
  4. Issuer: Validates iss claim matches configuration
  5. Audience: Ensures aud claim includes configured audience
  6. Required Claims: Verifies sub claim is present

Authentication Context

After successful authentication, the server creates an auth context available to all handlers:
type AuthContext struct {
    UserID   string                 // From 'sub' claim
    TenantID string                 // From 'tenant_id' claim
    Roles    []string               // From 'roles' claim
    Claims   map[string]interface{} // All JWT claims
}
This context is used for:
  • Multi-tenant data isolation
  • Role-based access control (RBAC)
  • Audit logging
  • Custom permission checks

Error Responses

Missing Authorization Header

curl -X GET “http://localhost:8080/users
```json
{
  "error": {
    "code": "UNAUTHORIZED",
    "message": "missing authorization header"
  },
  "meta": {
    "request_id": "e1f2a3b4c5d6"
  }
}
Status Code: 401 Unauthorized

Invalid Token Format

curl -X GET “http://localhost:8080/users
-H “Authorization: InvalidToken”
{
  "error": {
    "code": "UNAUTHORIZED",
    "message": "invalid authorization header format"
  },
  "meta": {
    "request_id": "f2a3b4c5d6e7"
  }
}

**Status Code**: `401 Unauthorized`

### Expired Token

curl -X GET "http://localhost:8080/users" \
  -H "Authorization: Bearer <expired_token>"

```json
{
  "error": {
    "code": "EXPIRED_TOKEN",
    "message": "token has expired"
  },
  "meta": {
    "request_id": "a3b4c5d6e7f8"
  }
}

**Status Code**: `401 Unauthorized`

### Invalid Signature

```json
{
  "error": {
    "code": "INVALID_TOKEN",
    "message": "invalid token signature"
  },
  "meta": {
    "request_id": "b4c5d6e7f8a9"
  }
}

**Status Code**: `401 Unauthorized`

### Invalid Issuer

```json
{
  "error": {
    "code": "INVALID_TOKEN",
    "message": "invalid token issuer"
  },
  "meta": {
    "request_id": "c5d6e7f8a9b0"
  }
}

**Status Code**: `401 Unauthorized`

### Invalid Audience

```json
{
  "error": {
    "code": "INVALID_TOKEN",
    "message": "invalid token audience"
  },
  "meta": {
    "request_id": "d6e7f8a9b0c1"
  }
}

**Status Code**: `401 Unauthorized`

## Token Generation

Permission Mongo does not generate JWT tokens. You must obtain tokens from your identity provider (e.g., Auth0, Keycloak, custom OAuth server).

### Example Token Generation (External)

Using a Node.js library:

const jwt = require('jsonwebtoken');
const fs = require('fs');

// For RS256
const privateKey = fs.readFileSync('private_key.pem');
const token = jwt.sign(
  {
    sub: 'user_123456',
    tenant_id: 'tenant_abc',
    roles: ['admin'],
    department: 'engineering'
  },
  privateKey,
  {
    algorithm: 'RS256',
    expiresIn: '1h',
    issuer: 'https://auth.example.com',
    audience: 'permission-mongo-api'
  }
);

console.log(token);

For HS256:

const token = jwt.sign(
  {
    sub: 'user_123456',
    tenant_id: 'tenant_abc',
    roles: ['admin']
  },
  'your-secret-key-here',
  {
    algorithm: 'HS256',
    expiresIn: '1h',
    issuer: 'https://auth.example.com',
    audience: 'permission-mongo-api'
  }
);

## Development Mode

<Warning>
  **Never use development mode in production!** It bypasses all authentication.
</Warning>

For local development and testing, you can bypass authentication:

export PM_DEV_MODE=true
export PM_DEV_TENANT_ID=000000000000000000000001

In dev mode:
- All requests are authenticated as an admin user
- No `Authorization` header required
- Tenant ID is set to `PM_DEV_TENANT_ID`
- Logs show "Dev mode enabled" warning

### Dev Mode Request

# No Authorization header needed in dev mode
curl -X GET "http://localhost:8080/users"

## Best Practices

1. **Use RS256 in Production**: More secure than HS256
2. **Short Token Expiry**: Recommend 1 hour or less
3. **Rotate Keys Regularly**: Update public keys periodically
4. **Validate Issuer**: Always configure and validate the `iss` claim
5. **Use HTTPS**: Encrypt tokens in transit
6. **Include Tenant ID**: Required for multi-tenant isolation
7. **Minimal Claims**: Only include necessary claims in JWT
8. **Token Refresh**: Implement refresh token flow in your app

## Troubleshooting

### "Invalid algorithm" Error

Ensure your JWT uses the same algorithm configured in the server (RS256 or HS256).

### "Invalid signature" Error

- For RS256: Verify the public key matches the private key used to sign tokens
- For HS256: Ensure the secret matches on both sides

### "Missing required claims" Error

Ensure your JWT includes the `sub` claim (user ID).

### Permission Denied

Authentication succeeded but authorization failed. Check:
- Tenant ID matches the resource tenant
- User has required roles in the policy
- RBAC rules allow the operation

Build docs developers (and LLMs) love