Have you ever wanted to know authentication works? Did you wonder what verifiable credentials are, how OIDC ID Token work, and why isn’t SAML more? Or maybe you are curious how your identity and data are kept secure. Well, you’re in the right place!
In this series, we’ll pull back the curtain on the protocols that help us access services, gain access to resources, and allow services to act on our behalf securely.
Deciphering the ID Token
Let’s kick things off with the ID Token, a protocol we encounter daily. We’ll delve into how it streamlines authentication, reducing the hassle of repeated logins and safeguarding against reply attacks.
An ID token, typically formatted as a JSON Web Token (JWT), is our trusted ally in managing user authentication. It carries a set of claims or statements about a user and additional metadata packaged in three parts: Header, Payload, and Signature.
Understanding the ID Token Structure
Let’s break down the structure of an ID Token:
- Header: It encompasses two properties – alg signifying the algorithm used to sign the token, and typ, which denotes the token type as “JWT”.
- Payload: This section carries claims about the user. The OpenID Connect specification standard claims and includes the following:
- iss (Issuer): Ensures a trusted authorization server issued the token. If the iss value doesn’t match the expected issuer, the token should be rejected to prevent token injection attacks by untrusted issuers.
- sub (Subject): This is the unique identifier for the user. This claim is essential to identify the user for whom the token was issued.
- aud (Audience): Protects against sending the token to an unintended audience (typically, an unintended client application). The client application should reject any token if the aud claim doesn’t match its identifier. This helps mitigate potential token interception and replay attacks.
- exp (Expiration Time): Ensures that the token is not used beyond its intended life. It’s used to protect against replay attacks. Once the token has expired, the client application should reject it.
- iat (Issued At): This claim can be used to determine the age of the token and reject tokens that were issued too far in the past.
- auth_time (Authentication Time): It represents the time when the user was authenticated. It’s useful in scenarios where you wish to enforce re-authentication of the user after a certain period.
- nonce (Arbitrary String): This is used to associate a client’s session with an ID token to mitigate replay attacks. The client application provides this string at the start of the authentication process and should verify it in the ID token returned.
- acr and amr (Authentication Context Class Reference and Authentication Methods References): Provide information about the context and methods used for user authentication. This can be useful in assessing the ‘strength’ of the user authentication and applying additional security measures if needed.
- azp (Authorized party): Used to identify the party to which the ID Token was issued. If present, it should contain the client ID of the party. This is useful when the ID token has a single intended recipient but could potentially be handled by multiple parties.
- Signature: Helps ensure data integrity and confirm the sender’s identity. If the signature verification fails, the token should be rejected as it may have been tampered with during transit (protecting against man-in-the-middle attacks).
- Signature: The concluding part of the JWT ensures the sender’s authenticity and the message’s integrity during transit.
The ID token’s primary role is to authenticate the user. Upon successful login, the authorization server issues this token to the client application, which can decode it, verify its issuing authority, confirm its intended use, and extract user information from the payload to establish a user session. ID tokens should only be used for authentication, not authorization, as that’s the job of access tokens, which we’ll cover later.
Each property of an ID token safeguards against potential security threats and ensures secure authentication. For instance, iss guarantees that a trusted authorization server issued the token, sub uniquely identifies the user, and aud helps prevent token interception by confirming the token is meant for the correct audience. The exp, iat, and auth_time claims regulate the token’s life cycle and validity period. nonce, acr, amr, and azp further fortify the authentication process by mitigating replay attacks, providing context and methods for authentication, and identifying the authorized party. Lastly, the signature ensures data integrity and verifies the sender’s identity, protecting against man-in-the-middle attacks. The ID token’s properties work collaboratively to authenticate the user securely, maintain token integrity, and mitigate potential attack vectors.
Let’s say you’re building a web application called “Awesome App”. You decide to use an external authentication service to handle user authentication so that you don’t have to manage usernames and passwords yourself. You choose to use an OpenID Connect compatible service like Google.
Here’s what happens when a user tries to log in to your app:
- The user visits “Awesome App” and clicks the “Log in with Google” button.
- “Awesome App” generates a random nonce value for this authentication request, say random123, and sends it along with the request to Google’s authentication server.
- The user is redirected to the Google login page, where they enter their Google username and password.
- Once the user is authenticated, Google’s authentication server issues an ID token. The payload of this token includes claims like:
- iss: “https://accounts.google.com” (the issuer)
- sub: “1234567890” (the subject, a unique identifier for the user)
- aud: “awesome-app” (the audience, your app’s client ID)
- exp: 1684810447 (the expiration time, a Unix timestamp)
- iat: 1684806847 (the time at which the token was issued, a Unix timestamp)
- auth_time: 1684806810 (the time the user authenticated, a Unix timestamp)
- nonce: random123 (the same nonce your app generated at the start of this process)
- The ID token is returned to “Awesome App”.
- “Awesome App” verifies the ID token:
- It checks the iss claim to ensure the token came from “https://accounts.google.com“.
- It checks the aud claim to ensure the token is intended for “Awesome App” by matching it with “awesome-app”.
- It checks the exp claim to ensure the token has not expired (i.e., the current Unix timestamp is less than 1684810447).
- It checks the nonce claim to make sure it matches the nonce generated earlier (random123).
- Once the ID token is verified, “Awesome App” creates a server-side session for the user and sends a session cookie to the user’s browser. This session cookie contains a session ID, say session123, uniquely identifying the user’s session.
- The user’s browser automatically includes the session cookie for each subsequent request. “Awesome App” uses the cookie’s session ID (session123) to look up the user’s session and confirm their authentication status.
- When the user logs out, “Awesome App” invalidates the server-side session, and the browser discards the session cookie (session123).
In this scenario, the ID token (containing values like “https://accounts.google.com“, “1234567890”, “awesome-app”, 1684810447, 1684806847, 1684806810, random123) plays a key role in the initial user authentication. At the same time, the session cookie (session123) is used to maintain the user’s authentication status throughout their session. Together, they provide a secure and seamless user experience.
Ok, this was a lot for now. Subscribe to be notified when the next post comes out and send any questions or comments – I promise to read all of them!