omniauth_openid_federation 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +16 -0
- data/LICENSE.md +22 -0
- data/README.md +822 -0
- data/SECURITY.md +129 -0
- data/examples/README_INTEGRATION_TESTING.md +399 -0
- data/examples/README_MOCK_OP.md +243 -0
- data/examples/app/controllers/users/omniauth_callbacks_controller.rb.example +33 -0
- data/examples/app/jobs/jwks_rotation_job.rb.example +60 -0
- data/examples/app/models/user.rb.example +39 -0
- data/examples/config/initializers/devise.rb.example +97 -0
- data/examples/config/initializers/federation_endpoint.rb.example +206 -0
- data/examples/config/mock_op.yml.example +83 -0
- data/examples/config/open_id_connect_config.rb.example +210 -0
- data/examples/config/routes.rb.example +12 -0
- data/examples/db/migrate/add_omniauth_to_users.rb.example +16 -0
- data/examples/integration_test_flow.rb +1334 -0
- data/examples/jobs/README.md +194 -0
- data/examples/jobs/federation_cache_refresh_job.rb.example +78 -0
- data/examples/jobs/federation_files_generation_job.rb.example +87 -0
- data/examples/mock_op_server.rb +775 -0
- data/examples/mock_rp_server.rb +435 -0
- data/lib/omniauth_openid_federation/access_token.rb +504 -0
- data/lib/omniauth_openid_federation/cache.rb +39 -0
- data/lib/omniauth_openid_federation/cache_adapter.rb +173 -0
- data/lib/omniauth_openid_federation/configuration.rb +135 -0
- data/lib/omniauth_openid_federation/constants.rb +13 -0
- data/lib/omniauth_openid_federation/endpoint_resolver.rb +168 -0
- data/lib/omniauth_openid_federation/entity_statement_reader.rb +122 -0
- data/lib/omniauth_openid_federation/errors.rb +52 -0
- data/lib/omniauth_openid_federation/federation/entity_statement.rb +331 -0
- data/lib/omniauth_openid_federation/federation/entity_statement_builder.rb +188 -0
- data/lib/omniauth_openid_federation/federation/entity_statement_fetcher.rb +142 -0
- data/lib/omniauth_openid_federation/federation/entity_statement_helper.rb +87 -0
- data/lib/omniauth_openid_federation/federation/entity_statement_parser.rb +198 -0
- data/lib/omniauth_openid_federation/federation/entity_statement_validator.rb +502 -0
- data/lib/omniauth_openid_federation/federation/metadata_policy_merger.rb +276 -0
- data/lib/omniauth_openid_federation/federation/signed_jwks.rb +210 -0
- data/lib/omniauth_openid_federation/federation/trust_chain_resolver.rb +225 -0
- data/lib/omniauth_openid_federation/federation_endpoint.rb +949 -0
- data/lib/omniauth_openid_federation/http_client.rb +70 -0
- data/lib/omniauth_openid_federation/instrumentation.rb +383 -0
- data/lib/omniauth_openid_federation/jwks/cache.rb +76 -0
- data/lib/omniauth_openid_federation/jwks/decode.rb +174 -0
- data/lib/omniauth_openid_federation/jwks/fetch.rb +153 -0
- data/lib/omniauth_openid_federation/jwks/normalizer.rb +49 -0
- data/lib/omniauth_openid_federation/jwks/rotate.rb +97 -0
- data/lib/omniauth_openid_federation/jwks/selector.rb +101 -0
- data/lib/omniauth_openid_federation/jws.rb +416 -0
- data/lib/omniauth_openid_federation/key_extractor.rb +173 -0
- data/lib/omniauth_openid_federation/logger.rb +99 -0
- data/lib/omniauth_openid_federation/rack_endpoint.rb +187 -0
- data/lib/omniauth_openid_federation/railtie.rb +29 -0
- data/lib/omniauth_openid_federation/rate_limiter.rb +55 -0
- data/lib/omniauth_openid_federation/strategy.rb +2029 -0
- data/lib/omniauth_openid_federation/string_helpers.rb +30 -0
- data/lib/omniauth_openid_federation/tasks_helper.rb +428 -0
- data/lib/omniauth_openid_federation/utils.rb +166 -0
- data/lib/omniauth_openid_federation/validators.rb +126 -0
- data/lib/omniauth_openid_federation/version.rb +3 -0
- data/lib/omniauth_openid_federation.rb +98 -0
- data/lib/tasks/omniauth_openid_federation.rake +376 -0
- data/sig/federation.rbs +218 -0
- data/sig/jwks.rbs +63 -0
- data/sig/omniauth_openid_federation.rbs +254 -0
- data/sig/strategy.rbs +60 -0
- metadata +352 -0
data/SECURITY.md
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# SECURITY
|
|
2
|
+
|
|
3
|
+
## Reporting a Vulnerability
|
|
4
|
+
|
|
5
|
+
**Do NOT** open a public GitHub issue for security vulnerabilities.
|
|
6
|
+
|
|
7
|
+
Email security details to: **contact@kiskolabs.com**
|
|
8
|
+
|
|
9
|
+
Include: description, steps to reproduce, potential impact, and suggested fix (if available).
|
|
10
|
+
|
|
11
|
+
**Response Timeline:**
|
|
12
|
+
- Acknowledgment within 48 hours
|
|
13
|
+
- Initial assessment within 7 days
|
|
14
|
+
- Coordinated disclosure after patching
|
|
15
|
+
|
|
16
|
+
## Library Security Features
|
|
17
|
+
|
|
18
|
+
### Implemented Protections
|
|
19
|
+
|
|
20
|
+
**✅ Constant-Time State Comparison**
|
|
21
|
+
- Uses `Rack::Utils.secure_compare` for state parameter validation to prevent timing attacks
|
|
22
|
+
- Location: `strategy.rb:182`
|
|
23
|
+
|
|
24
|
+
**✅ Path Traversal Protection**
|
|
25
|
+
- `Utils.validate_file_path!` prevents `..` sequences and validates against allowed directories
|
|
26
|
+
- File paths are restricted to configured allowed directories
|
|
27
|
+
|
|
28
|
+
**✅ Signed Request Objects (RFC 9101)**
|
|
29
|
+
- All authorization requests **MUST** use signed request objects (mandatory per OpenID Federation 1.0 spec)
|
|
30
|
+
- Signing is enforced at library level - cannot be bypassed
|
|
31
|
+
- Request objects **MAY** be encrypted (optional) when provider requires it or `always_encrypt_request_object` is enabled
|
|
32
|
+
- Prevents parameter tampering and ensures request authenticity
|
|
33
|
+
|
|
34
|
+
**✅ JWT Algorithm Validation**
|
|
35
|
+
- JWT library validates algorithms and signatures
|
|
36
|
+
- Unsigned tokens (`alg: none`) are explicitly handled and rejected for signed tokens
|
|
37
|
+
- Only strong algorithms accepted (RS256, etc.)
|
|
38
|
+
|
|
39
|
+
**✅ Logging Sanitization**
|
|
40
|
+
- Sensitive data (tokens, keys) is never logged
|
|
41
|
+
- File paths are sanitized before logging
|
|
42
|
+
- URLs are sanitized (query parameters removed) in debug logs
|
|
43
|
+
|
|
44
|
+
**✅ Timeout Limits**
|
|
45
|
+
- HTTP requests have configurable timeouts to prevent long-running requests
|
|
46
|
+
|
|
47
|
+
### Known Limitations & Risks
|
|
48
|
+
|
|
49
|
+
**⚠️ SSRF Risk (Server-Side Request Forgery)**
|
|
50
|
+
- The library fetches entity statements and JWKS from URLs without validating against internal network access
|
|
51
|
+
- **Risk:** URLs could target localhost, private IPs, or cloud metadata endpoints
|
|
52
|
+
- **Mitigation:** Application should validate URLs before passing to library, or implement URL validation in library configuration
|
|
53
|
+
|
|
54
|
+
**⚠️ Memory Safety**
|
|
55
|
+
- Ruby does not provide secure memory management for sensitive data
|
|
56
|
+
- Private keys may persist in memory until garbage collection
|
|
57
|
+
- Memory dumps may contain key material
|
|
58
|
+
- **Mitigation:** Use secure environments and access controls
|
|
59
|
+
|
|
60
|
+
**⚠️ SSL/TLS Verification**
|
|
61
|
+
- SSL verification is automatically disabled in Rails development mode
|
|
62
|
+
- **Production:** Application must ensure SSL verification is enabled
|
|
63
|
+
|
|
64
|
+
**⚠️ Entity Statement Validation**
|
|
65
|
+
- Fingerprint validation is optional - skipping validation may allow malicious entity statements
|
|
66
|
+
- **Recommendation:** Always validate entity statement fingerprints when fetching from untrusted sources
|
|
67
|
+
|
|
68
|
+
**⚠️ Key Rotation Window**
|
|
69
|
+
- Brief window (up to cache TTL, default 24 hours) where rotated keys might not be immediately available
|
|
70
|
+
- Library handles this with retry logic, but applications should monitor rotation events
|
|
71
|
+
|
|
72
|
+
## Input/Output Points
|
|
73
|
+
|
|
74
|
+
### Input Points (Library Handles)
|
|
75
|
+
|
|
76
|
+
1. **OAuth Callback Parameters** (`code`, `state`, `error`)
|
|
77
|
+
- State validated with constant-time comparison
|
|
78
|
+
- Authorization codes are single-use and time-limited
|
|
79
|
+
|
|
80
|
+
2. **OAuth Request Parameters** (`acr_values`, `login_hint`, etc.)
|
|
81
|
+
- All parameters are signed in JWT request objects (RFC 9101)
|
|
82
|
+
- Parameters validated before inclusion
|
|
83
|
+
|
|
84
|
+
3. **Entity Statement URLs**
|
|
85
|
+
- Fetched via HTTP client
|
|
86
|
+
- **SSRF Risk:** No validation against internal network access
|
|
87
|
+
|
|
88
|
+
4. **File Paths** (configuration)
|
|
89
|
+
- Validated with `Utils.validate_file_path!` to prevent path traversal
|
|
90
|
+
- Restricted to allowed directories
|
|
91
|
+
|
|
92
|
+
5. **JWT/JWE Tokens** (from OAuth provider)
|
|
93
|
+
- Algorithm validation enforced
|
|
94
|
+
- Signature validation required for signed tokens
|
|
95
|
+
- Entity statement fingerprints provide additional validation
|
|
96
|
+
|
|
97
|
+
### Output Points (Library Exposes)
|
|
98
|
+
|
|
99
|
+
1. **HTTP Responses** (if using `RackEndpoint`)
|
|
100
|
+
- `/.well-known/openid-federation` - Entity statement (JWT)
|
|
101
|
+
- `/.well-known/jwks.json` - Public JWKS
|
|
102
|
+
- `/.well-known/signed-jwks.json` - Signed JWKS (JWT)
|
|
103
|
+
- Only public keys exposed (private keys never exposed)
|
|
104
|
+
|
|
105
|
+
2. **Logs**
|
|
106
|
+
- Sensitive data (tokens, keys) never logged
|
|
107
|
+
- File paths and URLs sanitized before logging
|
|
108
|
+
|
|
109
|
+
3. **Error Messages**
|
|
110
|
+
- File paths sanitized in error messages
|
|
111
|
+
- Generic error messages (stack traces only in development)
|
|
112
|
+
|
|
113
|
+
## Security Considerations
|
|
114
|
+
|
|
115
|
+
- **Timing Attacks**: State parameter protected with constant-time comparison
|
|
116
|
+
- **Path Traversal**: ✅ `Utils.validate_file_path!` prevents `..` sequences
|
|
117
|
+
- **JWT Algorithm Confusion**: ✅ Algorithm validation enforced, `alg: none` rejected
|
|
118
|
+
- **Replay Attacks**: Authorization codes are single-use and time-limited
|
|
119
|
+
|
|
120
|
+
## Automation Security
|
|
121
|
+
|
|
122
|
+
* **Context Isolation:** It is strictly forbidden to include production credentials, API keys, or Personally Identifiable Information (PII) in prompts sent to third-party LLMs or automation services.
|
|
123
|
+
|
|
124
|
+
* **Supply Chain:** All automated dependencies must be verified.
|
|
125
|
+
|
|
126
|
+
## Contact
|
|
127
|
+
|
|
128
|
+
**Security concerns**: contact@kiskolabs.com
|
|
129
|
+
**General support**: https://github.com/amkisko/omniauth_openid_federation.rb/issues
|
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
# OpenID Federation Integration Testing
|
|
2
|
+
|
|
3
|
+
This directory contains comprehensive mock servers and integration tests for the complete OpenID Federation flow.
|
|
4
|
+
|
|
5
|
+
## Architecture
|
|
6
|
+
|
|
7
|
+
The integration test setup consists of:
|
|
8
|
+
|
|
9
|
+
1. **Mock OP Server** (`mock_op_server.rb`) - Simulates an OpenID Provider
|
|
10
|
+
2. **Mock RP Server** (`mock_rp_server.rb`) - Simulates a Relying Party (Client)
|
|
11
|
+
3. **Integration Test Flow** (`integration_test_flow.rb`) - Automated tests for the complete flow
|
|
12
|
+
|
|
13
|
+
## Complete OpenID Federation Flow
|
|
14
|
+
|
|
15
|
+
### 1. Provider Exposes Entity Statement with JWKS
|
|
16
|
+
|
|
17
|
+
The OP server exposes its entity configuration at:
|
|
18
|
+
```
|
|
19
|
+
GET /.well-known/openid-federation
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Returns a signed JWT containing:
|
|
23
|
+
- Provider metadata (authorization_endpoint, token_endpoint, etc.)
|
|
24
|
+
- JWKS (public keys for signing/encryption)
|
|
25
|
+
- Authority hints (if subordinate to a Trust Anchor)
|
|
26
|
+
|
|
27
|
+
### 2. Client Exposes Entity Statement with JWKS
|
|
28
|
+
|
|
29
|
+
The RP server exposes its entity configuration at:
|
|
30
|
+
```
|
|
31
|
+
GET /.well-known/openid-federation
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Returns a signed JWT containing:
|
|
35
|
+
- RP metadata (redirect_uris, client_name, etc.)
|
|
36
|
+
- JWKS (public keys for signing request objects)
|
|
37
|
+
- Authority hints (if subordinate to a Trust Anchor)
|
|
38
|
+
|
|
39
|
+
### 3. Client Fetches Provider Statement with Keys
|
|
40
|
+
|
|
41
|
+
When the RP initiates login:
|
|
42
|
+
1. RP fetches OP's entity statement from `/.well-known/openid-federation`
|
|
43
|
+
2. RP validates the entity statement signature
|
|
44
|
+
3. RP extracts OP's JWKS for future token validation
|
|
45
|
+
4. RP extracts OP's metadata (endpoints, capabilities)
|
|
46
|
+
|
|
47
|
+
### 4. Client Sends Login Request
|
|
48
|
+
|
|
49
|
+
RP generates a signed request object (JWT) containing:
|
|
50
|
+
- `client_id`: RP's Entity ID
|
|
51
|
+
- `redirect_uri`: Callback URL
|
|
52
|
+
- `scope`: Requested scopes
|
|
53
|
+
- `state`: CSRF protection
|
|
54
|
+
- `nonce`: Replay protection
|
|
55
|
+
- `aud`: OP's Entity ID
|
|
56
|
+
|
|
57
|
+
The request object is:
|
|
58
|
+
- **Always signed** with RP's private key (RFC 9101 requirement)
|
|
59
|
+
- **Optionally encrypted** if OP requires encryption
|
|
60
|
+
|
|
61
|
+
RP redirects to OP's authorization endpoint:
|
|
62
|
+
```
|
|
63
|
+
GET /auth?request=<signed_jwt>
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### 5. Provider Fetches Client Statement and Keys
|
|
67
|
+
|
|
68
|
+
When OP receives the authorization request:
|
|
69
|
+
1. OP extracts `client_id` from the request object
|
|
70
|
+
2. OP fetches RP's entity statement from `/.well-known/openid-federation`
|
|
71
|
+
3. OP validates RP's entity statement signature
|
|
72
|
+
4. OP extracts RP's JWKS from the entity statement
|
|
73
|
+
5. OP validates the request object signature using RP's public key
|
|
74
|
+
6. OP resolves RP's trust chain (if trust anchors configured)
|
|
75
|
+
7. OP applies metadata policies from trust chain
|
|
76
|
+
8. OP validates `redirect_uri` against RP's allowed redirect URIs
|
|
77
|
+
|
|
78
|
+
### 6. Exchange and Authenticated Login
|
|
79
|
+
|
|
80
|
+
After user authorization:
|
|
81
|
+
1. OP redirects back to RP with authorization code
|
|
82
|
+
2. RP exchanges code for tokens at OP's token endpoint
|
|
83
|
+
3. OP returns ID token (signed with OP's private key)
|
|
84
|
+
4. RP validates ID token signature using OP's JWKS
|
|
85
|
+
5. RP validates ID token claims (iss, aud, exp, nonce)
|
|
86
|
+
6. User is authenticated
|
|
87
|
+
|
|
88
|
+
## Error Scenarios Supported
|
|
89
|
+
|
|
90
|
+
The mock servers support error injection via `?error_mode=<mode>` parameter:
|
|
91
|
+
|
|
92
|
+
### Invalid Statement
|
|
93
|
+
```
|
|
94
|
+
GET /.well-known/openid-federation?error_mode=invalid_statement
|
|
95
|
+
```
|
|
96
|
+
Returns malformed JWT to test error handling.
|
|
97
|
+
|
|
98
|
+
### Wrong Keys
|
|
99
|
+
```
|
|
100
|
+
GET /.well-known/jwks.json?error_mode=wrong_keys
|
|
101
|
+
GET /.well-known/openid-federation?error_mode=wrong_keys
|
|
102
|
+
```
|
|
103
|
+
Returns JWKS with keys that don't match the signing key.
|
|
104
|
+
|
|
105
|
+
### Invalid Request
|
|
106
|
+
```
|
|
107
|
+
GET /auth?error_mode=invalid_request
|
|
108
|
+
```
|
|
109
|
+
Rejects request object validation to test error handling.
|
|
110
|
+
|
|
111
|
+
### Invalid Signature
|
|
112
|
+
```
|
|
113
|
+
GET /.well-known/signed-jwks.json?error_mode=invalid_signature
|
|
114
|
+
```
|
|
115
|
+
Returns signed JWKS with invalid signature.
|
|
116
|
+
|
|
117
|
+
### Expired Statement
|
|
118
|
+
```
|
|
119
|
+
GET /.well-known/openid-federation?error_mode=expired_statement
|
|
120
|
+
```
|
|
121
|
+
Returns expired entity statement to test expiration handling.
|
|
122
|
+
|
|
123
|
+
### Missing Metadata
|
|
124
|
+
```
|
|
125
|
+
GET /.well-known/openid-federation?error_mode=missing_metadata
|
|
126
|
+
```
|
|
127
|
+
Returns entity statement without metadata to test validation.
|
|
128
|
+
|
|
129
|
+
## Usage
|
|
130
|
+
|
|
131
|
+
### Quick Start (Automated - Recommended)
|
|
132
|
+
|
|
133
|
+
The integration test flow can automatically start servers, generate keys, and run all tests:
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
# Single command - fully automated
|
|
137
|
+
ruby examples/integration_test_flow.rb
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
This will:
|
|
141
|
+
1. Create temporary directories for keys and configs
|
|
142
|
+
2. Generate RSA keys for both OP and RP (using rake task logic)
|
|
143
|
+
3. Configure and start both servers automatically
|
|
144
|
+
4. Wait for servers to be ready
|
|
145
|
+
5. Run all integration tests
|
|
146
|
+
6. Clean up (kill servers, remove tmp dirs) on exit
|
|
147
|
+
|
|
148
|
+
### Manual Start (For Debugging)
|
|
149
|
+
|
|
150
|
+
If you want to start servers manually for debugging:
|
|
151
|
+
|
|
152
|
+
**Terminal 1 - OP Server:**
|
|
153
|
+
```bash
|
|
154
|
+
ruby examples/mock_op_server.rb
|
|
155
|
+
# Server runs on http://localhost:9292
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
**Terminal 2 - RP Server:**
|
|
159
|
+
```bash
|
|
160
|
+
ruby examples/mock_rp_server.rb
|
|
161
|
+
# Server runs on http://localhost:9293
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
**Terminal 3 - Integration Tests:**
|
|
165
|
+
```bash
|
|
166
|
+
# Disable auto-start to use manually started servers
|
|
167
|
+
AUTO_START_SERVERS=false ruby examples/integration_test_flow.rb
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Environment Variables
|
|
171
|
+
|
|
172
|
+
The integration test flow supports the following environment variables:
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
# Server URLs
|
|
176
|
+
OP_URL=http://localhost:9292 # OP server URL
|
|
177
|
+
RP_URL=http://localhost:9293 # RP server URL
|
|
178
|
+
|
|
179
|
+
# Server Ports
|
|
180
|
+
OP_PORT=9292 # OP server port
|
|
181
|
+
RP_PORT=9293 # RP server port
|
|
182
|
+
|
|
183
|
+
# Entity IDs
|
|
184
|
+
OP_ENTITY_ID=https://op.example.com # OP entity identifier
|
|
185
|
+
RP_ENTITY_ID=https://rp.example.com # RP entity identifier
|
|
186
|
+
|
|
187
|
+
# Temporary Directory
|
|
188
|
+
TMP_DIR=tmp/integration_test # Directory for keys/configs (default: tmp/integration_test)
|
|
189
|
+
|
|
190
|
+
# Auto-start servers (true/false)
|
|
191
|
+
AUTO_START_SERVERS=true # Auto-start servers (default: true)
|
|
192
|
+
|
|
193
|
+
# Cleanup on exit (true/false)
|
|
194
|
+
CLEANUP_ON_EXIT=true # Clean up tmp dirs on exit (default: true)
|
|
195
|
+
|
|
196
|
+
# Key Type
|
|
197
|
+
KEY_TYPE=separate # 'single' or 'separate' (default: separate)
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
**Example with custom configuration:**
|
|
201
|
+
```bash
|
|
202
|
+
KEY_TYPE=separate \
|
|
203
|
+
TMP_DIR=/tmp/my_test \
|
|
204
|
+
ruby examples/integration_test_flow.rb
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
**Note**: By default, the integration test uses localhost URLs for complete isolation:
|
|
208
|
+
- No DNS resolution required
|
|
209
|
+
- No external network dependencies
|
|
210
|
+
- All communication happens on localhost
|
|
211
|
+
- Entity IDs default to `http://localhost:9292` (OP) and `http://localhost:9293` (RP)
|
|
212
|
+
|
|
213
|
+
This ensures the tests work in any environment without network configuration.
|
|
214
|
+
|
|
215
|
+
### Manual Testing
|
|
216
|
+
|
|
217
|
+
**1. Test Provider Entity Statement:**
|
|
218
|
+
```bash
|
|
219
|
+
curl http://localhost:9292/.well-known/openid-federation
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
**2. Test Client Entity Statement:**
|
|
223
|
+
```bash
|
|
224
|
+
curl http://localhost:9293/.well-known/openid-federation
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
**3. Test Login Flow:**
|
|
228
|
+
```bash
|
|
229
|
+
# Initiate login (will redirect to OP)
|
|
230
|
+
curl -L "http://localhost:9293/login?provider=https://op.example.com"
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
**4. Test Error Scenarios:**
|
|
234
|
+
```bash
|
|
235
|
+
# Invalid statement
|
|
236
|
+
curl "http://localhost:9292/.well-known/openid-federation?error_mode=invalid_statement"
|
|
237
|
+
|
|
238
|
+
# Wrong keys
|
|
239
|
+
curl "http://localhost:9292/.well-known/jwks.json?error_mode=wrong_keys"
|
|
240
|
+
|
|
241
|
+
# Expired statement
|
|
242
|
+
curl "http://localhost:9292/.well-known/openid-federation?error_mode=expired_statement"
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
## Configuration
|
|
246
|
+
|
|
247
|
+
### OP Server Configuration
|
|
248
|
+
|
|
249
|
+
Create `examples/config/mock_op.yml`:
|
|
250
|
+
|
|
251
|
+
```yaml
|
|
252
|
+
entity_id: "https://op.example.com"
|
|
253
|
+
server_host: "localhost:9292"
|
|
254
|
+
signing_key: |
|
|
255
|
+
-----BEGIN RSA PRIVATE KEY-----
|
|
256
|
+
...
|
|
257
|
+
-----END RSA PRIVATE KEY-----
|
|
258
|
+
encryption_key: | # Optional, defaults to signing_key
|
|
259
|
+
-----BEGIN RSA PRIVATE KEY-----
|
|
260
|
+
...
|
|
261
|
+
-----END RSA PRIVATE KEY-----
|
|
262
|
+
trust_anchors:
|
|
263
|
+
- entity_id: "https://ta.example.com"
|
|
264
|
+
jwks:
|
|
265
|
+
keys:
|
|
266
|
+
- kty: "RSA"
|
|
267
|
+
kid: "ta-key-1"
|
|
268
|
+
use: "sig"
|
|
269
|
+
n: "..."
|
|
270
|
+
e: "AQAB"
|
|
271
|
+
authority_hints: # Optional, if OP is subordinate
|
|
272
|
+
- "https://ta.example.com"
|
|
273
|
+
op_metadata:
|
|
274
|
+
issuer: "https://op.example.com"
|
|
275
|
+
authorization_endpoint: "https://op.example.com/auth"
|
|
276
|
+
token_endpoint: "https://op.example.com/token"
|
|
277
|
+
request_object_encryption_alg_values_supported: ["RSA-OAEP"]
|
|
278
|
+
request_object_encryption_enc_values_supported: ["A128CBC-HS256"]
|
|
279
|
+
require_request_encryption: false # Set to true to require encryption
|
|
280
|
+
validate_request_objects: true # Set to false to skip validation
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
### RP Server Configuration
|
|
284
|
+
|
|
285
|
+
Create `examples/config/mock_rp.yml`:
|
|
286
|
+
|
|
287
|
+
```yaml
|
|
288
|
+
entity_id: "https://rp.example.com"
|
|
289
|
+
server_host: "localhost:9293"
|
|
290
|
+
signing_key: |
|
|
291
|
+
-----BEGIN RSA PRIVATE KEY-----
|
|
292
|
+
...
|
|
293
|
+
-----END RSA PRIVATE KEY-----
|
|
294
|
+
encryption_key: | # Optional, for decrypting encrypted ID tokens
|
|
295
|
+
-----BEGIN RSA PRIVATE KEY-----
|
|
296
|
+
...
|
|
297
|
+
-----END RSA PRIVATE KEY-----
|
|
298
|
+
trust_anchors:
|
|
299
|
+
- entity_id: "https://ta.example.com"
|
|
300
|
+
jwks:
|
|
301
|
+
keys: [...]
|
|
302
|
+
authority_hints: # Optional, if RP is subordinate
|
|
303
|
+
- "https://ta.example.com"
|
|
304
|
+
redirect_uris:
|
|
305
|
+
- "https://rp.example.com/callback"
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
## Testing Scenarios
|
|
309
|
+
|
|
310
|
+
### Happy Path
|
|
311
|
+
|
|
312
|
+
1. Start both servers
|
|
313
|
+
2. Run integration tests: `ruby examples/integration_test_flow.rb`
|
|
314
|
+
3. All tests should pass
|
|
315
|
+
|
|
316
|
+
### Error Scenarios
|
|
317
|
+
|
|
318
|
+
1. Test invalid entity statement:
|
|
319
|
+
```bash
|
|
320
|
+
curl "http://localhost:9292/.well-known/openid-federation?error_mode=invalid_statement"
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
2. Test wrong keys:
|
|
324
|
+
```bash
|
|
325
|
+
curl "http://localhost:9292/.well-known/jwks.json?error_mode=wrong_keys"
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
3. Test expired statement:
|
|
329
|
+
```bash
|
|
330
|
+
curl "http://localhost:9292/.well-known/openid-federation?error_mode=expired_statement"
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
### Request Object Validation
|
|
334
|
+
|
|
335
|
+
1. Test with valid signed request object:
|
|
336
|
+
```bash
|
|
337
|
+
curl "http://localhost:9293/login?provider=https://op.example.com"
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
2. Test with invalid request object:
|
|
341
|
+
```bash
|
|
342
|
+
curl "http://localhost:9292/auth?error_mode=invalid_request&request=invalid.jwt"
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
### Trust Chain Resolution
|
|
346
|
+
|
|
347
|
+
1. Configure trust anchors in both servers
|
|
348
|
+
2. Ensure RP has authority_hints pointing to trust anchor
|
|
349
|
+
3. OP will resolve RP's trust chain during authorization
|
|
350
|
+
4. OP will apply metadata policies from trust chain
|
|
351
|
+
|
|
352
|
+
## Security Testing
|
|
353
|
+
|
|
354
|
+
The mock servers support comprehensive security testing:
|
|
355
|
+
|
|
356
|
+
- **Algorithm Confusion**: Test rejection of non-RS256 algorithms
|
|
357
|
+
- **Key Confusion**: Test rejection of wrong keys
|
|
358
|
+
- **Signature Verification**: Test rejection of tampered signatures
|
|
359
|
+
- **Path Traversal**: Test rejection of malicious paths
|
|
360
|
+
- **Replay Attacks**: Test nonce validation
|
|
361
|
+
- **Request Object Validation**: Test signature and encryption validation
|
|
362
|
+
- **Trust Chain Validation**: Test authority hints and metadata policy enforcement
|
|
363
|
+
|
|
364
|
+
## Troubleshooting
|
|
365
|
+
|
|
366
|
+
### Server Won't Start
|
|
367
|
+
|
|
368
|
+
- Check if ports 9292 (OP) and 9293 (RP) are available
|
|
369
|
+
- Verify all dependencies are installed: `bundle install`
|
|
370
|
+
- Check configuration files exist and are valid YAML
|
|
371
|
+
|
|
372
|
+
### Entity Statements Not Validating
|
|
373
|
+
|
|
374
|
+
- Verify keys are correctly configured
|
|
375
|
+
- Check entity IDs match between servers
|
|
376
|
+
- Ensure trust anchors are configured if using trust chains
|
|
377
|
+
|
|
378
|
+
### Request Objects Rejected
|
|
379
|
+
|
|
380
|
+
- Verify RP's entity statement is accessible
|
|
381
|
+
- Check RP's JWKS contains the signing key
|
|
382
|
+
- Ensure request object is properly signed
|
|
383
|
+
- Verify encryption if required by OP
|
|
384
|
+
|
|
385
|
+
### Trust Chain Resolution Fails
|
|
386
|
+
|
|
387
|
+
- Verify trust anchors are configured
|
|
388
|
+
- Check authority_hints are correct
|
|
389
|
+
- Ensure all entity statements in chain are valid
|
|
390
|
+
- Verify subordinate statements are available
|
|
391
|
+
|
|
392
|
+
## Next Steps
|
|
393
|
+
|
|
394
|
+
1. **Add More Error Scenarios**: Extend error injection modes
|
|
395
|
+
2. **Performance Testing**: Add load testing scenarios
|
|
396
|
+
3. **Security Testing**: Add fuzzing and penetration tests
|
|
397
|
+
4. **Trust Mark Testing**: Add trust mark validation
|
|
398
|
+
5. **Metadata Policy Testing**: Add comprehensive policy merging tests
|
|
399
|
+
|