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?;
This commit is contained in:
434
docs/openid-compliance.md
Normal file
434
docs/openid-compliance.md
Normal file
@@ -0,0 +1,434 @@
|
||||
# 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:
|
||||
|
||||
```mermaid
|
||||
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:
|
||||
|
||||
```mermaid
|
||||
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:**
|
||||
```json
|
||||
{
|
||||
"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:**
|
||||
```json
|
||||
{
|
||||
"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:**
|
||||
```json
|
||||
{
|
||||
"sub": "user-unique-identifier",
|
||||
"email": "user@example.com",
|
||||
"name": "John Doe",
|
||||
"public_key": "04a1b2c3d4e5f6...",
|
||||
"created_at": "1640995200"
|
||||
}
|
||||
```
|
||||
|
||||
### JWT Token Structure
|
||||
|
||||
#### Access Token Claims
|
||||
|
||||
```rust
|
||||
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)
|
||||
|
||||
```rust
|
||||
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`
|
||||
|
||||
```json
|
||||
{
|
||||
"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`
|
||||
|
||||
```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
|
||||
|
||||
```javascript
|
||||
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
|
||||
|
||||
```javascript
|
||||
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
|
||||
|
||||
```rust
|
||||
// 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
|
||||
Reference in New Issue
Block a user