Skip to content

[FIX] OIDC: Explicitly request email claim in ID token to comply with OpenID Connect spec#3285

Open
kvizillon wants to merge 1 commit intoLeantime:masterfrom
kvizillon:kvizillon-patch-1
Open

[FIX] OIDC: Explicitly request email claim in ID token to comply with OpenID Connect spec#3285
kvizillon wants to merge 1 commit intoLeantime:masterfrom
kvizillon:kvizillon-patch-1

Conversation

@kvizillon
Copy link
Copy Markdown

Description

This pull request adds the claims parameter to the OIDC authorization request to explicitly request the email claim to be included in the ID Token.

Background & Problem

Currently, Leantime attempts to read user claims (like email) directly from the ID Token. According to the OpenID Connect Core 1.0 specification, an ID Token is primarily for authentication and contains only a minimal set of claims (such as iss, sub, aud, exp, iat). It is not required, nor guaranteed, to contain any user claims like email or name.

The specification states that user claims should be obtained from the UserInfo Endpoint using the Access Token. However, Leantime's current OIDC implementation does not call the UserInfo endpoint if an ID Token is present, leading to authentication failures when the Identity Provider (IdP) does not include these claims in the ID Token by default.

From the OpenID Connect Core 1.0 specification:

"ID Token is a JSON Web Token (JWT) that contains claims about the authentication event. It is NOT intended to provide user information claims; those are available from the UserInfo Endpoint."
OpenID Connect Core 1.0, Section 2

"Claims requested using the claims parameter can be specified as essential or voluntary. An essential claim is one that the Client considers necessary for a successful authentication flow."
OpenID Connect Core 1.0, Section 5.5.1

Many IdPs (like Google) include email as a pragmatic measure, but strictly compliant IdPs (like SimpleSAMLphp with the OIDC module) follow the specification and do not include them, causing Leantime to fail with errors like Unable to read email to log you in.

The Fix

This PR implements the correct OIDC flow for requesting claims. As per the specification, a Client can request specific claims to be returned in the ID Token using the claims request parameter.

By adding this to the authorization request:

'claims' => json_encode([
    'id_token' => [
        'email' => ['essential' => true]
    ]
])

Leantime explicitly signals to the IdP that the email claim is essential and should be included in the ID Token. This ensures the IdP includes it, making the authentication flow work with all compliant providers without changing Leantime's core logic of reading the ID Token.

Changes Made

  1. Modified buildLoginUrl() in app/Domain/Oidc/Services/Oidc.php to add the claims parameter to the authorization URL.

  2. The parameter requests the email claim as essential for the ID Token.

Code Changes

public function buildLoginUrl(): string
{
    if ($this->getAuthUrl()) {
        return $this->getAuthUrl().'?'.http_build_query([
            'client_id' => $this->clientId,
            'redirect_uri' => $this->buildRedirectUrl(),
            'response_type' => 'code',
            'scope' => $this->scopes,
            'state' => $this->generateState(),
+           'claims' => json_encode([
+               'id_token' => [
+                   'email' => ['essential' => true]
+               ]
+           ]),
        ]);
    }
    return false;
}

Impact

  • Fixes authentication for strictly compliant OpenID Connect Providers (e.g., SimpleSAMLphp, Keycloak, etc.)

  • Maintains backward compatibility with providers that already include claims in the ID Token

  • Brings Leantime's OIDC implementation one step closer to full specification compliance without a major rewrite

  • No breaking changes for existing installations

Related Issues

This addresses a common problem where users integrating Leantime with standard OIDC providers encounter email retrieval errors. Similar issues have been reported in various integration scenarios.

How Has This Been Tested?

  • Tested with SimpleSAMLphp OIDC module (strictly compliant provider)

  • Verified that email is properly extracted from ID Token

  • Confirmed backward compatibility with existing OIDC providers

References

OpenID Connect Core 1.0, Section 2 - ID Token

OpenID Connect Core 1.0, Section 5.5.1 - Individual Claims Requests

OpenID Connect Core 1.0, Section 5.3 - UserInfo Endpoint

Additional Notes

This change follows the principle of "explicit over implicit" in OIDC. By explicitly requesting the email claim for the ID Token, Leantime becomes more robust and compatible with a wider range of OpenID Connect Providers while maintaining the same user experience.

… OpenID Connect spec

### Description
This pull request adds the `claims` parameter to the OIDC authorization request to explicitly request the `email` claim to be included in the ID Token.

### Background & Problem
Currently, Leantime attempts to read user claims (like `email`) directly from the ID Token. According to the **OpenID Connect Core 1.0 specification**, an ID Token is primarily for authentication and contains only a minimal set of claims (such as `iss`, `sub`, `aud`, `exp`, `iat`). It is not required, nor guaranteed, to contain any user claims like `email` or `name`.

The specification states that user claims should be obtained from the **UserInfo Endpoint** using the Access Token. However, Leantime's current OIDC implementation does not call the UserInfo endpoint if an ID Token is present, leading to authentication failures when the Identity Provider (IdP) does not include these claims in the ID Token by default.

Many IdPs (like Google) include email as a pragmatic measure, but strictly compliant IdPs (like SimpleSAMLphp with the OIDC module) follow the specification and do not include them, causing Leantime to fail with errors like `Unable to read email to log you in`.

### The Fix
This PR implements the correct OIDC flow for requesting claims. As per [**OpenID Connect Core 1.0, Section 5.5.1**](https://openid.net/specs/openid-connect-core-1_0.html#IndividualClaimsRequests), a Client can request specific claims to be returned in the ID Token using the `claims` request parameter.

By adding:
```php
'claims' => json_encode(['id_token' => ['email' => ['essential' => true]]])
to the authorization request, Leantime explicitly signals to the IdP that the email claim is essential and should be included in the ID Token. This ensures the IdP includes it, making the authentication flow work with all compliant providers without changing Leantime's core logic of reading the ID Token.

Changes Made
Modified buildLoginUrl() in Oidc.php to add the claims parameter to the authorization URL.

The parameter requests the email claim as essential for the ID Token.

Impact
Fixes authentication for strictly compliant OpenID Connect Providers (e.g., SimpleSAMLphp).

Maintains backward compatibility with providers that already include claims in the ID Token.

Brings Leantime's OIDC implementation one step closer to full specification compliance without a major rewrite.

Related Issue
This addresses a common problem where users integrating Leantime with standard OIDC providers encounter email retrieval errors.
@kvizillon kvizillon requested a review from a team as a code owner February 27, 2026 15:22
@kvizillon kvizillon requested review from pmarcowski and removed request for a team February 27, 2026 15:22
@CLAassistant
Copy link
Copy Markdown

CLAassistant commented Feb 27, 2026

CLA assistant check
All committers have signed the CLA.

@marcelfolaron marcelfolaron requested review from Copilot and marcelfolaron and removed request for pmarcowski February 28, 2026 15:06
@marcelfolaron
Copy link
Copy Markdown
Contributor

Thank you!

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates Leantime’s OIDC authorization URL construction to explicitly request the email claim in the ID Token via the OpenID Connect claims parameter, aiming to improve compatibility with spec-compliant IdPs that don’t include user claims by default.

Changes:

  • Add a claims parameter to the OIDC authorization request.
  • Request email as an essential claim in the id_token.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +136 to +138
'id_token' => [
'email' => ['essential' => true]
]
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The claims request hard-codes email as an essential ID-token claim, but the login flow actually reads the username from the configurable $this->fieldEmail (which can be changed to a different claim like preferred_username/upn). Marking email as essential can cause otherwise-working IdPs/configs to fail the auth request if they can’t provide an email. Consider only requesting email when $this->fieldEmail === 'email', or deriving the requested claim from $this->fieldEmail when it’s a simple (non-dotted) claim name, and not marking it essential otherwise.

Copilot uses AI. Check for mistakes.
Comment on lines +135 to +139
'claims' => json_encode([
'id_token' => [
'email' => ['essential' => true]
]
]),
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

json_encode() can return false (e.g., on encoding errors) and this would silently produce an empty/invalid claims parameter in the authorization URL. Consider encoding with JSON_THROW_ON_ERROR and handling the exception (or otherwise checking json_last_error()) so failures are explicit and easier to diagnose.

Copilot uses AI. Check for mistakes.
@marcelfolaron
Copy link
Copy Markdown
Contributor

Can you review the comments and see if you can find a more flexible solution for the email claim?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants