Problem/Motivation

When upgrading from openid_connect 1.x to 3.x, some sites encounter a fatal error when attempting to log in:

Symfony\Component\Routing\Exception\InvalidParameterException: Parameter "openid_connect_client" for route
"openid_connect.redirect_controller_redirect" must match "[^/]++" ("" given)

This happens because 3.x switched from plugin-based clients to config entities (see comment #4), and the redirect URL slug is now derived from the config entity machine name. Drupal machine names cannot contain dashes (-), but many OIDC providers (e.g. the Belgian government's ACM/IDM service) use dashes in redirect URIs that were registered during the 1.x era (e.g. /openid-connect/acm-idm). Those registrations cannot be changed on the provider side, so sites are unable to upgrade without breaking their redirect URI (see comment #5, comment #6, comment #17).

Note: a related symptom — parentEntityId being empty — is resolved simply by running drush updatedb after upgrading (see comment #6).

Proposed resolution

Add a separate "slug" field to the OpenID Connect client config entity that decouples the redirect URL identifier from the machine name (first proposed in comment #6, patch attached in comment #19). This allows operators to set a slug containing dashes to match their registered redirect URI at the provider, while keeping the machine name valid per Drupal conventions. When no slug is set, the machine name is used as before (backward-compatible).

The redirect controller is also updated to look up the client by slug rather than requiring a 1:1 match with the machine name.

This approach was reworked into MR !167 (see comment #22) and confirmed working in production by the original reporter (see comment #25).

Remaining tasks

  • ✅ Add/ensure test coverage
  • ✅ Rebase with 3.x
  • Manual testing

User interface changes

A new "Redirect URI slug" field is added to the OpenID Connect client configuration form, allowing a custom string (including dashes) to be used in the redirect URI path.

API changes

None.

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

sboden created an issue. See original summary.

sboden’s picture

Issue summary: View changes
sboden’s picture

I've been debugging a bit in openid_connect.

It seems when moving from v1 to v2 the config data changed names, anyway my config from v1 isn't visible anymore in v2.

So I recreated my config. After that. I have my own client that derives from OpenIDConnectClientBase and that doesn't seem to be able to get its config data, e.g. the parentEntityId (which seems to be filled in in plugin initialization) is "", while the config actually has a value for it.
Strange thing is that I can see my OIDC config data in the $configuration variable which is coming from OpenIDConnectClientBase.

jcnventura’s picture

You probably realized that the major change from 1 to 2 is that the openid plugins are no longer part of the module configuration, but are now "config entities". That means that you can actually re-use the same plugin for different remote services, which was impossible before.

This was a major refactoring of the code, but plugins should not change that much. Please see #2899185: Use config entities for clients.

Check https://git.drupalcode.org/project/openid_connect/-/compare/4b2c6bfe9bae... for the changes done to the Generic client until now.. Note that some of those (autodiscovery of endpoints and the end-session endpoint) are new functionalities that don't have anything to do with the changes needed to make the plugin compliant with the new functionality.

sboden’s picture

Thanks for the information... maybe an entry could be made in the readme file to explain upgrades.

I'm still not out of the woods with the problem but I pinpointed where it is.

But first, I need my own OIDC client since I need a small extension to support a "login_hint" feature. So what I do is make a new client that extends OpenIDConnectClientBase, which is empty besides a copy of the authorize() method from OpenIDConnectClientBase to which I add 2 lines. But for testing purposes I even just extended OpenIDConnectClientBase and removed all methods.

Then I get the error "Symfony\Component\Routing\Exception\InvalidParameterException: Parameter "openid_connect_client" for route "openid_connect.redirect_controller_redirect" must match "[^/]++" ("" given) to generate a corresponding URL. in Drupal\Core\Routing\UrlGenerator->doGenerate() (line 203 of /var/www/html/www/core/lib/Drupal/Core/Routing/UrlGenerator.php)." when I go to my site.
Reason for this is because in the method below the "$this->parentEntityId" is an empty string.

  public function authorize(string $scope = 'openid email', array $additional_params = []): Response {
    $language_none = \Drupal::languageManager()
      ->getLanguage(LanguageInterface::LANGCODE_NOT_APPLICABLE);

    $redirect_uri = Url::fromRoute(
      'openid_connect.redirect_controller_redirect',
      [
        'openid_connect_client' => $this->parentEntityId,
      ],
      [
        'absolute' => TRUE,
        'language' => $language_none,
      ]
    )->toString(TRUE);

    $url_options = [
      'query' => [
        'client_id' => $this->configuration['client_id'],
        'response_type' => 'code',
        'scope' => $scope,
        'redirect_uri' => $redirect_uri->getGeneratedUrl(),
        'state' => $this->stateToken->generateToken(),
      ],
    ];

    if (!empty($additional_params)) {
      $url_options['query'] = array_merge($url_options['query'], $additional_params);
    }

    $endpoints = $this->getEndpoints();
    // Clear _GET['destination'] because we need to override it.
    $this->requestStack->getCurrentRequest()->query->remove('destination');
    $authorization_endpoint = Url::fromUri($endpoints['authorization'], $url_options)->toString(TRUE);

    $this->loggerFactory->get('openid_connect_' . $this->pluginId)->debug('Send authorize request to @url', ['@url' => $authorization_endpoint->getGeneratedUrl()]);
    $response = new TrustedRedirectResponse($authorization_endpoint->getGeneratedUrl());
    // We can't cache the response, since this will prevent the state to be
    // added to the session. The kill switch will prevent the page getting
    // cached for anonymous users when page cache is active.
    $this->pageCacheKillSwitch->trigger();

    return $response;
  }

I compared what the URLs were in openid_connect v1.2.0 and what solves it for me in v3 is this. Replacing $this->parentEntityId by $this->getPluginId() in the authorize() method.

    $redirect_uri = Url::fromRoute(
      'openid_connect.redirect_controller_redirect',
      [
        'openid_connect_client' => $this->getPluginId(),
      ],
      [
        'absolute' => TRUE,
        'language' => $language_none,
      ]
    )->toString(TRUE);

$this->getPluginID() results in my case in "acm-idm" (the system name I used for the plugin, and also the id of the OIDC server). And the URLs I get out of it are:

redirect URI = "https://mysite.be/openid-connect/acm-idm"
authorization url = "https://authentication-server.be/op/v1/auth?client_id=e243df5e-9f0d-4977...

The strange thing is also that on the client config screen the Redirect URL is calculated as "https://mysite.be/openid-connect/acm_idm"... which in my case is wrong: the acm_idm is the system name of the client config (which can't have dashes, while the actual string should "https://mysite.be/openid-connect/acm-idm" (dash instead of underscore), BUT it does find "acm_idm" while during the actual execution that part is always empty. The reason for the discrepancy is that the formbase for the config screen has its own getRedirectUrl() method which calculates the string with the system name of the client config.

So I still have 2 problems:
1. The parentEntityId doesn't get filled in properly. I see it being filled in in the initializePlugin() method of OpenIDConnectClientCollection, but I see no evidence of it actually happening.
2. If parentEntityId would be filled in properly I can't make it work since I need "acm-id" as part of the URIs, and system names in Drupal can't contain a dash, so I can't make an OIDC client system name that I can use with my OIDC server. And I'm only a user of the OIDC server, so I can't change anything to the setup there.

sboden’s picture

Some more debugging.

Number 1. got solved by running "drush updatedb", my bad.

For number 2. I would suggest to introduce a new field and use that instead of the system name of the instance of the plugin in the redirect URL. For backwards compatibility: when the new field is empty the system name could be used anyway. Sole reason is that Drupal system names can't use '-', and some OIDC providers (e.g. the one of the Belgian government) do use a dash ("acm-idm").
For the name of the name field, no idea what it should be called. Right now the redirect parameter is filled in in "parentEntityId" and it gets taken from "client_id", which it not really is in OIDC terminology.

Short term work-around for me is to override the parameter for the redirect URL in my own client, which would actually revert it a bit more like v1.

vensires’s picture

Version: 2.0.0-beta3 » 3.x-dev
Category: Support request » Bug report
Status: Active » Needs work

Since we only have an issue with the dash, we should better only take care of it under the hood.
I'm going to open a MR for this based on v3 of this module. This should be easily applied to v2 though I suppose too.
I also mark it as a bug report too since it's trying to fix a functionality that was working previously - and of course for other developers to also be able to find it more easily.

vensires’s picture

Status: Needs work » Needs review

Setting as ready for review after committing to https://git.drupalcode.org/project/openid_connect/-/merge_requests/84.

kawaljeet singh’s picture

StatusFileSize
new579 bytes

Addding a patch as suggested in #5. This patch will fix this error : Symfony\Component\Routing\Exception\InvalidParameterException: Parameter "openid_connect_client" for route "openid_connect.redirect_controller_redirect" must match "[^/]++" ("" given) to generate a corresponding URL. in Drupal\Core\Routing\UrlGenerator->doGenerate() (line 203 of /var/www/html/www/core/lib/Drupal/Core/Routing/UrlGenerator.php).

maboresev’s picture

I've tested #10 and I can confirm that it works properly in combination with this Keycloak related issue.

johan den hollander’s picture

We're having the #10 patch in use for a while now. Works fine. Small change.

johan den hollander’s picture

Applied the patch against the latest 2.x version.

vensires’s picture

Just to sum up, since the MR is using a param_converter approach, do we all agree that what @sboden proposed in #5 is a better solution here?

So, the issue addressed is fixed by using patch in #13 (while also crediting @kawaljeet-singh for #10)?

jcnventura’s picture

I honestly would need to test this. The plugins changed from being config objects to being config entities. So now you can have multiple Okta plugins installed each with a different name. I'm not 100% sure, but I think that the above only works as long as the machine ID of the config entity is the default value (same as plugin machine name). So if you can, please create two config entities, both with names not being the default from the same plugin and check what this function returns for those entities.

If that is confirmed, then #10 and #13 are basically junk, and the solution should probably be closer to what is proposed in the MR.

johan den hollander’s picture

#13 is junk. I Agree 100% because the patch is malformed. Using the patch will give you errors. Updated the code which should work now.

pfrilling’s picture

Status: Needs review » Postponed (maintainer needs more info)

It's been over a year since the last update here. I'm going to mark this postponed for now. Is anyone able to confirm they are still using the proposed patches?

sboden’s picture

I'm still using patches for this one, but it's becoming a huge patch.

The problem is that we have some government sites that use "acm-idm" as slug in the openid_connect redirect URL, and you can't have a system name in Drupal with a "-" in it (in the very old openid_connect 1.x module, you could get the "right" redirect URL with a "-" in it because the redirect used the plugin-name and not the config system name). I have about 20 Drupal sites integrating with the same openid_connect server and I'm not in control of the redirect URLs.

So it's only a problem when you integrate with an openid_connect server using -'s in the redirect URL.

pfrilling’s picture

Status: Postponed (maintainer needs more info) » Needs work

Thanks @sboden. I'm moving this back to needs work. I'll review further and see about getting this fixed.

sboden’s picture

Attached patch works for me on openid_connect 3.0.0-alpha2 (I can reroll it).

It decouples the config name with the name used in the redirect controller.
Because of that some wiring magic is also needed to convert the other way around: to match config with incoming slug in the redirect controller.

soubi’s picture

Adapted #20 for 3.0.0-alpha6

sboden’s picture

Probably a coincidence. I also adapted #20 for 3.0.0-alpha6. I have been using this patch in production now on 1 site for 2 weeks, no-one is complaining so far.

I did add a small piece from another issue as the edits to the same code made me nervous. This is now the only patch I use for 3.0.0-alpha6

pfrilling’s picture

Status: Needs work » Needs review

Thanks @soubi and @sboden!

I re-worked your patches into a new merge request. Can you review/test these and report your findings? There is one failing test, but it doesn't apply to this MR and is associated with an update hook.

sboden’s picture

Which exact version do I test?

pfrilling’s picture

You would want to test the upgrade from 1.x to 3.x using the code from MR 167.

You can also just use the patch from MR 167 against the -dev version to confirm if everything is working.

sboden’s picture

Seems I was looking at a wrong branch.

I tested the 3 dev version. The provider slug is working. For the Belgian government we usually have 'acm-idm' as slug, you can change that slug when you request the integration; but all the old integrations still use 'acm-idm' of course. And I didn't want to go back and change old integrations.

steinmb’s picture

Title: Upgrade problems openid_connect 1 to 2 » Problems upgrading openid_connect 8.x-1.4 to 3.x

As 2.x no longer looks supported, I assume the supported upgrade path is from 8.x-1.4 to 3.x.

benjifisher’s picture

I am adding the tag for an issue summary update.

Browsing the comments on this issue and the code changes, I am not at all sure what problem this issue is trying to fix. Is it about custom code that integrates with this module or just the process of upgrading to the 3.x branch? If someone can give a clear description of what conditions lead to what problems, then I will be happy to help with this issue: review, test, whatever is needed.

pfrilling’s picture

Assigned: Unassigned » pfrilling
Issue summary: View changes
Status: Needs review » Needs work

I updated the issue summary to more clearly define what the issue was. I also moved to needs work for the required rebase. I want to also ensure test coverage is available.

Assigning to myself to complete that work.

pfrilling’s picture

Issue summary: View changes
pfrilling’s picture

Issue summary: View changes
steinmb’s picture

Thank your for updating the IS and I do not think we need the tag https://www.drupal.org/node/3359789/revisions/view/14321958/14321967.

pfrilling’s picture

Issue summary: View changes
Status: Needs work » Needs review

I rebased and added additional test coverage. My testing seems to be working well. I believe this is ready for another review.