Files
self/docs/openid-compliance.md
Timur Gordon f970f3fb58 Add SelfFreezoneClient wrapper for Self components
- Created SelfFreezoneClient in Self components
- Wraps SDK FreezoneScriptClient for Self-specific operations
- Implements send_verification_email method
- Uses Rhai script template for email verification
- Includes template variable substitution
- Added serde-wasm-bindgen dependency

Usage:
  let client = SelfFreezoneClient::builder()
      .supervisor_url("http://localhost:8080")
      .secret("my-secret")
      .build()?;

  client.send_verification_email(
      "user@example.com",
      "123456",
      "https://verify.com/abc"
  ).await?;
2025-11-03 16:16:18 +01:00

12 KiB

OpenID Connect Compliance

Overview

Self implements OpenID Connect (OIDC) compatible endpoints while maintaining its self-sovereign identity principles. The implementation provides standard OAuth 2.0 and OIDC flows that can integrate with existing identity providers and relying party applications.

OpenID Connect Implementation

Supported Flows

1. Client Credentials Flow with Cryptographic Assertions

Self implements a modified client credentials flow using cryptographic signatures instead of client secrets:

sequenceDiagram
    participant C as Client
    participant S as Self Server
    participant RP as Relying Party

    Note over C,RP: Authentication Flow
    C->>S: POST /oauth/token (with signature)
    S->>S: Verify cryptographic signature
    S->>C: Return access_token (JWT)
    C->>S: GET /oauth/userinfo (Bearer token)
    S->>C: Return user claims
    C->>RP: Present identity claims

2. Authorization Code Flow (Future)

Planned implementation for web applications:

sequenceDiagram
    participant U as User
    participant RP as Relying Party
    participant S as Self Server

    U->>RP: Access protected resource
    RP->>S: Redirect to /oauth/authorize
    S->>U: Present consent screen
    U->>S: Approve/deny consent
    S->>RP: Redirect with authorization code
    RP->>S: POST /oauth/token (exchange code)
    S->>RP: Return access_token + id_token

Endpoint Compliance

Token Endpoint - /oauth/token

OAuth 2.0 Compliance:

  • Supports client_credentials grant type
  • Returns standard token response format
  • Implements proper error responses
  • Validates client authentication
  • 🔄 Planned: authorization_code grant type
  • 🔄 Planned: refresh_token support

Request Format:

{
  "grant_type": "client_credentials",
  "client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
  "client_assertion": "signed-jwt-containing-challenge-response",
  "public_key": "client-identifier",
  "challenge": "server-provided-challenge",
  "scope": "openid profile"
}

Response Format:

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

UserInfo Endpoint - /oauth/userinfo

OpenID Connect Compliance:

  • Requires Bearer token authentication
  • Returns standard OIDC claims
  • Supports CORS for cross-origin requests
  • Validates JWT token signatures
  • Returns appropriate error responses

Response Format:

{
  "sub": "user-unique-identifier",
  "email": "user@example.com",
  "name": "John Doe",
  "public_key": "04a1b2c3d4e5f6...",
  "created_at": "1640995200"
}

JWT Token Structure

Access Token Claims

struct Claims {
    // Standard OIDC claims
    sub: String,        // Subject identifier (public key)
    iss: String,        // Issuer ("self-sovereign-identity")
    aud: String,        // Audience ("identity-server")
    exp: usize,         // Expiration time
    iat: usize,         // Issued at time
    
    // OAuth 2.0 claims
    scope: String,      // Granted scopes
    
    // Self-specific claims
    challenge: Option<String>, // Authentication challenge
}

ID Token (Future Implementation)

struct IdTokenClaims {
    // Required OIDC claims
    sub: String,        // Subject identifier
    iss: String,        // Issuer
    aud: String,        // Audience (client_id)
    exp: usize,         // Expiration time
    iat: usize,         // Issued at time
    
    // Optional OIDC claims
    email: Option<String>,
    name: Option<String>,
    picture: Option<String>,
    
    // Authentication context
    auth_time: Option<usize>,
    nonce: Option<String>,
}

Discovery Document

OpenID Configuration Endpoint

Planned Implementation: /.well-known/openid-configuration

{
  "issuer": "https://identity.example.com",
  "authorization_endpoint": "https://identity.example.com/oauth/authorize",
  "token_endpoint": "https://identity.example.com/oauth/token",
  "userinfo_endpoint": "https://identity.example.com/oauth/userinfo",
  "jwks_uri": "https://identity.example.com/.well-known/jwks.json",
  
  "response_types_supported": [
    "code",
    "token",
    "id_token",
    "code token",
    "code id_token",
    "token id_token",
    "code token id_token"
  ],
  
  "grant_types_supported": [
    "authorization_code",
    "client_credentials",
    "refresh_token"
  ],
  
  "subject_types_supported": ["public"],
  
  "id_token_signing_alg_values_supported": ["HS256", "RS256"],
  
  "scopes_supported": [
    "openid",
    "profile",
    "email"
  ],
  
  "claims_supported": [
    "sub",
    "iss",
    "aud",
    "exp",
    "iat",
    "email",
    "name",
    "public_key",
    "created_at"
  ],
  
  "token_endpoint_auth_methods_supported": [
    "client_secret_post",
    "client_assertion"
  ],
  
  "token_endpoint_auth_signing_alg_values_supported": ["HS256", "RS256"]
}

JWKS Endpoint

Planned Implementation: /.well-known/jwks.json

{
  "keys": [
    {
      "kty": "oct",
      "use": "sig",
      "kid": "self-signing-key-1",
      "alg": "HS256",
      "k": "base64url-encoded-secret"
    }
  ]
}

Scope and Claims Mapping

Supported Scopes

Scope Description Claims Returned
openid OpenID Connect authentication sub, iss, aud, exp, iat
profile Basic profile information name, public_key, created_at
email Email address email

Claim Definitions

Claim Type Description Source
sub string Subject identifier (unique user ID) User record ID
email string Email address User registration
name string Display name User registration
public_key string Cryptographic public key Key generation
created_at string Account creation timestamp Registration time

Integration Examples

Relying Party Integration

JavaScript Client

class SelfOIDCClient {
    constructor(issuer, clientId) {
        this.issuer = issuer;
        this.clientId = clientId;
    }
    
    async authenticate(publicKey, signature, challenge) {
        const tokenResponse = await fetch(`${this.issuer}/oauth/token`, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify({
                grant_type: 'client_credentials',
                client_assertion_type: 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
                client_assertion: signature,
                public_key: publicKey,
                challenge: challenge,
                scope: 'openid profile email'
            })
        });
        
        const tokens = await tokenResponse.json();
        
        // Get user info
        const userInfoResponse = await fetch(`${this.issuer}/oauth/userinfo`, {
            headers: {
                'Authorization': `Bearer ${tokens.access_token}`
            }
        });
        
        return await userInfoResponse.json();
    }
}

Node.js Server

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

const app = express();

// Middleware to verify Self tokens
function verifySelfToken(req, res, next) {
    const authHeader = req.headers.authorization;
    if (!authHeader || !authHeader.startsWith('Bearer ')) {
        return res.status(401).json({ error: 'Missing or invalid authorization header' });
    }
    
    const token = authHeader.substring(7);
    
    try {
        // Verify token with Self's public key or shared secret
        const decoded = jwt.verify(token, process.env.SELF_JWT_SECRET);
        req.user = decoded;
        next();
    } catch (error) {
        return res.status(401).json({ error: 'Invalid token' });
    }
}

// Protected route
app.get('/protected', verifySelfToken, (req, res) => {
    res.json({
        message: 'Access granted',
        user: req.user
    });
});

Identity Provider Integration

SAML Bridge

// Future implementation: SAML assertion generation
pub struct SamlAssertion {
    pub subject: String,
    pub issuer: String,
    pub audience: String,
    pub attributes: HashMap<String, String>,
    pub signature: String,
}

impl SamlAssertion {
    pub fn from_oidc_claims(claims: &Claims) -> Self {
        let mut attributes = HashMap::new();
        attributes.insert("email".to_string(), claims.email.clone());
        attributes.insert("name".to_string(), claims.name.clone());
        attributes.insert("public_key".to_string(), claims.public_key.clone());
        
        SamlAssertion {
            subject: claims.sub.clone(),
            issuer: claims.iss.clone(),
            audience: "saml-service-provider".to_string(),
            attributes,
            signature: "".to_string(), // Generate SAML signature
        }
    }
}

Security Considerations

Token Security

  1. JWT Signing: Tokens signed with HMAC-SHA256 or RSA-SHA256
  2. Token Expiration: Short-lived tokens (1 hour default)
  3. Scope Validation: Strict scope checking for claims
  4. Audience Validation: Tokens bound to specific audiences

OIDC Security Best Practices

  1. PKCE Support: Planned for authorization code flow
  2. State Parameter: Anti-CSRF protection
  3. Nonce Validation: Replay attack prevention
  4. HTTPS Only: All endpoints require HTTPS in production

Self-Sovereign Considerations

  1. No Central Authority: Users control their own identity
  2. Cryptographic Authentication: No passwords or shared secrets
  3. Local Key Storage: Private keys never transmitted
  4. Minimal Data Collection: Only necessary claims stored

Compliance Status

OAuth 2.0 RFC 6749

Requirement Status Notes
Authorization Endpoint 🔄 Planned For authorization code flow
Token Endpoint Implemented Client credentials flow
Error Responses Implemented Standard error format
Access Token Format Implemented JWT tokens
Scope Parameter Implemented openid, profile, email

OpenID Connect Core 1.0

Requirement Status Notes
ID Token 🔄 Planned JWT format with required claims
UserInfo Endpoint Implemented Standard claims format
Discovery 🔄 Planned .well-known/openid-configuration
Authentication Implemented Cryptographic challenge-response
Claims Implemented Standard and custom claims

OpenID Connect Discovery 1.0

Requirement Status Notes
Configuration Endpoint 🔄 Planned Metadata document
JWKS Endpoint 🔄 Planned Public key distribution
Dynamic Registration Not Planned Self-sovereign model

Future Enhancements

Planned Features

  1. Authorization Code Flow: Full web application support
  2. ID Tokens: Standard OIDC ID token implementation
  3. Refresh Tokens: Long-lived session management
  4. Discovery Document: Standard OIDC discovery
  5. JWKS Endpoint: Public key distribution
  6. PKCE Support: Enhanced security for public clients

Advanced Features

  1. Federation: Trust relationships with other identity providers
  2. Delegation: Temporary identity delegation
  3. Multi-Factor: Additional authentication factors
  4. Device Flow: Support for device authentication
  5. Logout: Single logout implementation

Standards Compliance

  1. RFC 7636: PKCE implementation
  2. RFC 7662: Token introspection
  3. RFC 7009: Token revocation
  4. RFC 8693: Token exchange
  5. OpenID Connect Session Management: Session handling