RFC Compliance Matrix
All project documents use RFC 2119 key words ("MUST", "SHOULD", "MAY", etc.) as defined in RFC 2119.
This document maps the luci-sso implementation to the relevant OIDC and OAuth2 standards. Security auditors can verify these claims by inspecting the modules listed in the Audit trail section.
Standards covered
Authorization Code Flow (OIDC Core §3.1 / RFC 6749 §4.1)
| Requirement |
Reference |
Status |
Notes |
| Authorization Code Grant |
RFC 6749 §4.1 |
✅ Implemented |
Only supported grant type. |
| Implicit Grant |
RFC 6749 §4.2 |
❌ Not implemented |
Intentionally omitted — tokens in redirect URLs are deprecated as insecure. |
Authorization request: response_type=code |
OIDC Core §3.1.2.1 |
✅ Implemented |
|
Authorization request: scope=openid |
OIDC Core §3.1.2.1 |
✅ Implemented |
Additional scopes (profile, email, groups) are configurable via UCI. |
Authorization request: state parameter |
RFC 6749 §4.1.1, §10.12 |
✅ Implemented |
Random value, constant-time verified at callback. |
Authorization request: nonce parameter |
OIDC Core §3.1.2.1 |
✅ Implemented |
Required. Constant-time verified against ID Token claim. |
| Token request: back-channel exchange |
OIDC Core §3.1.3.1 |
✅ Implemented |
HTTPS enforced. |
Token response: id_token required |
OIDC Core §3.1.3.3 |
✅ Implemented |
Missing id_token triggers MISSING_ID_TOKEN. |
Token error: invalid_grant handling |
OIDC Core §3.1.3.4 |
✅ Implemented |
Logged as OIDC_INVALID_GRANT. |
| Refresh tokens |
OIDC Core §12 |
❌ Not implemented |
Sessions expire after one hour; re-authentication is required. By design — see About the Session Lifecycle. |
| UserInfo endpoint (fallback) |
OIDC Core §5.3 |
✅ Implemented |
Fetched when email claim is absent from the ID Token. |
| RP-Initiated Logout |
OIDC Session Management |
✅ Implemented |
Browser redirected to end_session_endpoint if advertised by the IdP. |
OIDC Discovery (OIDC Discovery 1.0 §4)
| Requirement |
Reference |
Status |
Notes |
Discovery document fetch from <issuer>/.well-known/openid-configuration |
Discovery §4 |
✅ Implemented |
Cached in /var/run/luci-sso/ (tmpfs) for 24 hours. |
issuer field validation |
Discovery §4.3 |
✅ Implemented |
Must exactly match issuer_url. Triggers DISCOVERY_ISSUER_MISMATCH on mismatch. |
authorization_endpoint required |
Discovery §3 |
✅ Implemented |
Missing field triggers DISCOVERY_MISSING_ENDPOINT. |
token_endpoint required |
Discovery §3 |
✅ Implemented |
Missing field triggers DISCOVERY_MISSING_ENDPOINT. |
jwks_uri required |
Discovery §3 |
✅ Implemented |
Missing field triggers DISCOVERY_MISSING_ENDPOINT. |
| All endpoints must use HTTPS |
Discovery §4.2 |
✅ Implemented |
HTTP endpoints trigger INSECURE_ENDPOINT. |
issuer in discovery must match fetch URL |
Discovery §4.3 |
⚠️ Intentional deviation |
When internal_issuer_url is set (split-horizon), the discovery document is fetched from the internal address but issuer is validated against the public issuer_url. See How to Configure Split-Horizon Networking. |
ID Token Validation (OIDC Core §3.1.3.7 / RFC 7519)
| Requirement |
Reference |
Status |
Notes |
iss claim validation |
OIDC Core §3.1.3.7 (2) |
✅ Implemented |
Must exactly match issuer_url. |
aud claim validation |
OIDC Core §3.1.3.7 (3) |
✅ Implemented |
Must include client_id. |
exp claim validation |
RFC 7519 §4.1.4 |
✅ Implemented |
Clock skew tolerance applied via clock_tolerance UCI option. |
iat claim validation |
OIDC Core §3.1.3.7 (9) |
✅ Implemented |
Rejected if too far in the past or future. |
sub claim required |
OIDC Core §3.1.3.7 (2) |
✅ Implemented |
Missing sub triggers MISSING_SUB_CLAIM. |
nonce claim validation |
OIDC Core §3.1.3.7 (11) |
✅ Implemented |
Constant-time comparison against stored nonce. |
at_hash validation |
OIDC Core §3.1.3.7 (9) |
✅ Implemented |
Mandatory. Binds the access token to the ID Token. Missing at_hash triggers MISSING_AT_HASH. |
PKCE (RFC 7636)
| Requirement |
Reference |
Status |
Notes |
| Code verifier generation |
RFC 7636 §4.1 |
✅ Implemented |
CSPRNG, 43–128 characters of Base64URL-safe alphabet. |
Code challenge method: S256 |
RFC 7636 §4.2 |
✅ Implemented |
Only S256 is accepted. |
Code challenge method: plain |
RFC 7636 §4.2 |
❌ Rejected |
Intentionally not supported — plain provides no security benefit over omitting PKCE entirely. |
| Code challenge sent with authorization request |
RFC 7636 §4.3 |
✅ Implemented |
|
| Code verifier sent with token request |
RFC 7636 §4.5 |
✅ Implemented |
|
Algorithms (RFC 7518 / RFC 7519)
| Algorithm |
Status |
Notes |
| RS256 (RSA + SHA-256) |
✅ Accepted |
Minimum 2048-bit keys enforced in the native C bridge. |
| ES256 (ECDSA + P-256 + SHA-256) |
✅ Accepted |
EC coordinate validation performed before use. |
| HS256 (HMAC-SHA-256) |
❌ Rejected |
Intentionally blocked — symmetric algorithms are vulnerable to Algorithm Confusion attacks. See Security Model. |
alg: none |
❌ Rejected |
Unsigned tokens are never accepted. |
Intentional deviations
| Deviation |
Rationale |
Split-horizon issuer URL — When internal_issuer_url is set, back-channel requests use a different origin than issuer_url. OIDC Discovery §4.3 requires the fetch URL to match the issuer identifier. |
Self-hosted deployments commonly cannot route the router's back-channel traffic through the IdP's public DNS name. Requiring a match would break most home lab configurations. The iss claim is still validated against the public issuer_url, preserving the security property that matters. |
| Refresh tokens not supported — OIDC Core §12 defines the Refresh Token flow. |
The router has no persistent token store. Sessions are bounded to one hour; users re-authenticate on expiry. This avoids the need to store and protect long-lived refresh tokens on an embedded device. |
| Implicit flow not supported — RFC 6749 §4.2 defines the Implicit Grant. |
The Implicit flow places tokens in redirect URLs, which are logged by browsers, proxies, and servers. It is deprecated by the OAuth 2.0 Security Best Current Practice (RFC 9700). |
plain PKCE method rejected — RFC 7636 §4.2 defines both plain and S256. |
plain sends the verifier as the challenge, providing no protection against an attacker who can observe the authorization request. S256 is strictly superior when available. |
Audit trail
Verify the claims in this document by inspecting these source files:
| Area |
Source |
| Authorization Code Flow, token exchange, session injection |
files/usr/share/ucode/luci_sso/handshake.uc |
| ID Token validation, nonce, at_hash, iss, aud, exp |
files/usr/share/ucode/luci_sso/oidc.uc |
| Discovery fetch, cache, issuer mismatch detection |
files/usr/share/ucode/luci_sso/discovery.uc |
| PKCE generation (S256), constant-time comparisons |
files/usr/share/ucode/luci_sso/crypto.uc |
| Algorithm enforcement, JWK parsing, signature verification |
src/native_common.c, src/native_mbedtls.c |
HTTPS enforcement (is_https()) |
files/usr/share/ucode/luci_sso/encoding.uc |