Safeguard
Application Security•

ASVS 5.0 V10.4.10: Authenticate Confidential Clients on Back Channels

Token, PAR and revocation requests from a confidential client must carry client authentication. Configure it, keep secrets out of repos, and test the rejection.

Safeguard Security Team
4 min read

ASVS 5.0 V10.4.10 (Level 2) says that when a confidential client talks directly to the authorization server, the server must authenticate it. That covers token requests, pushed authorization requests (PAR) and token revocation requests.

A confidential client is a server side application that can keep a credential private. If it is registered as confidential, the authorization server should never accept an unauthenticated call from it.

Why it matters

Without client authentication, anyone who obtains an authorization code or refresh token can redeem it without being the client it was issued to. Client authentication also stops outsiders from revoking another client's tokens or flooding your PAR endpoint with garbage.

Authentication proves which client is calling. It does not replace PKCE or single use codes, which cover different failures.

How to implement it

Pick a method per client in the registration:

  • client_secret_basic: HTTP Basic with the client ID and secret. Fine for L2 when the secret is strong and stored well.
  • private_key_jwt: the client signs a short assertion with its private key. Stronger, and required at L3 (see V10.4.16).
  • tls_client_auth: mutual TLS.

Server side, check authentication before any other processing, and use constant time comparison.

import hmac, hashlib

def authenticate_client(client, presented_secret):
    if client.auth_method == "none":
        raise InvalidClient()  # confidential clients must authenticate
    digest = hashlib.sha256(presented_secret.encode()).hexdigest()
    if not hmac.compare_digest(digest, client.secret_sha256):
        raise InvalidClient()

Apply the same check to /token, /par and /revoke. Return 401 with invalid_client on failure.

Keep client secrets out of source control. They belong in a secret manager and in environment variables injected at deploy time. Rotate them when staff leave or a leak is suspected.

Secrets in repos are a very common way these credentials escape. Safeguard has a secrets scanning demo that shows how that kind of leak is surfaced:

Click through it at your own pace, or open it full screen.

Rotating and storing secrets

Treat each client secret like a password for a service account. Generate it with at least 32 random bytes, store only a hash on the server, and show it once at creation. Offer a rotation flow where the old and new secrets work side by side for a short overlap, so deployments do not break.

If you can, move confidential clients to private_key_jwt over time. It removes the shared secret from the picture, which is what V10.4.16 asks for at Level 3.

How to test it

Using curl against a confidential test client, run each of these against the token, PAR and revocation endpoints:

  1. No credentials. Expect 401.
  2. Wrong secret. Expect 401.
  3. Correct secret. Expect success.
  4. Another client's valid credentials presenting this client's code. Expect invalid_grant.

Also search your repositories and CI logs for the secret value.

Automating the check

A shell loop against a staging server covers all three endpoints:

for ep in token par revoke; do
  code=$(curl -s -o /dev/null -w "%{http_code}" -X POST "https://auth.example.com/$ep" -d "client_id=orders-backend&token=x")
  echo "$ep without credentials: $code"   # expect 401 for each
done

Run the same loop with a wrong secret and with the right one, and assert the expected codes. Add a scheduled search for the secret value in your repositories and build logs.

Common mistakes

  • Forgetting the revocation and PAR endpoints while protecting the token endpoint.
  • Registering a server side app as a public client for convenience.
  • Using one shared secret across environments.
  • Logging the Authorization header.

Quick check

Which three kinds of requests must be authenticated?

Token requests, pushed authorization requests and token revocation requests.

Does client authentication replace PKCE?

No. They protect against different attacks and are used together.

Sources

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.