Skip to content

Latest commit

 

History

History
480 lines (383 loc) · 9.95 KB

File metadata and controls

480 lines (383 loc) · 9.95 KB

Authentication Guide

WFM Archive supports multiple authentication methods for API access and user management.

Authentication Methods

1. JWT Token Authentication (Recommended)

The primary authentication method uses JWT tokens obtained through the login endpoint.

Login Request

POST /auth/login

curl -X POST "{base_url}/auth/login" \
  -H "Content-Type: application/json" \
  -d '{
    "username": "john.doe",
    "password": "SecurePassword123!"
  }'

Login Response

{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "user": {
    "id": "user-123",
    "username": "john.doe",
    "email": "john.doe@company.com",
    "roles": ["DOCUMENT_VIEWER", "DOCUMENT_EDITOR"]
  }
}

Using the Token

Include the token in the Authorization header:

curl -X GET "{base_url}/api/documents" \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Or as a query parameter (for file downloads):

curl -X GET "{base_url}/api/documents/download?access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

2. Keycloak SSO Integration

For enterprise Single Sign-On, WFM Archive integrates with Keycloak.

Keycloak Configuration

1. Create Realm
{
  "realm": "wfmarchive",
  "enabled": true,
  "sslRequired": "external",
  "registrationAllowed": false,
  "loginWithEmailAllowed": true,
  "duplicateEmailsAllowed": false,
  "resetPasswordAllowed": true,
  "editUsernameAllowed": false,
  "bruteForceProtected": true
}
2. Create Client
{
  "clientId": "wfmarchive-app",
  "enabled": true,
  "clientAuthenticatorType": "client-secret",
  "redirectUris": [
    "http://localhost:8080/*",
    "https://wfmarchive.yourdomain.com/*"
  ],
  "webOrigins": ["+"],
  "protocol": "openid-connect",
  "standardFlowEnabled": true,
  "implicitFlowEnabled": false,
  "directAccessGrantsEnabled": true,
  "serviceAccountsEnabled": true
}
3. Configure Application

In app.properties:

keycloak.enabled = true
keycloak.serverUrl = https://keycloak.yourdomain.com/auth
keycloak.realm = wfmarchive
keycloak.clientId = wfmarchive-app
keycloak.clientSecret = ${KEYCLOAK_CLIENT_SECRET}
keycloak.ssl.required = external

SSO Login Flow

sequenceDiagram
    participant User
    participant App as WFM Archive
    participant KC as Keycloak

    User->>App: Access Protected Resource
    App->>User: Redirect to Keycloak
    User->>KC: Login with Credentials
    KC->>KC: Validate Credentials
    KC->>User: Return with Auth Code
    User->>App: Present Auth Code
    App->>KC: Exchange Code for Token
    KC->>App: Return Access Token
    App->>User: Grant Access
Loading

Using Keycloak Tokens

# Get token from Keycloak
curl -X POST "https://keycloak.yourdomain.com/auth/realms/wfmarchive/protocol/openid-connect/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "client_id=wfmarchive-app" \
  -d "client_secret=your-secret" \
  -d "grant_type=password" \
  -d "username=john.doe" \
  -d "password=password"

# Use token with API
curl -X GET "{base_url}/api/documents" \
  -H "Authorization: Bearer {keycloak_token}"

3. API Key Authentication

For system-to-system integration, API keys provide a simple authentication method.

Generate API Key

POST /auth/api-key

curl -X POST "{base_url}/auth/api-key" \
  -H "Authorization: Bearer {admin_token}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Integration Service",
    "expiresAt": "2026-12-31T23:59:59Z",
    "scopes": ["documents:read", "documents:write"]
  }'

Response:

{
  "apiKey": "wfm_ak_1234567890abcdef",
  "name": "Integration Service",
  "created": "2025-09-21T10:00:00Z",
  "expiresAt": "2026-12-31T23:59:59Z",
  "scopes": ["documents:read", "documents:write"]
}

Using API Keys

Include the API key in the X-API-Key header:

curl -X GET "{base_url}/api/documents" \
  -H "X-API-Key: wfm_ak_1234567890abcdef"

Token Management

Refresh Token

POST /auth/refresh

curl -X POST "{base_url}/auth/refresh" \
  -H "Content-Type: application/json" \
  -d '{
    "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
  }'

Response:

{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "Bearer",
  "expires_in": 3600
}

Logout

POST /auth/logout

curl -X POST "{base_url}/auth/logout" \
  -H "Authorization: Bearer {token}"

Token Validation

GET /auth/validate

curl -X GET "{base_url}/auth/validate" \
  -H "Authorization: Bearer {token}"

Response:

{
  "valid": true,
  "expires_in": 1800,
  "user": {
    "id": "user-123",
    "username": "john.doe"
  }
}

Security Headers

Required Headers

Authorization: Bearer {token}
Content-Type: application/json
X-Request-ID: {unique-request-id}

CORS Configuration

# CORS settings in web-app.properties
cuba.rest.cors.allowedOrigins = https://app.yourdomain.com
cuba.rest.cors.allowedMethods = GET,POST,PUT,DELETE,OPTIONS
cuba.rest.cors.allowedHeaders = Authorization,Content-Type,X-Request-ID,X-API-Key
cuba.rest.cors.exposedHeaders = X-Total-Count,X-Page-Count
cuba.rest.cors.maxAge = 3600

Role-Based Access Control (RBAC)

Predefined Roles

Role Description Permissions
ADMIN Full system access All operations
DOCUMENT_MANAGER Manage documents Create, read, update, delete documents
DOCUMENT_EDITOR Edit documents Create, read, update documents
DOCUMENT_VIEWER View documents Read documents only
API_USER API access Based on API key scopes

Permission Scopes

{
  "documents:read": "View documents",
  "documents:write": "Create and update documents",
  "documents:delete": "Delete documents",
  "admin:users": "Manage users",
  "admin:config": "System configuration",
  "admin:audit": "View audit logs"
}

Check Permissions

GET /auth/permissions

curl -X GET "{base_url}/auth/permissions" \
  -H "Authorization: Bearer {token}"

Response:

{
  "roles": ["DOCUMENT_EDITOR"],
  "permissions": [
    "documents:read",
    "documents:write"
  ],
  "organizations": ["org-001", "org-002"]
}

Multi-Factor Authentication (MFA)

Enable MFA

POST /auth/mfa/enable

curl -X POST "{base_url}/auth/mfa/enable" \
  -H "Authorization: Bearer {token}"

Response:

{
  "secret": "JBSWY3DPEHPK3PXP",
  "qr_code": "data:image/png;base64,iVBORw0KGgo...",
  "backup_codes": [
    "12345678",
    "87654321",
    "11111111"
  ]
}

Verify MFA Code

POST /auth/mfa/verify

curl -X POST "{base_url}/auth/mfa/verify" \
  -H "Content-Type: application/json" \
  -d '{
    "code": "123456"
  }'

Security Best Practices

1. Token Security

  • Store tokens securely (never in localStorage for web apps)
  • Use short expiration times (1 hour for access tokens)
  • Implement token refresh mechanism
  • Revoke tokens on logout

2. API Key Management

  • Rotate API keys regularly
  • Use different keys for different environments
  • Restrict API key scopes to minimum required
  • Monitor API key usage

3. HTTPS Configuration

  • Always use HTTPS in production
  • Implement certificate pinning for mobile apps
  • Use TLS 1.2 or higher
  • Enable HSTS headers

4. Rate Limiting

# Rate limiting configuration
security.rateLimit.enabled = true
security.rateLimit.requests = 1000
security.rateLimit.window = 3600
security.rateLimit.blockDuration = 900

5. Audit Logging

All authentication events are logged:

  • Successful logins
  • Failed login attempts
  • Token refreshes
  • Permission changes
  • API key usage

Troubleshooting

Common Issues

Invalid Token

{
  "error": "INVALID_TOKEN",
  "message": "Token is invalid or expired",
  "code": 401
}

Solution: Refresh token or re-authenticate

Insufficient Permissions

{
  "error": "FORBIDDEN",
  "message": "Insufficient permissions for this operation",
  "code": 403
}

Solution: Check user roles and permissions

Keycloak Connection Failed

{
  "error": "SSO_ERROR",
  "message": "Unable to connect to Keycloak server",
  "code": 503
}

Solution: Verify Keycloak URL and network connectivity

Integration Examples

JavaScript/Node.js

const axios = require('axios');

class WFMArchiveClient {
  constructor(baseUrl) {
    this.baseUrl = baseUrl;
    this.token = null;
  }

  async login(username, password) {
    const response = await axios.post(`${this.baseUrl}/auth/login`, {
      username,
      password
    });
    this.token = response.data.access_token;
    return this.token;
  }

  async getDocuments() {
    return await axios.get(`${this.baseUrl}/api/documents`, {
      headers: {
        'Authorization': `Bearer ${this.token}`
      }
    });
  }
}

// Usage
const client = new WFMArchiveClient('https://api.example.com');
await client.login('john.doe', 'password');
const documents = await client.getDocuments();

Python

import requests

class WFMArchiveClient:
    def __init__(self, base_url):
        self.base_url = base_url
        self.token = None

    def login(self, username, password):
        response = requests.post(f"{self.base_url}/auth/login", json={
            "username": username,
            "password": password
        })
        self.token = response.json()["access_token"]
        return self.token

    def get_documents(self):
        headers = {"Authorization": f"Bearer {self.token}"}
        return requests.get(f"{self.base_url}/api/documents", headers=headers)

# Usage
client = WFMArchiveClient("https://api.example.com")
client.login("john.doe", "password")
documents = client.get_documents()

Next Steps