Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/KTS-o7/permission-mongo/llms.txt

Use this file to discover all available pages before exploring further.

Permission Mongo uses JWT (JSON Web Tokens) for authentication. The system supports both RS256 (asymmetric) and HS256 (symmetric) signing algorithms.

Overview

The authentication system:
  • Validates JWT tokens from the Authorization header
  • Extracts user identity, tenant ID, and roles from token claims
  • Supports custom claims for fine-grained access control
  • Integrates with RBAC policies for permission enforcement

Token Structure

A valid JWT token must contain these required claims:
{
  "sub": "user-123",          // Subject (user ID) - REQUIRED
  "tenant_id": "acme-corp",   // Tenant identifier
  "roles": ["manager", "editor"],  // User roles
  "iat": 1640000000,           // Issued at (Unix timestamp)
  "exp": 1640003600,           // Expiration (Unix timestamp)
  "iss": "your-auth-service", // Issuer (optional)
  "aud": ["permission-mongo"]  // Audience (optional)
}
Custom claims can also be included:
{
  "sub": "user-456",
  "tenant_id": "acme-corp",
  "roles": ["sales-rep"],
  "department": "sales",     // Custom claim
  "region": "us-west",        // Custom claim
  "iat": 1640000000,
  "exp": 1640003600
}
Source: /home/daytona/workspace/source/pkg/auth/jwt.go:39-49

Configuration

1
Step 1: Choose Your Algorithm
2
Decide between RS256 (recommended for production) or HS256:
3
RS256 (Asymmetric)
4
  • More secure
  • Uses public/private key pair
  • Private key stays with auth service, public key with Permission Mongo
  • Recommended for production
  • 5
    HS256 (Symmetric)
    6
  • Simpler setup
  • Uses shared secret
  • Secret must be kept secure on both sides
  • Good for development and internal services
  • 7
    Step 2: Configure RS256 Authentication
    8
    For RS256, you need to provide the public key:
    9
    auth:
      algorithm: RS256
      public_key_file: /path/to/public.pem
      # OR provide key directly:
      # public_key: |
      #   -----BEGIN PUBLIC KEY-----
      #   MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...
      #   -----END PUBLIC KEY-----
      issuer: your-auth-service  # Optional: validate issuer
      audience: permission-mongo  # Optional: validate audience
    
    10
    Generate a key pair for testing:
    11
    # Generate private key
    openssl genrsa -out private.pem 2048
    
    # Extract public key
    openssl rsa -in private.pem -pubout -out public.pem
    
    12
    Source: /home/daytona/workspace/source/pkg/auth/jwt.go:29-37
    13
    Step 3: Configure HS256 Authentication
    14
    For HS256, provide a shared secret:
    15
    auth:
      algorithm: HS256
      secret: ${ENV.JWT_SECRET}  # Use environment variable
      issuer: your-auth-service
      audience: permission-mongo
    
    16
    Generate a strong secret:
    17
    openssl rand -base64 32
    

    Token Generation

    Your authentication service generates tokens. Here’s an example using Go:

    RS256 Token Generation

    import (
    	"crypto/rsa"
    	"time"
    	"github.com/golang-jwt/jwt/v5"
    )
    
    func generateRS256Token(privateKey *rsa.PrivateKey, userID, tenantID string, roles []string) (string, error) {
    	claims := jwt.MapClaims{
    		"sub":       userID,
    		"tenant_id": tenantID,
    		"roles":     roles,
    		"iat":       time.Now().Unix(),
    		"exp":       time.Now().Add(time.Hour).Unix(),
    		"iss":       "your-auth-service",
    		"aud":       []string{"permission-mongo"},
    	}
    
    	token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
    	return token.SignedString(privateKey)
    }
    

    HS256 Token Generation

    func generateHS256Token(secret, userID, tenantID string, roles []string) (string, error) {
    	claims := jwt.MapClaims{
    		"sub":       userID,
    		"tenant_id": tenantID,
    		"roles":     roles,
    		"iat":       time.Now().Unix(),
    		"exp":       time.Now().Add(time.Hour).Unix(),
    	}
    
    	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
    	return token.SignedString([]byte(secret))
    }
    
    Source: /home/daytona/workspace/source/pkg/auth/jwt_test.go:53-71

    Making Authenticated Requests

    Include the JWT token in the Authorization header:
    curl -X GET https://api.example.com/products \
      -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
    
    The middleware extracts and validates the token: Source: /home/daytona/workspace/source/pkg/api/middleware.go:21-69

    Custom Claims in Policies

    Custom JWT claims can be used in RBAC policy conditions:
    policies:
      sales_leads:
        sales_rep:
          actions: [read, update]
          when: |
            doc.region == user.claims.region &&
            doc.status == "open"
    
    The user.claims context includes all JWT claims:
    // Available in policy expressions:
    user.claims.department  // "sales"
    user.claims.region      // "us-west"
    user.claims.level       // "senior"
    
    Source: /home/daytona/workspace/source/pkg/auth/context.go:101-118

    Token Validation

    Permission Mongo validates tokens automatically:
    1
    Step 1: Signature Validation
    2
    Verifies the token was signed by the expected key:
    3
    # RS256: Verifies with public key
    # HS256: Verifies with shared secret
    
    4
    Step 2: Expiration Check
    5
    Rejects expired tokens:
    6
    {
      "error": {
        "code": "expired_token",
        "message": "token has expired"
      }
    }
    
    7
    Step 3: Issuer & Audience
    8
    Validates iss and aud claims if configured:
    9
    auth:
      issuer: your-auth-service    # Must match token's "iss"
      audience: permission-mongo    # Must be in token's "aud" array
    
    10
    Step 4: Required Claims
    11
    Ensures sub (subject) claim is present:
    12
    {
      "sub": "user-123"  // Required - user identifier
    }
    
    Source: /home/daytona/workspace/source/pkg/auth/jwt.go:150-215

    Error Responses

    Invalid Token

    {
      "error": {
        "code": "invalid_token",
        "message": "invalid token"
      }
    }
    

    Expired Token

    {
      "error": {
        "code": "expired_token",
        "message": "token has expired"
      }
    }
    

    Invalid Signature

    {
      "error": {
        "code": "invalid_token",
        "message": "invalid token signature"
      }
    }
    

    Missing Authorization

    {
      "error": {
        "code": "unauthorized",
        "message": "missing authorization header"
      }
    }
    
    Source: /home/daytona/workspace/source/pkg/api/middleware.go:46-60

    Development Mode

    For local development, you can bypass authentication:
    server:
      dev_mode: true
      dev_tenant: test-corp
    
    This sets a default admin context without requiring tokens.
    Never enable dev_mode in production! It bypasses all authentication.
    Source: /home/daytona/workspace/source/pkg/api/middleware.go:107-131

    Multi-Tenancy

    The tenant_id claim automatically scopes all operations:
    {
      "sub": "user-123",
      "tenant_id": "acme-corp",  // All queries filtered by this
      "roles": ["admin"]
    }
    
    Queries are automatically filtered:
    // User queries:
    GET /products
    
    // Automatically becomes:
    GET /products?tenant_id=acme-corp
    
    Source: /home/daytona/workspace/source/pkg/auth/context.go:16-25

    Best Practices

    Use RS256 in production for better security
    Set short expiration times (15-60 minutes)
    Validate issuer and audience claims
    Store secrets in environment variables, not config files
    Rotate signing keys regularly
    Include minimal claims in tokens (roles, tenant_id)
    Use refresh tokens for long-lived sessions

    Testing Authentication

    Create test tokens for development:
    func TestCreateProduct(t *testing.T) {
    	// Generate test token
    	token := createHS256Token(jwt.MapClaims{
    		"sub":       "user-123",
    		"tenant_id": "test-corp",
    		"roles":     []string{"admin"},
    		"iat":       time.Now().Unix(),
    		"exp":       time.Now().Add(time.Hour).Unix(),
    	}, "test-secret")
    
    	// Make authenticated request
    	resp := makeRequest("POST", "/products", token, productData)
    	assert.Equal(t, 201, resp.StatusCode)
    }
    
    Source: /home/daytona/workspace/source/pkg/auth/jwt_test.go:190-233

    Next Steps

    Multi-Tenancy

    Configure tenant isolation and data scoping

    RBAC Policies

    Define role-based permissions

    Build docs developers (and LLMs) love