- 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?;
435 lines
12 KiB
Markdown
435 lines
12 KiB
Markdown
# 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
|