# OAuth/OIDC/SAML + Advanced JWT/JWK Security

> Advanced authentication protocol bypass, token confusion, redirect security, JWK/kid injection

## Core Risks
- Redirect/callback: `redirect_uri`/`post_logout_redirect_uri` validation missing or weak -> open redirect, code theft
- State correlation: `state`/`nonce` missing or predictable -> CSRF/replay/confusion attacks
- PKCE missing -> authorization code interception/replay
- Token confusion: `aud/iss` not validated or accepting mismatched tokens -> cross-client/cross-resource abuse
- JWT/JWK: `alg=none`, HS/RS confusion, `kid/jku/jwk` externally controllable leading to SSRF/arbitrary key import
- SAML: Signature only verifies assertion not response, `Recipient`/`Audience` not validated, `KeyInfo` controllable
- Refresh/device codes: Refresh tokens not bound to client/device, device code polling unlimited -> brute-force/credential stuffing

## Dangerous Patterns
```js
// Weak redirect_uri validation
if (redirectUri.includes('example.com')) { // Substring matching is bypassable
  return redirectUri;
}

// JWT without iss/aud/alg validation
const payload = jwt.verify(token, secret); // algorithms not restricted

// kid injection (HS/RS confusion)
jwt.verify(token, publicKey, { algorithms: ['HS256','RS256'] });
// Attacker provides HS256 + kid=publicKey, can forge tokens
```

```xml
<!-- SAML only validates assertion signature -->
<ds:Signature>...</ds:Signature> <!-- Response not signed, can wrap attacker assertion -->
```

## Detection Checklist
- [ ] `redirect_uri`/`post_logout_redirect_uri` strict whitelist with exact/prefix matching and disallow `@`, `//`, `%0d%0a`
- [ ] `state`/`nonce` is random and validated on return; response type mix-up protection (client validates `response_type`/`client_id`)
- [ ] PKCE enforced (`S256`), `code_verifier` has sufficient length
- [ ] JWT validates fixed `alg`, mandatory `iss`/`aud`/`exp`/`iat`/`nbf`, disallows `none`
- [ ] `kid/jku/jwk`: Only allow trusted domains/built-in JWK sets; disallow HTTP; cache/pin public keys
- [ ] `jwks_uri`/`discovery` only trusts https + allowlist; disable client-supplied `jku`
- [ ] SAML: Response and Assertion dual-signed or at least validate Response; verify `Recipient`/`Audience`/`InResponseTo`
- [ ] Refresh tokens bound to client/device; rotate refresh tokens; device code polling rate-limited/code expiry enforced
- [ ] Logout callback whitelisted; validate `origin` in SPA scenarios
- [ ] SAML dual signature (Response + Assertion) or at least Response; ACS URL strict match; Issuer/Audience/Recipient validation
- [ ] Reverse proxy/load balancer: Do not accept client-supplied Host/Proto/Port; consistent redirect_uri validation between frontend and backend

## Detection Commands
```bash
# Find redirect_uri/state/nonce
rg -n "redirect_uri|post_logout_redirect_uri|state|nonce" --glob "*.{js,ts,go,py,java,cs,rb,php}"

# JWT verification configuration
rg -n "jwt\\.decode|jwt\\.verify|JwtParser|Jwts\\.parser" --glob "*.{js,ts,java,py,go,cs,rb,php}"
rg -n "HS256|RS256|algorithms" --glob "*.{js,ts,java,py,go,cs,rb,php}"

# JWK/kid/jku
rg -n "jwk|jku|kid" --glob "*.{js,ts,java,py,go,cs,rb,php,yml,yaml,properties}"

# SAML
rg -n "SAML|AssertionConsumerService|Audience|Recipient|InResponseTo" --glob "*.{xml,java,cs,rb,py}"
```

## Security Baseline
- Discovery documents/config: Check if OIDC discovery (`.well-known/openid-configuration`) allows external jku/jwks_uri
- Token validation: Fixed algorithms; strict validation of `iss/aud/azp`; enable `requireSignedTokens`
- Public key source: Built-in/pinned JWKS; allowlist for `kid`; disallow HTTP/internal addresses
- Redirects: Whitelist matching + URL parse before comparison; reject arbitrary subdomain/port/protocol confusion
- Flow protection: PKCE enforced; `state`/`nonce` stored and compared; limit `response_mode=form_post` to prevent interception
- SAML: Response/Assertion dual signatures; validate `Recipient/Audience/InResponseTo/NotBefore/NotOnOrAfter`
- Refresh/device codes: Rotate refresh tokens, bind to client; device code polling rate-limited/code expiry

## Verification Steps
- Forge `redirect_uri=https://attacker.com%2f..%2f@legit.com` to see if it is accepted
- Send authorization code flow without/duplicate `state`/`nonce`, confirm if rejected
- JWT with `alg=none` or HS256+public key as secret to see if it passes validation
- Provide external `jku` pointing to attacker's JWKS, see if server fetches it
- SAML Response unsigned or `Recipient` not matching ACS to see if accepted

## Minimal PoC Examples
- kid/jku injection:
```bash
header='{"alg":"HS256","kid":"attacker","jku":"https://attacker.test/jwks.json"}'
payload='{"sub":"admin","iss":"victim","aud":"api","exp":9999999999}'
token="$(echo -n "$header" | base64 -w0).$(echo -n "$payload" | base64 -w0).sig"
curl -H "Authorization: Bearer $token" https://victim/api/me
```
- redirect_uri bypass:
```bash
curl -I "https://auth.example.com/authorize?client_id=web&response_type=code&redirect_uri=https://attacker.com%2f%40example.com/cb&state=1"
```

## Configuration Example (Secure)
```yaml
# OIDC discovery (production)
issuer: https://idp.example.com
jwks_uri: https://idp.example.com/.well-known/jwks.json
redirect_uris:
  - https://app.example.com/callback
post_logout_redirect_uris:
  - https://app.example.com/logout-complete
require_pkce: true
```

```xml
<!-- SAML: Response + Assertion signature -->
<md:IDPSSODescriptor>
  <md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
                          Location="https://idp.example.com/sso"/>
  <md:KeyDescriptor use="signing">...</md:KeyDescriptor>
</md:IDPSSODescriptor>
```
