Code Monkey home page Code Monkey logo

symfony-oidc's People

Contributors

bobvandevijver avatar hockdudu avatar lorisz avatar michaelkaefer avatar muhlemmer avatar rvmourik avatar tobias-93 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

symfony-oidc's Issues

Required id_token parameter is not in spec

Hi i am currently integrating a third party openid connect api for our logins.

I ran into a problem where the token response did not include an id_token parameter. You referenced the RFC but this does not mention a id_token. Only access_token, token_type and state are required. In fact i can't find a reference to id_token in the whole RFC.

So what is going on here? Am i just misunderstanding something?

// These are the only required parameters per https://tools.ietf.org/html/rfc6749#section-4.2.2
if (!isset($tokens->id_token) || !isset($tokens->access_token)) {

Edit: Hold on i have accidentally removed the openid scope. That might be the issue

Question about cross-origin authentication

First of all, pardon my ignorance on this subject. Any pointers in the right direction would be appreciated.

The scenario is that I have an existing Symfony (6.2) application that has a working authentication mechanism using the now native User Entity that talks to a self-hosted database. The application is a single instance that spans multiple domains (say A.com and B.com), which is handled by routing. The form_login resides on one of said domains (say A.com/login). So far so good.

Now what I want to achieve is that if a user has successfully authenticated with the form on A.com/login, and then after visits B.com, they should automatically be authenticated on B.com. I figured I should be able to do this by introducing OpenID Connect, and that is how I stumbled upon this bundle, but perhaps that was misguided?

I installed and configured the bundle, including a custom UserProvider and a controller with a checkLogin. This appears to work as intended, because when I visit B.com/login_oidc, it redirects me to A.com/login, and there the user can authenticate. What I fail to understand, however, is how I get B.com to automatically check the authentication status with A.com, and then authenticate the user on B.com accordingly.

I’m sure I’m missing a step, but I’m not sure of the magnitude of this step, ranging from just missing a configuration/setting, to having entirely missed the point of this bundle. Would anyone be so kind as to indicate if this bundle at all helps with what I’m trying to archive, and in any event point me in the right direction? Thanks in advance, as this would be greatly appreciated.

Allow additional authentication parameters

Some OIDC providers like Microsoft accept additional auth params like login_hint and domain_hint.1

It would be cool if there was an easy way to pass these parameters when generating the authorization request. Currently, it's only possible by modifying the redirect response.2

This can be currently made in the Jumbojett's implementation by using the field authParams.3

Maybe we could do this here with an additional parameter at \Drenso\OidcBundle\OidcClient::generateAuthorizationRedirect (e.g. array $additionalParameters = [])?

Footnotes

  1. https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-protocols-oidc#send-the-sign-in-request

  2. For instance:

    $response = $oidcClient->generateAuthorizationRedirect();
    $targetUrl = $response->getTargetUrl();
    $targetUrl .= '&' . http_build_query(['domain_hint' => 'example.com']);
    return new RedirectResponse($targetUrl);
    
  3. See https://github.com/jumbojett/OpenID-Connect-PHP/blob/6aae75b1c7b58d8d64a6d14877f77b8a13ab90f6/src/OpenIDConnectClient.php#L594 and https://github.com/jumbojett/OpenID-Connect-PHP/blob/6aae75b1c7b58d8d64a6d14877f77b8a13ab90f6/src/OpenIDConnectClient.php#L797

How to save the identity provider used for registration?

In the OidcUserProviderInterface there is a function ensureUserExists where the user is either resolved or created. If I use multiple OIDC providers, I am not sure how to save the provider that the user used in this function (e.g. Google, Twitter, ...). Is it possible to access that value in this function or in another place?

These are the identifiers which I would need to access:

drenso_oidc:
    clients:
        google: 
            ...
        twitter: 
            ...

example project

Hello,
is there an example project that uses symfony-oidc?

UserProvider

Hello There,
Can someone have an exemple of his :
Security.yaml
Drenso_oidc.yaml
LoginController.php
Userprovider.php
I can't find a way to get OIDC working, i think i am missing things.
Thinks !

How to retrieve user roles during the authentication ?

Hello,
thank you for this library.

I'm seeking a way to retrieve the user roles during the authentication in the function ensureUserExists of the user provider. I noticed that retrieveUserInfo() does not return the field that contains roles. But I also noticed that the tokens returned by authenticate() contain these roles.

This is my question: Am I wrong trying to read tokens in the ensureUserExists() function ?

If I'm not, here is the solution I've in mind:

// vendor/drenso/symfony-oidc-bundle/src/Security/OidcAuthenticator.php

public function authenticate(Request $request): Passport
  //...
  $authData = $this->oidcClient->authenticate($request);
  $userData = $this->oidcClient->retrieveUserInfo($authData);
  //...
  $this->oidcUserProvider->ensureUserExists($userIdentifier, $userData, $authData);
  // INSTEAD OF
  $this->oidcUserProvider->ensureUserExists($userIdentifier, $userData);

It's about one single parameter. Will you accept a Pull Request to add this parameter ? If not, could you please suggest me a way to get user roles the right way ?

Thanks in advance

Can not disable verifyNonce when dealing with JWT claims

Hello,

I'm calling an Open ID Connect which does not provide "nonce" into JWT claims inside the token.
Warning: Undefined property: stdClass::$nonce

This does give me an error inside OidcJwtHelper::verifyJwtClaims method as it's called through OidcClient::authenticate method without the possibility of specifying parameter verifyNonce to set it to false.

// Request and verify the tokens return $this->verifyTokens( $this->requestTokens('authorization_code', $code, $this->getRedirectUrl()) );

So I dag a little bit and tried to create my own client but was not able to make it replace the default one.

I must say that I'm not particularly familiar with security stuffs into Symfony.
Can you please tell me what I'm doing wrong ?

Thanks.
Benjamin.

PS : Please find attached my configuration files.
drenso_oidc.yaml.txt
security.yaml.txt

Invalid session state

Hi,

Thanks for this bundle which is veru usefull !

I get an exception when trying to set up a second client.

drenso_oidc.yaml :

drenso_oidc:
    #default_client: default # The default client, will be aliased to OidcClientInterface
    clients:
        default: # The client name, each client will be aliased to its name (for example, $defaultOidcClient)
            # Required OIDC client configuration
            well_known_url: '%env(OIDC_WELL_KNOWN_URL)%'
            client_id: '%env(OIDC_CLIENT_ID)%'
            client_secret: '%env(OIDC_CLIENT_SECRET)%'
            # Extra configuration options
            redirect_route: '/oidc/login_check'
            #custom_client_headers: []

        # Add any extra client
        another_client: # Will be accessible using $linkOidcClient
            well_known_url: 'http://localhost:8080/realms/my-realm/.well-known/openid-configuration'
            client_id: 'another_client'
            client_secret: 'hA5OUlkzEFX63Qhmf5jFVdfmT9vDgZkX'

When i am connecting with the "default" client it works. But when i'm working with "another_client" i get an exception :

[2023-03-08T16:26:01.252978+01:00] security.INFO: Authenticator failed. {"exception":"[object] (Drenso\\OidcBundle\\Security\\Exception\\OidcAuthenticationException(code: 0): Invalid session state at /var/www/html/fizyou/vendor/drenso/symfony-oidc-bundle/src/OidcClient.php:90)","authenticator":"Drenso\\OidcBundle\\Security\\OidcAuthenticator"} []
[2023-03-08T16:26:01.253267+01:00] security.DEBUG: Authentication failure, redirect triggered. {"failure_path":"/login"} []

Here is my config :

security:
    #enable_authenticator_manager: true
    providers:
        #app_user_provider: 
            #entity: {class: App\Entity\User, property: email}
        oidc_provider:
            id: App\Security\OpenIdConnectUserProvider
    password_hashers:
        App\Entity\User:
            algorithm: auto

    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false       
            
        main:
            lazy: true
            provider: oidc_provider
            custom_authenticators: 
                - App\Security\LoginFormAuthenticator

            user_checker: App\Security\UserChecker
            form_login: ~
            oidc:
                user_identifier_property: email
                check_path: /oidc/login_check
            entry_point: form_login
            logout:
                path: app_logout
                # where to redirect after logout
                target: app_login
            # configure the maximum login attempts in a custom period of time
            login_throttling:
                max_attempts: 3
                interval: '15 minutes'
            remember_me: 
                secret: '%kernel.secret%'
                lifetime: 2592000 # 30 days in seconds
                path: /
                secure: true
                remember_me_parameter: _remember_me`

My controller :

    #[Route('/oidc/login/{entityId}', name: 'oidc_login')]
    #[IsGranted('PUBLIC_ACCESS')]
    public function surfconext(OidcClientLocator $clientLocator, OidcClientInterface $oidcClient, Request $request, string $entityId): RedirectResponse
    {
      // Redirect to authorization @ OIDC provider
      //return $oidcClient->generateAuthorizationRedirect(); 
      return $clientLocator->getClient($entityId)->generateAuthorizationRedirect();
    }

    #[Route('/oidc/login_check', name: 'oidc_login_check')]
    #[IsGranted('PUBLIC_ACCESS')]
    public function checkLogin(OidcClientInterface $oidcClient, Request $request, string $code, string $state): RedirectResponse
    {

    }

Do you have any idea how to fix this issue ?

Thank you !

Authentication token during ensureUserExists

Hi, i need the authentication token in ensureUserExists to access a profile endpoint to retrieve data to populate a new profile.

On v1 I used loadUserByToken(OidcToken $token) but this changed to ensureUserExists(string $userIdentifier, OidcUserData $userData) where i only have the OidcUserData object.

How can i do this in v2? Do i have to overwrite the OidcClient class for a custom retrieveUserInfo?

Add support for array as userIdentifier

SURFconext Test IdP gives as user data:

afbeelding

We would like to use the uid as user identification, however, using an array (with only 1 value) as userIdentifier not supported right now. Is there a way to still use this? Or is there a way to alter the userIdentifier to get a value from this array before using it?

Thanks in advance

Question: using in API REST context

Hi,

Is there any example how to secure an API with this bundle ?

I've tried to setup the configuration as described in the documentation but when I call the controller I got this error:
No route found for "GET http://localhost/login"

Below the use case:

  • Our API must be secured with JWT supplied from our third party IDP

  • The API is being called from an Angular application, actually I'm testing with Postman, I've a public account in our IDP that I'm using

  • The JWT contains a unique identifier (sub) which is the email's user

  • I've created a class implementing OidcUserProviderInterface to load the user from our User table which contains ROLES for every user in the system

  • below my configuration:

//drenso config:

    #default_client: default # The default client, will be aliased to OidcClientInterface
    clients:
        default: # The client name, each client will be aliased to its name (for example, $defaultOidcClient)
            # Required OIDC client configuration
            well_known_url: '%env(OIDC_WELL_KNOWN_URL)%'
            client_id: '%env(OIDC_CLIENT_ID)%'
            client_secret: '%env(OIDC_CLIENT_SECRET)%'
            well_known_cache_time: 1800 # Time in seconds, will only be used when symfony/cache is available

//security.yml

security:
    role_hierarchy:
        ROLE_ADMIN: [ROLE_USER, ROLE_VIEWER, ROLE_APPROVER]
        ROLE_APPROVER: [ROLE_USER, ROLE_VIEWER]
        ROLE_VIEWER: [ROLE_USER]

    providers:
        oidc_user_provider:
            id: App\Security\OidcUserProvider

    enable_authenticator_manager: true

    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        api:
            pattern:   ^/
            # pattern:   ^/api/(v|V)[0-9]+/.*
            stateless: true
            provider: oidc_user_provider
            oidc:
                user_identifier_from_idtoken: true
                user_identifier_property: "%env(USER_IDENTITY_FIELD)%"

        main:
            provider: oidc_user_provider
            pattern: ^/
            oidc: ~
            lazy: true
            stateless: true

    # Easy way to control access for large sections of your site
    # Note: Only the *first* access control that matches will be used
    access_control:
        - { path: ^/api/(v|V)[0-9]+/.*, roles: [ROLE_USER] }
        - { path: ^/api/(v|V)[0-9]+/admin/.*, roles: ROLE_ADMIN }

//controller

<?php

namespace App\Controller;

use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
use Symfony\Component\Security\Core\User\UserInterface;
use Drenso\OidcBundle\OidcClientInterface;

#[Route('/api/v1', name:'lucky')]
class LuckyController extends AbstractController {
    
    #[Route('/lucky/number', name:'lucky_number')]
    // public function number(UserInterface $user): JsonResponse
    public function number(OidcClientInterface  $user): JsonResponse
    {
        try {
            $number = random_int(0, 100);
    
            $data = [
                'username' => $user->getFirstName() . ' '.$user->getLastName(),
                'random_number' => $number
            ];
            
            // Return a JSON response with the data
            return $this->json($data);

        }
        catch(BadCredentialsException){
            return $this->json(['error' => $e->getMessage()], 401);
        }
        catch(Exception){
            return $this->json(['error' => $e->getMessage()], 500);
        }
    }
}

Limitations to pass idP adapter (pfidpadapterid) details

We are unable to pass additional parameter idP adapter (pfidpadapterid) details as the constructor accepts only $wellKnownUrl, $clientId, $clientSecret parameters. Is there a possibility to pass the idP adapter details to the oidcclient?

How to use more clients in the same firewall ?

Hello,
please could you explain, how to use more clients with their check_paths in one firewall? I would like to distinguish what "response" is from where and so what to do in code.
Thank you, J.

Facebook OIDC configuration doesn't contain "token_endpoint_auth_methods_supported"

Heya, I was trying to set up your bundle in a Symfony 6.1 project and pointing it at Facebook for OIDC connectivity (using their configuration endpoint as specified on https://developers.facebook.com/docs/facebook-login/limited-login/token/)

However, authentication seems to fail, after some digging around and adding some dumps to the OidcClient, I figured out that it fails inside of the getConfigurationValue(string $key) method, as it is looking for the key token_endpoint_auth_methods_supported in the configuration, but this key isn't present. See the screenshot below for the dumped config object.

image

Any clue how to proceed and/or combat this? :) If there's anything I can do to help out, provide more info, etc, let me know :)

JWT Decode missing on UserInfo

Hi,

First of all, I'd like to warn that I'm very new to OpenID implementations, and as such, I might have failed to properly understand how it works and this issue could be a pebkac in this instance. If it is, my apologies.

That being said : I am having an issue with the library when using the openid scope, specifically within the OidcAuthenticator and the OidcClient. I'm running version 3.1.0.

Here is theOidcClient::retrieveUserInfo method.

  public function retrieveUserInfo(OidcTokens $tokens): OidcUserData
 {
   // Set the authorization header
   $headers = ["Authorization: Bearer {$tokens->getAccessToken()}"];

   // Retrieve the user information and convert the encoding to UTF-8 to harden for surfconext UTF-8 bug
   $jsonData = $this->urlFetcher->fetchUrl($this->getUserinfoEndpoint(), null, $headers);
   $jsonData = mb_convert_encoding($jsonData, 'UTF-8');

   // Read the data
   $data = json_decode($jsonData, true);

   // Check data due
   if (!is_array($data)) {
     throw new OidcException('Error retrieving the user info from the endpoint.');
   }

   return new OidcUserData($data);
 }

As you can see, the method expects the returned data to be available immediately, and tries to json_decode it. However, as per the OpenID specs of the UserInfo response, this data might actually be encoded within a JWT as well. If it is, the json_decode will return null (because we are passing it the encoded JWT, and not actual json data), which will then trigger the throw, and making the whole auth fail despite my provider sending back the correct data (I've decoded the response manually for debug purposes).

I thought that maybe I was missing something and that the user_identifier_from_idtoken config value had to be set to true when using encoded openid, but even so, given how the OidcAuthenticator::authenticate method is implemented, the OidcClient::retrieveUserInfo will be called regardless and will fail anyway.

public function authenticate(Request $request): Passport
  {
    try {
      // Try to authenticate the request
      $authData = $this->oidcClient->authenticate($request);

      // Retrieve the user data with the authentication data
      $userData = $this->oidcClient->retrieveUserInfo($authData);

      // Look for the user identifier in either the id_token or the userinfo endpoint
      if ($this->userIdentifierFromIdToken) {
        $userIdentifier = OidcJwtHelper::parseToken($authData->getIdToken())
          ->claims()
          ->get($this->userIdentifierProperty);
      } else {
        $userIdentifier = $userData->getUserDataString($this->userIdentifierProperty);
      }

      [Rest of the code, irrelevent to my current issue]

      return $passport;
    } catch (OidcException $e) {
      throw new OidcAuthenticationException('OIDC authentication failed', $e);
    }
  }

So, ultimately, the issue I'm raising is : shouldn't the OidcClient::retrieveUserInfo method handle having an JWT-encoded response ?

Really slow response time for /login_check

I am seeing really slow response times for the /login_check endpoint.

It is ~4 seconds in the dev environment - I see in the profiler that TraceableFirewallListener is taking almost all of this time.

Then I compared this to the time for the /login_check endpoint in my prod environment and it's roughly the same 4 seconds, so I don't think it's any debugging / profiling overhead particularly.

Any ideas what could be causing the endpoint to take so long to return a response?

Thanks!

token_endpoint_auth_methods_supported should be optional in OIDC configuration

I am currently in the process of configuring my Symfony App to connect with Casdoor.

The Casdoor OIDC discovery is missing the token_endpoint_auth_methods_supported (https://casdoor.org/fr/docs/how-to-connect/oidc-client/), which leads to an OidcConfigurationException in the bundle.

From https://openid.net/specs/openid-connect-discovery-1_0.html#rfc.section.3

token_endpoint_auth_methods_supported
OPTIONAL. JSON array containing a list of Client Authentication methods supported by this Token Endpoint. The options are client_secret_post, client_secret_basic, client_secret_jwt, and private_key_jwt, as described in Section 9 of OpenID Connect Core 1.0 [OpenID.Core]. Other authentication methods MAY be defined by extensions. If omitted, the default is client_secret_basic -- the HTTP Basic Authentication Scheme specified in Section 2.3.1 of OAuth 2.0 [RFC6749].

It seems to me that the token_endpoint_auth_methods_supported key should be optional and the parser should default to ['client_secret_basic'] if it is missing ?

No route found for "POST https://my-domain/login_check" (from "https://identity-provider.domain/")

I get in my Symfony No route found for "POST https://my.domain/login_check" (from "https://identity-provider.domain/") if my identity-provider redirects back to the redirect_uri (https://my.domain/login_check) including a code parameter. It is OpenID Connect Code Flow step 5.

Symfony: 6.3
drenso/symfony-oidc-bundle: 2.12.0

my security.yaml:

security:
    password_hashers:
        Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'
    providers:
        oidc_user_provider:
            id: App\Security\UserProvider
    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        main:
            pattern: ^/
            oidc: ~

            lazy: true
            provider: oidc_user_provider

    access_control:

my drenso_oidc.yaml :

drenso_oidc:
    #default_client: default # The default client, will be aliased to OidcClientInterface
    clients:
        default: # The client name, each client will be aliased to its name (for example, $defaultOidcClient)
            # Required OIDC client configuration
            well_known_url: '%env(OIDC_WELL_KNOWN_URL)%'
            client_id: '%env(OIDC_CLIENT_ID)%'
            client_secret: '%env(OIDC_CLIENT_SECRET)%'

my LoginController.php:

class LoginController extends AbstractController
{
    /**
     * This controller forwards the user to the OIDC login
     * @throws OidcException
     */
    #[Route('/login_oidc', name: 'login_oidc')]
    #[IsGranted('PUBLIC_ACCESS')]
    public function loginOidc(OidcClientInterface $oidcClient): RedirectResponse
    {
        // Redirect to authorization @ OIDC provider
        return $oidcClient->generateAuthorizationRedirect('login', ['openid'], false, ['response_mode' => 'form_post']);
    }
}

"No support for signature type: ES384"

Hi, I couldn't find any issue mentionning this (nor could I find any info in the docs).

After succesfully setting up Zitadel with my app, for testing purposes, I'm trying to set up another provider, Logto.
When trying to log in, the following exception is thrown :

Drenso\OidcBundle\Security\Exception\OidcAuthenticationException
  #message: "No support for signature type: ES384"
  #file: ".../vendor/drenso/symfony-oidc-bundle/src/OidcJwtHelper.php&line=130#line130)"

While the error itself is pretty self-explanatory, the following is not :

  • Is the absence of support an oversight / should it be treated as a work-in-progress ?
  • If not, are we supposed to implement it ourselves (or use an existing third-party implementation) ? How ?

Thanks for reading, and thanks for the bundle 👍

Refresh token not set to user object

Hello,
Seems there is a problem with set refresh token. I using this for a symfony project, and i found that refresh token is not set to user object, looked on source code and found the problem in src/OidcTokens.php in counstructor that it check a 'refresh_tokens' instead of 'refresh_token'

if (isset($tokens->refresh_tokens)) {
      $this->refreshToken = $tokens->refresh_tokens;
}

is it correct or maybe it's a bug?

Configuration key "authorization_endpoint" does not exist.

Hello,
I have a problem with authorization_endpoint, the client don't find my endpoint, I create a custom manager client with the correct authorization_endpoint but the problem is the bundle always take the default client. here my configuration

  main:
        lazy: true
        oidc:
            provider: oidc_provider
            login_path: /gardian/login
            check_path: /gardian/login_check
            client: gardian
            user_identifier_property: nni

Action in the controller

      /**
       * This controller forwards the user to the OIDC login
       *
       * @throws OidcException
       */
       #[Route('/gardian/login', name: 'login_oidc')]
      #[IsGranted('PUBLIC_ACCESS')]
      public function surfconext(OidcClientInterface $gardianOidcClient): RedirectResponse
     {
            // Redirect to authorization @ OIDC provider
            return $gardianClient->generateAuthorizationRedirect();
     }

configuration of the bundle

drenso_oidc:
        #default_client: default # The default client, will be aliased to OidcClientInterface
        clients:
            default: # The client name, each client will be aliased to its name (for example, $defaultOidcClient)
               # Required OIDC client configuration
               well_known_url: '%env(OIDC_WELL_KNOWN_URL)%'
               client_id: '%env(OIDC_CLIENT_ID)%'
               client_secret: '%env(OIDC_CLIENT_SECRET)%'
               redirect_route: '%env(OIDC_REDIRECT_ROUTE)%'
         gardian: # The client name, each client will be aliased to its name (for example, $gardianOidcClient)
             # Required OIDC client configuration
              well_known_url: '%env(OIDC_WELL_KNOWN_URL)%'
              client_id: '%env(OIDC_CLIENT_ID)%'
              client_secret: '%env(OIDC_CLIENT_SECRET)%'
              redirect_route: '%env(OIDC_REDIRECT_ROUTE)%' 

I used the OidcClientLocator but I have the same problem.

in my use case a have another identity provider so OidcClient dosn't work for me i have to override (generateAuthorizationRedirect methode) because my identity provider takes more paramets that are not available in

   $data = [
          'client_id'     => $this->clientId,
          'response_type' => 'code',
           'redirect_uri'  => $this->getRedirectUrl(),
           'scope'         => implode(' ', $scopes),
           'state'         => $this->generateState(),
            'nonce'         => $this->generateNonce(),
   ];

Is it possible to do such thing ?

phpseclib contains security issues and requires update to 2.0.47 and 3.0.36

phpsesclib contians the following security issues and needs to be pined to the versions ^2.0.47 and ^3.0.36.

+-------------------+----------------------------------------------------------------------------------+
| Package           | phpseclib/phpseclib                                                              |
| Severity          | high                                                                             |
| CVE               | CVE-2024-27354                                                                   |
| Title             | phpseclib a large prime can cause a denial of service                            |
| URL               | https://github.com/advisories/GHSA-hg35-mp25-qf6h                                |
| Affected versions | >=3.0.0,<3.0.36|>=2.0.0,<2.0.47|>=1.0.0,<1.0.23                                  |
| Reported at       | 2024-03-02T00:31:33+00:00                                                        |
+-------------------+----------------------------------------------------------------------------------+
+-------------------+----------------------------------------------------------------------------------+
| Package           | phpseclib/phpseclib                                                              |
| Severity          | high                                                                             |
| CVE               | CVE-2024-27355                                                                   |
| Title             | phpseclib does not properly limit the ASN1 OID length                            |
| URL               | https://github.com/advisories/GHSA-jr22-8qgm-4q87                                |
| Affected versions | >=2.0.0,<2.0.47|>=3.0.0,<3.0.36|<1.0.23                                          |
| Reported at       | 2024-03-02T00:31:33+00:00                                                        |
+-------------------+----------------------------------------------------------------------------------+

https://github.com/phpseclib/phpseclib/blob/2.0.47/CHANGELOG.md
https://github.com/phpseclib/phpseclib/blob/3.0.36/CHANGELOG.md

Link to our example project

My current workplace is an identity provider and we wanted to publish an example symfony project that uses OIDC with our product. Although I'm not a PHP engineer, it was quite straightforward to get started with Symfony and this OIDC bundle. Kudos for that!

I came across #1, where you mentioned:

I currently do not know of any project that uses this library, and is open source at the same time.

Well, we are a open source company and naturally our example is also open source 😄 . If it is is not a conflict with your own business, I would like to add a link to our project in your README.

https://github.com/zitadel/example-symfony-oidc

Thanks!

Error in OidcJwtHelper

During my authentication phase, I am getting an error from OidcJwtHelper.

Drenso\OidcBundle\OidcJwtHelper::decodeJwt(): Return value must be of type object, null returned

I did some debugging and it looks like the value that is being returned to decodeJwt from the base64url_decode function when $section is 0 is binary so when json_decode is called it fails and returns a null. I am not familiar enough with OIDC to be able to say why the value is binary or what the normal expected value is.

I would be happy to post the encoded value here, but I'm not sure if it contains any secrets.

Unclear how to implement 'login_check' route

Currently, I'm trying to implement an OIDC client flow using this bundle. After following the steps provided in the readme, I've received an "Unable to generate a URL for the named route "login_check" as such route does not exist." error. After checking out the source code, it has become clear that the login_check route is used to verify the returned authorization code. However, this is step is currently undocumented.

Add support for AccessTokenHandler (Symfony 6.2)

In Symfony 6.2 there is a new class AccessTokenHandler, which allow you to implement support for checking a token which was provided through a Header, Request Body or Querystring.

Would you accept a PR which adds a AccessTokenHandler which can be activated through:

# config/packages/security.yaml
security:
    firewalls:
        main:
            oidc: ~
            access_token:
                token_handler: Drenso\OidcBundle\Security\OidcAccessTokenHandler

The OidcAccessTokenHandler will contain the code to check if the passed Token is valid against the active Oidc client for this particular firewall.

This implementation then can be used for example if the Symfony Application contains a REST api and the API client sends the token to the SF REST API. The AccessTokenHandler will then check if the passed token is still valid.

It would need a disclaimer it is only usable with SF 6.2

Local logout option in generateEndSessionEndpointRedirect function

Hello,

Would it be possible to add an optional parameter to the OidcClient generateEndSessionEndpointRedirect function?

According to the documentation (https://openid.net/specs/openid-connect-rpinitiated-1_0.html#RPLogout):

It is up to the RP whether to locally log out the End-User before redirecting the User Agent to the OP's Logout Endpoint. On one hand, if the End-User approves the logout at the OP, the RP initiating the logout should receive a logout message from the OP and can perform a local logout at that time. On the other hand, some logout notification methods from the OP to the RP are unreliable and therefore the notification might not be received. Also, the End-User might not approve the OP logging out, in which case the RP would not receive a logout notification.

This would avoid overwriting / decorating the functionality just to kill the token when you want to disconnect the application manually.

If it's possible, I'll leave it up to you how you implement it.

# In generateEndSessionEndpointRedirect function
# ...

if (null !== $postLogoutRedirectUrl) {
    $data = array_merge($data, [
        'post_logout_redirect_uri' => $postLogoutRedirectUrl,
    ]);
}

###############################################
// Implementation of conditional local logout 
###############################################

$endpointHasQuery = parse_url($this->endSessionEndpoint, PHP_URL_QUERY);

return new RedirectResponse(
    sprintf(
        '%s%s%s',
        $this->endSessionEndpoint,
        $endpointHasQuery ? '&' : '?', http_build_query($data))
);

User identifier from id_token

Hi,

I'm looking to connect to AzureAD OIDC and match users in an LDAP that don't know the sub. I am not able to use the upn claim as the user_identifier_property. The problem seems to be that the upn claim is not present in the userinfo endpoint of AzureAD, but only in the idtoken. Apparently you can't change the data in de userinfo endpoint: https://learn.microsoft.com/en-us/azure/active-directory/develop/userinfo. Yay Microsoft :-(.

"Notes and caveats on the UserInfo endpoint
You can't add to or customize the information returned by the UserInfo endpoint.
To customize the information returned by the identity platform during authentication and authorization, use claims mapping and optional claims to modify security token configuration."
_

I don't see any existing option in the Drenso OIDC library to use data of the idtoken to use as user_identifier_property. I see some solutions:

  1. Add an explicit option that specifies the option to use the idtoken instead of userinfo for the user_identifier_property.
  2. Always merge the data from the idtoken into the OidcUserData jsonData
    public function retrieveUserInfo(OidcTokens $tokens): OidcUserData. If there is an id_token. The idtoken is already passed in inside $token, I think.
  3. Override OidcClient with a custom retrieveUserInfo()` which uses the id_token in the retrieveUserInfo. Which would work for me now and not require altering this library.

What do you think is the way to do this best? Maybe I missed something obvious?

Regards,
Albert-Jan

Check for additional contraints / claims

We need to check some additional constraints / claims. How can I extend the verifyToken function to add an additional constraint (HasClaimWithValue)?

We have to set an acr claim value and therefor we have to check if the same acr value is returned in the tokens.

Upgrade 3.0 bug? The JWT string must have two dots

I have upgraded a previously working setup to v3.0 and now I cannot login. The login_check endpoint 500 errors with The JWT string must have two dots.

I don't believe any of the upgrade notes affected my setup, my SSO provider (Zitadel) was working fine before the upgrade, so presuming tokens are the same. (i.e. no code has been changed, just the upgrade of the package to 3.0)

Is this because the v3.0 utilises the lcobucci/jwt package which is applying stricter validation perhaps?

Lcobucci\JWT\Token\InvalidTokenStructure:
The JWT string must have two dots

  at vendor/lcobucci/jwt/src/Token/InvalidTokenStructure.php:13
  at Lcobucci\JWT\Token\InvalidTokenStructure::missingOrNotEnoughSeparators()
     (vendor/lcobucci/jwt/src/Token/Parser.php:65)

Thank you

Microsoft IdP compatibility

You were right about the rfc. I am sorry, had a different interpretation of the specs.

I've found some information about the difference to the Microsoft stuff and i have two initial questions:

  • Are you interested in supporting Microsoft IdP in general?
  • Would you be willing to accept a clean PR of me?

If so, i would prefer to clarify how a possible solution could look like in first place.

Unfortunately Microsoft does here really something different (wrong!?) than expected.

Here is the Microsoft related problem:

Microsoft uses different issuers for ID Tokens and Access Tokens, which can lead to issues when validating the issuer on tokens. This is a common problem that developers face when working with Microsoft’s identity platform.

ID Tokens
ID Tokens are used for authentication and are issued by the Azure Active Directory (AAD) tenant. The issuer for ID Tokens is typically in the format https://login.microsoftonline.com/{tenant_id}/v2.0. For example, if the tenant ID is 12345678-1234-1234-1234-123456789012, the issuer would be https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0.

Access Tokens
Access Tokens, on the other hand, are used for authorization and are issued by the Azure AD App Registration. The issuer for Access Tokens is typically in the format https://login.microsoftonline.com/{tenant_id}/v2.0/.well-known/openid-configuration. Again, using the same example tenant ID, the issuer would be https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration.

Validation Issues
The main issue that arises from using different issuers for ID Tokens and Access Tokens is that when validating the issuer on tokens, the validation logic needs to be aware of the different issuers used by Microsoft. This can lead to complex and error-prone code, especially when working with multiple tenants or applications.

Conclusion
In conclusion, Microsoft’s use of different issuers for ID Tokens and Access Tokens can lead to issues when validating the issuer on tokens. Developers need to be aware of these differences and implement the necessary logic to handle the different issuers used by Microsoft.

Would be great to discuss a solution.

Add client manager to access available client dynamically

I am trying to have multiple firewalls in Symfony with each their own OIDC config.
In the firewall I specify the client:

        rkd:
            pattern: ^/rkd
            lazy: true
            provider: oidc_user_provider
            oidc:
                client: rkd
                user_identifier_property: 'email'
                login_path: '/rkd/login_oidc'
                check_path: '/rkd/login_check'
            logout:
                path: logout

        admin:
            pattern: ^/admin
            lazy: true
            provider: oidc_user_provider
            oidc:
                client: admin
                user_identifier_property: 'email'
                login_path: '/admin/login_oidc'
                check_path: '/admin/login_check'
            logout:
                path: logout

I have multiple clients defined, which I can load with DI if i know the client name in the DI OIDCClientInterface the login url. But I would like to get a specific named client dynamically.

    default: 
      # Required OIDC client configuration
      well_known_url: '%env(OIDC_WELL_KNOWN_URL)%'
      client_id: '%env(OIDC_CLIENT_ID)%'
      client_secret: '%env(OIDC_CLIENT_SECRET)%'
      redirect_route: '/rkd/login_check'

    admin: 
      # Required OIDC client configuration
      well_known_url: '%env(OIDC_WELL_KNOWN_URL)%'
      client_id: 'cap_rkd'
      client_secret: '%env(OIDC_CLIENT_SECRET)%'
      redirect_route: '/test/login_check'

The problem is with starting the OIDC, I always get the default client in the login controller.

  #[Route('/{slug}/login_oidc', name: 'login_oidc')]
  #[IsGranted('PUBLIC_ACCESS')]
    public function surfconext(Organisation $organisation, OidcClientInterface $client): RedirectResponse
    {
        return $client->generateAuthorizationRedirect();   
    }

How can I best get the specific client in this case?

Make use of PKCE verification

Hi There,

I am trying to implement the bundle with https://logto.io/, which is an OpenID provider. But when I redirect it through the $oidcClient->generateAuthorizationRedirect(); I instantly get an error while being redirected back to my application with the following error: "Authorization Server policy requires PKCE to be used for this request".

For now it is not an option to disable PKCE on the Logto side, is it possible to use PKCE with this bundle?

Thanks in advance.

Azure token verification fails

Hello everyone

I have discovered a problem with token verification with Azure.
I don't know the reason yet and therefore I don't have a fix ready.

I have now built a workaround to move forward, like disable_nonce but for disable_token_verification.
Token verification can be switched off optionally but is switched on by default.

Not validating the token is of course bad and not a real solution, but would you allow this config or not?
Would you like this PR already or should I get back to you when/if I have a fix ready?

If any of you would like to have a look into that too, please feel free.

The error occurs on:

message = "Unable to verify JWT claims"
file = "/var/www/app/vendor/drenso/symfony-oidc-bundle/src/OidcJwtHelper.php"
line = 167

When OidcTokenType = ACCESS
It fails on validating token signature

well_known_parser is never called

Edit : pre-existing well-known data existed in the cache, so the custom parser wasn't called.


Hi,

I need to hook up my app with an OIDC provider that does not supply a "well-known" url, so I was thinking of injecting all the config through a well_known_parser.

drenso_oidc:
    clients:
        default:
            # [well_known_url, client_id, client_secret...]
            well_known_parser: 'App\Security\Oidc\CustomWellKnownParser'

However, after setting one up (I called it CustomWellKnownParser), it's never called. I know it has been taken into account, since I can see it while inspecting the OidcClient in a debugger :

A screenshot of a debugger showing the the customWellKnownParser is part of the OidcClient

(No sensitive information here, it's a local test environment)

To test whether the custom parser was being called, I tried setting a breakpoint there (not hit) and changing the $config array (the change is never reflected).

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.