OAuth 2.0 client authentication

The Connect2id server supports all standard methods for authenticating clients at the token endpoint, specified in the core OAuth 2.0 spec and various extensions. Each method has its own security properties. This guide will help you choose the most appropriate one for your client application. But first some basics.

1. Confidential vs public OAuth 2.0 clients

Confidential clients are those that have the capability to store secrets which cannot be accessed by the end-user or other parties. Traditional web applications with a backend server fall into this category.

Public clients on the other hand do not have this capability, so they cannot be reasonably authenticated at the token endpoint. Examples are native applications running on a user's device or computer, and browser based (JavaScript) applications.

The two client types are defined in the OAuth 2.0 framework spec (RFC 6749).

2. Why authenticate OAuth 2.0 clients?

Client authentication ensures the tokens get issued to a legitimate client and not some other, potentially malicious, party.

Public clients, which don't get authenticated, may for this reason only get issued with tokens that have a restricted scope.

3. The two types of client credential

The credential a client uses to authenticate falls into one of these two types:

  • Shared secret -- The Connect2id server issues the client with a secret (password) that is stored by the server as well as the client.

  • Private key -- The client generates a private RSA or EC key and stores it securely. The client then authenticates by signing an assertion (a JWT) with the private key, or by using the private key in a client X.509 certificate based authentication during the TLS handshake of the HTTPS connection.

4. Private key based authentication is more secure

Of the two methods the one based on a private key has stronger security properties.

  • A client can store the private key in a Hardware Security Module (HSM) or a Trusted Platform Module (TPM) where the signing is performed internally, within the module, and extraction of the stored keys is not possible. This can reduce the risk of client impersonation significantly.

  • The authorisation server doesn't keep any client-related credentials, limiting potential exposure.

  • The signed assertions (JWT) expire, limiting the time window for replay if they get accidentally leaked. Client certificates can also be made to expire quickly.

  • Finally, the issued tokens can be bound to the private key, enabling resource servers to require the client to prove its key possession when the token is submitted, thus upgrading the security of the OAuth 2.0 bearer tokens.

5. Supported client authentication methods

The Connect2id server supports the following standard methods for client authentication. Use their designations when you register a client to set the preferred method.

For an up-to-date list of the supported client authentication methods check the Connect2id server datasheet.

5.1 Shared secret based

5.1.1 client_secret_basic

This is essentially basic authentication, tweaked for OAuth 2.0 by applying additional "application/x-www-form-urlencoded" encoding to the client_id and client_secret. This is the simplest but also the least secure method.

The Connect2id server will automatically generate a secret for the client during registration. To let the client determine the secret use the custom preferred_client_secret registration parameter.

The client authentication is passed in the Authorization HTTP header.

POST /token HTTP/1.1
Host: c2id.com
Content-Type: application/x-www-form-urlencoded
Authorization: Basic czZCaGRSa3F0Mzo3RmpmcDBaQnIxS3REUmJuZlZkbUl3

grant_type=authorization_code
&code=n0esc3NRze7LTCu7iYzS6a5acc3f0ogp4

5.1.2 client_secret_post

Differs from client_secret_basic in that the credentials are passed in the request body as form parameters.

POST /token HTTP/1.1
Host: c2id.com
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&code=n0esc3NRze7LTCu7iYzS6a5acc3f0ogp4
&client_id=s6BhdRkqt3
&client_secret=7Fjfp0ZBr1KtDRbnfVdmIw

5.1.3 client_secret_jwt

Also uses a client secret, but this time the secret is used as a key to compute a hash-based message authentication code (HMAC) for a JWT assertion. Has stronger security properties than client_secret_basic and client_secret_post because the secret itself doesn't get transmitted to the server, reducing the damage if the token request gets accidentally sent over plain HTTP. The JWT expiration limits the time window for replay.

The JWT header must include the "alg" parameter to specify the HMAC algorithm. Other header parameters are not required.

Example JWT header:

{
  "alg" : "HS256"
}

The JWT contains the following claims:

  • iss and sub set to the client_id.
  • aud set to the token endpoint URL.
  • exp set the JWT expiration time.
  • jti set to a unique identifier for the JWT, used to prevent replay (optional).
  • nbf set to time before which the JWT must not be accepted for processing (optional).
  • iat set to the JWT issue time (optional).

Example JWT claims:

{
  "iss" : "oe7aiz60",
  "sub" : "oe7aiz60",
  "aud" : "https://demo.c2id.com/token",
  "exp" : 1453021544,
  "jti" : "Eefaevo0"
}

The client authenticates with client_secret_jwt by including the client_assertion_type parameter set to urn:ietf:params:oauth:client-assertion-type:jwt-bearer and the client_assertion parameter set to the serialised JWT.

POST /token HTTP/1.1
Host: c2id.com
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&code=n0esc3NRze7LTCu7iYzS6a5acc3f0ogp4
&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer
&client_assertion=eyJhbGciOiJSUzI1NiIsImtpZCI6IjIyIn0.eyJpc3Mi...

The Connect2id server will prevent replay of authentication JWTs that include a jti claim, unless this security feature is disabled.

The Connect2id server supports the standard HS256, HS384 and HS512 JWS algorithms for JWTs secured with HMAC.

When registering for client_secret_jwt the preferred JWS algorithm should be set.

The client_secret_jwt method is specified in OpenID Connect Core 1.0, section 9 and in RFC 7523, section 2.2.

5.2 Private key based

5.2.1 private_key_jwt

Based on a private RSA or EC key belonging to the client to sign a JWT assertion. The matching public key must be registered in JSON Web Key (JWK) format with the Connect2id server, either by value or by URL. The registration by URL enables key-rollover without the need to update the client registration.

Starting with Connect2id server v12.12 a client can also use a qualified X.509 certificate included in the JWT header (as "x5c" parameter) to pass its public key. In this case registration of the client JWK set is skipped.

The JWT header must include the "alg" parameter to specify the signing algorithm. If the signing key was registered with a key ID ("kid") it should be included as well, so the server can select the correct key in case multiple signing keys were saved in the set for rollover purposes (otherwise the server will need to check the signature with each found key).

Example JWT header:

{
  "alg" : "RS256",
  "kid" : "1"
}

If the client is using a qualified certificate it must be included in the "x5c" header parameter (or the entire certificate chain if intermediate authorities exist).

{
  "alg" : "RS256",
  "x5c" : [ "MIIE3jCCA8agAwIBAgICAwEwDQYJKoZIhvcNAQEFBQAwYzELMAkGA1UEBhMC..." ]
}

The JWT contains the following claims:

  • iss and sub set to the client_id.
  • aud set to the token endpoint URL.
  • exp set the JWT expiration time.
  • jti set to a unique identifier for the JWT, used to prevent replay (optional).
  • nbf set to time before which the JWT must not be accepted for processing (optional).
  • iat set to the JWT issue time (optional).

Example JWT claims:

{
  "iss" : "oe7aiz60",
  "sub" : "oe7aiz60",
  "aud" : "https://demo.c2id.com/token",
  "exp" : 1453021544,
  "jti" : "Eefaevo0"
}

The client authenticates with private_key_jwt by including the client_assertion_type parameter set to urn:ietf:params:oauth:client-assertion-type:jwt-bearer and the client_assertion parameter set to the serialised JWT.

POST /token HTTP/1.1
Host: c2id.com
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&code=n0esc3NRze7LTCu7iYzS6a5acc3f0ogp4
&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer
&client_assertion=eyJhbGciOiJSUzI1NiIsImtpZCI6IjIyIn0.eyJpc3Mi...

The Connect2id server will prevent replay of authentication JWTs that include a jti claim, unless this security feature is disabled.

The Connect2id server supports RSA and EC keys:

  • Use the RS256, RS384, RS512, PS256, PS384 or PS512 JWS algorithm to sign the JWT assertion with a private RSA key (minimum required key length is 2048 bits).

  • Use ES256, ES384 or ES512 JWS algorithm to sign the JWT assertion with a private EC key (requires matching curve).

When registering for private_key_jwt the preferred JWS algorithm should be set.

The private_key_jwt method is specified in OpenID Connect Core 1.0, section 9 and in RFC 7523, section 2.2.

5.2.2 tls_client_auth

The client authenticates with an X.509 certificate submitted to the Connect2id server during the TLS handshake. The client certificate must be issued by a trusted Certificate Authority (CA) and will typically be validated by a dedicated TLS termination proxy in front of the server.

When registering for tls_client_auth the expected subject DN of the certificate must be set.

Note, the client must include its client_id in the HTTP request body to identify itself.

POST /token HTTP/1.1
Host: c2id.com
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&code=n0esc3NRze7LTCu7iYzS6a5acc3f0ogp4
&client_id=s6BhdRkqt3

A benefit of this method is that the issued access token is bound to the client certificate, which fixes the bearer weakness of regular OAuth 2.0 access tokens. This client authentication method and the binding are specified in the mTLS OAuth 2.0 extension (RFC 8705).

5.2.3 self_signed_tls_client_auth

Similarly to the tls_client_auth method the client authenticates with a X.509 certificate submitted to the Connect2id server during the TLS handshake, but the certificate can be self-signed (self-issued) and thus no CA and X.509 based public key infrastructure (PKIX) is required. The TLS termination proxy is configured to accept self-signed certificates.

The validity of the client certificate is established by the client having its public key of the certificate registered with the Connect2id server, in JWK format by value or URL, as with private_key_jwt.

The client must include its client_id in the HTTP request body to identify itself.

POST /token HTTP/1.1
Host: c2id.com
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&code=n0esc3NRze7LTCu7iYzS6a5acc3f0ogp4
&client_id=s6BhdRkqt3

The issued access token will get bound to the client certificate, addressing the bearer weakness of regular OAuth 2.0 access tokens. This client authentication method and the binding are specified in the mTLS OAuth 2.0 extension (RFC 8705).

6. Limiting the accepted client authentication methods and JWS algorithms

To limit the client authentication methods accepted by a Connect2id server deployment edit the op.token.authMethods configuration setting.

For example, if your policy prohibits public OAuth 2.0 clients remove the none method from the list.

To limit the available JWS algorithms for the JWT assertions edit the op.token.authJWSAlgs configuration setting.

The available authentication methods and algorithms can also be limited in your developer portal which manages requests to the client registration endpoint.

7. Credential update and rollover

7.1 Client secret expiration, update and rollover

The client secrets generated by the Connect2id server have no expiration, unless the server is configured to enforce a particular secret lifetime. A client with an expired secret will no longer be able to authenticate at the token endpoint or perform other requests requiring authentication.

Example client secret expiration set to 168 hours (1 week):

op.reg.clientSecretLifetime=168

To obtain a new secret make a client update request, with the refresh_client_secret metadata field set to true, for example:

PUT /clients/s6BhdRkqt3 HTTP/1.1
Authorization: Bearer SQvs1wv1NcAgsZomWWif0d9SDO0GKHYrUN6YR0ocmN0
Content-Type: application/json

{
  "client_id"             : "s6BhdRkqt3",
  "redirect_uris"         : [ "https://client.example.org/cb" ],
  "client_name"           : "My App",
  "refresh_client_secret" : true
}

If the secret has already expired or the Connect2id server is configured to always update the secret on a client update (the default setting) the refresh_client_secret field can be omitted.

The old secret will remain usable for another 30 minutes, so that the client software can switch to the new one without experiencing client authentication failures during the rollover.

7.2 Private key update and rollover

Clients that use private keys to authenticate with the Connect2id server determine on their own when to expire and update them. To enable seamless rollover the client should add the new signing key to its existing public JWK set (registered by value with jwks or by URL with jwks_uri) and then switch to it for authentication. The Connect2id server will pick the new key automatically.

8. How to debug client authentication errors

Clients that fail to authenticate at a Connect2id server endpoint, such as the token endpoint, will receive an HTTP 401 Unauthorized error response.

The error_description will have a helpful checklist of all possible causes why the client authentication may have failed. The actual cause will not be disclosed in the HTTP response, for security reasons.

HTTP/1.1 401 Unauthorized
Content-Type: application/json

{
  "error"             : "invalid_client",
  "error_description" : "Invalid client: Possible causes may be missing /
                         invalid client_id, missing client authentication,
                         invalid or expired client secret, invalid or expired
                         JWT authentication, invalid or expired client X.509
                         certificate, or an unexpected client authentication
                         method",
  "client_auth_id"    : "cgXB4EyYViWPt6g2"
}

If you have access to the server logs, or the client account is provisioned with access to its authentication events, the unique client_auth_id in the error response can be used to quickly locate the particular authentication and obtain the recorded cause.

Example error log message:

2022-06-03T12:21:48,974 INFO http-nio-8080-exec-26 TOKEN - [OP6203] Bad client
authentication: Client certificate subject DN doesn't match: Expected
CN=app923412, received CN=app912430: client_id=pjbizwoy5mjei
method=tls_client_auth client_auth_id=cgXB4EyYViWPt6g2

9. Client authentication events

The Connect2id server includes a plugin interface for intercepting all client authentications - both successful and failed, with additional context, such as the unique client_auth_id explained above to identify the authentication event.

This interface can be used to implement special client authentication auditing and reporting, or a self-service panel where client developers can go to quickly debug HTTP 401 Unauthorized errors.

10. Developer resources

The OAuth 2.0 / OpenID Connect SDK for Java that Connect2id maintains provides support for all standard client authentication methods.