ASVS V2 Audit: Authentication¶
Audit Date: 2026-02-28 RAPS Version: 4.14.0 ASVS Version: 4.0.3
Scope¶
Audit of the RAPS authentication subsystem covering:
- PKCE implementation (raps-kernel/src/auth/device_code.rs)
- 3-legged OAuth authorization code flow (raps-kernel/src/auth/three_leg.rs)
- 2-legged OAuth client credentials flow (raps-kernel/src/auth/two_leg.rs)
- Token lifecycle management (raps-kernel/src/auth/token_ops.rs)
- Token storage backends (raps-kernel/src/storage.rs)
- Auth client structure (raps-kernel/src/auth/mod.rs, raps-kernel/src/auth/types.rs)
Findings¶
V2.1.1 - PKCE Code Verifier Uses Cryptographically Secure Random Source¶
- Status: Met
- Evidence:
raps-kernel/src/auth/device_code.rs:25-33 - Details: The
generate_code_verifier()function usesrand::thread_rng()which delegates to the OS CSPRNG (getrandomon Linux/macOS,BCryptGenRandomon Windows). The verifier is 128 characters drawn from the RFC 7636 section 4.1 unreserved character set ([A-Za-z0-9\-._~]). The implementation is well within the RFC-mandated 43-128 character range. Unit tests verify length, charset, and uniqueness across invocations.
V2.1.2 - PKCE S256 Challenge Derivation¶
- Status: Met
- Evidence:
raps-kernel/src/auth/device_code.rs:37-39 - Details: The code challenge is derived using SHA-256 (
sha2::Sha256) and Base64-URL-encoded without padding (URL_SAFE_NO_PAD), matching RFC 7636 section 4.2 exactly. A known test vector from RFC 7636 Appendix B is verified at line 253-258.
V2.1.3 - State Parameter Validates CSRF on Callback (Device/PKCE Flow)¶
- Status: Met
- Evidence:
raps-kernel/src/auth/device_code.rs:57(generation),raps-kernel/src/auth/device_code.rs:117-123(validation) - Details: A UUID v4 state parameter is generated per login attempt. When the user pastes back a full callback URL, the state parameter is extracted and compared against the original. On mismatch, the flow is aborted with "State mismatch -- possible CSRF attack." If the user pastes a bare authorization code (no URL), the state check is necessarily skipped since there is no callback URL to parse.
V2.1.4 - State Parameter Validates CSRF on Callback (Browser Flow)¶
- Status: Met
- Evidence:
raps-kernel/src/auth/three_leg.rs:80(generation),raps-kernel/src/auth/three_leg.rs:196-203(validation) - Details: The browser-based 3-legged OAuth flow generates a UUID v4 state and includes it in the authorization URL. When the local HTTP server receives the callback, the returned state is validated against the original. On mismatch, a 400 response is returned to the browser and the flow is aborted with "State mismatch - possible CSRF attack". This is robust because the state never leaves the process memory.
V2.1.5 - Token Refresh Uses Proper Concurrency Control¶
- Status: Met
- Evidence:
raps-kernel/src/auth/mod.rs:34(Mutex declaration),raps-kernel/src/auth/three_leg.rs:20-58(refresh coordination) - Details: The 3-legged token cache is protected by
tokio::sync::Mutex, and arefreshingboolean flag prevents concurrent refresh requests. When one task is refreshing, other callers yield (sleep 100ms + loop). On success or failure, therefreshingflag is always reset. The 2-legged token cache usestokio::sync::RwLockwith multiple-reader/single-writer semantics. Note: There is a theoretical TOCTOU window between checkingcache.refreshingand setting it, but since both operations occur within the samelock().awaitscope, this is safe under Tokio's single-lock guarantee.
V2.1.6 - Token Expiry Check Includes Safety Margin¶
- Status: Met
- Evidence:
raps-kernel/src/auth/types.rs:56-58 - Details: The
CachedToken::is_valid()method for 2-legged tokens checksexpires_at > Instant::now() + Duration::from_secs(60), providing a 60-second safety margin to avoid race conditions with near-expired tokens.
V2.1.7 - File-Based Token Storage Sets Restrictive Permissions¶
- Status: Met
- Evidence:
raps-kernel/src/storage.rs:158-163 - Details: On Unix systems, after writing the token file, permissions are set to
0o600(read/write for owner only) usingstd::os::unix::fs::PermissionsExt. The directory creation (create_dir_all) does not explicitly set restrictive permissions on the parent directory, relying on the user's umask.
V2.1.8 - Default Token Storage Uses OS Keychain¶
- Status: Met
- Evidence:
raps-kernel/src/storage.rs:23-51 - Details: The
StorageBackend::from_env()defaults toStorageBackend::Keychain, which uses thekeyringcrate (Windows Credential Manager, macOS Keychain, Linux Secret Service). File-based storage is only enabled when explicitly configured via profile (use_keychain: false) or environment variable (RAPS_USE_FILE_STORAGE=true). When file storage is active, a warning is logged. The keychain backend gracefully falls back to file storage when the keyring is unavailable (e.g., headless CI/CD servers).
V2.1.9 - Credentials Sent via HTTP Basic Auth¶
- Status: Met
- Evidence:
raps-kernel/src/auth/device_code.rs:185,raps-kernel/src/auth/three_leg.rs:261,raps-kernel/src/auth/two_leg.rs:58 - Details: All token exchange and refresh requests send
client_idandclient_secretvia HTTP Basic Authentication (.basic_auth()), not as form body parameters. This is the recommended OAuth 2.0 approach per RFC 6749 section 2.3.1.
V2.1.10 - No Tokens Appear in Logs¶
- Status: Partial
- Evidence:
raps-kernel/src/logging.rs:141-163(redaction),raps-kernel/src/auth/three_leg.rs:165(debug log) - Details: The
redact_secrets()function redacts patterns matchingclient_secret,secret_key,api_key,token,access_token,refresh_token, andbearerwith values >= 20 characters. However, this redaction function must be explicitly called by the caller -- it is not automatically applied to all tracing output. The tracing framework logs HTTP URLs and status codes at debug level (e.g.,raps-kernel/src/http.rs:148-152), which could include tokens in URL query parameters if they were ever placed there (they are not in the current code, but this is not structurally prevented). Auth code exchange error messages atraps-kernel/src/auth/device_code.rs:195include the full error text from the server, which could potentially contain token fragments.
V2.1.11 - Credential Validation Before Use¶
- Status: Met
- Evidence:
raps-kernel/src/auth/device_code.rs:50,raps-kernel/src/auth/three_leg.rs:78,raps-kernel/src/auth/two_leg.rs:42 - Details: All auth flows call
self.config.require_credentials()at the start, which validates thatclient_idandclient_secretare non-empty before attempting any network requests.
V2.1.12 - Login-with-Token Validates Token¶
- Status: Met
- Evidence:
raps-kernel/src/auth/token_ops.rs:58-59 - Details: The
login_with_token()method (for CI/CD scenarios) validates the provided access token by calling the/userinfoendpoint before storing it. Invalid tokens are rejected with an error.
Summary¶
| Requirement | Status | Evidence |
|---|---|---|
| PKCE code_verifier uses CSPRNG | Met | device_code.rs:25-33 |
| PKCE S256 challenge derivation | Met | device_code.rs:37-39 |
| State parameter CSRF (device flow) | Met | device_code.rs:57,117-123 |
| State parameter CSRF (browser flow) | Met | three_leg.rs:80,196-203 |
| Token refresh concurrency control | Met | three_leg.rs:20-58, mod.rs:34 |
| Token expiry safety margin | Met | types.rs:56-58 |
| File storage restrictive permissions | Met | storage.rs:158-163 |
| Default to OS keychain | Met | storage.rs:23-51 |
| Credentials via HTTP Basic Auth | Met | device_code.rs:185, three_leg.rs:261, two_leg.rs:58 |
| No tokens in logs | Partial | logging.rs:141-163 |
| Credential validation before use | Met | device_code.rs:50, three_leg.rs:78, two_leg.rs:42 |
| Login-with-token validates token | Met | token_ops.rs:58-59 |
Recommendations¶
-
Automatic log redaction layer: Consider implementing a custom
tracinglayer that automatically appliesredact_secrets()to all log events before they reach any subscriber (file or stderr). This would provide defense-in-depth against accidental token leakage in log output, removing reliance on individual call sites to invoke the redaction function. -
Parent directory permissions: When creating the token storage directory (
create_dir_all), explicitly set directory permissions to0o700on Unix systems rather than relying on the user's umask. -
Bare authorization code bypass: In the PKCE device flow, when a user pastes a bare authorization code (not a URL), the CSRF state check is skipped. Consider documenting this tradeoff or requiring the full URL to maintain CSRF protection.
-
Error message sanitization: Error messages from token exchange failures (
device_code.rs:195,three_leg.rs:271) include raw server response text. Consider redacting these before display to prevent accidental exposure of sensitive data in error responses.