ruby-on-railsrubyopenid-connectomniauth

omniauth_openid_connect gem - Authentication failure! invalid_request: Rack::OAuth2::Client::Error, invalid_request :: Client credentials are invalid


Im using this gem to add Omniauth OpenID with a provider.

I configured the gem in the Devise Initializer, everything seems to be correct:

config.omniauth :openid_connect,
  {
      name: :openid_connect,
      scope: %i[openid profile groups_rewardops scope_rewardops],
      issuer: ConfigSettings.desjardins.issuer_url,
      response_type: :code,
      uid_field: 'sub',
      response_mode: :query,
      discovery: true,
      send_scope_to_token_endpoint: false,
      client_options:
      {
        port: 443,
        scheme: "https",
        host: ConfigSettings.desjardins.host,
        authorization_endpoint: "/affwebservices/CASSO/oidc/rewardops/authorize",
        token_endpoint: "/affwebservices/CASSO/oidc/rewardops/token",
        userinfo_endpoint: "/affwebservices/CASSO/oidc/rewardops/userinfo",
        identifier: ConfigSettings.desjardins.client_id,
        secret: ConfigSettings.desjardins.client_secret,
        redirect_uri: "#{ConfigSettings.api.base_url}front_end/users/auth/openid_connect/callback",
      },
  }

The flow I have atm is that the user can log in and grant access from the provider, then the provider sends a request to my devise callback url with the nonce, code and state. At this point everything seems to be correct but that request ends in failure when trying to generate the access_token with the following error:

ERROR -- omniauth: (openid_connect) Authentication failure! invalid_request: Rack::OAuth2::Client::Error, invalid_request :: Client credentials are invalid.

Im sure the identifier and the secret are correct, don't understand what's going on.

Since Im using discovery mode all the configs of the provider are in the .well-known you can check it here

Im blocked without ideas about how to debug the error. Checking at Rack::OAuth2 to see where the error is comming from I found this that says:

 invalid_request: "The request is missing a required parameter, includes an unsupported parameter or parameter value, repeats the same parameter, uses more than one method for including an access token, or is otherwise malformed.",

It seems for some reason the access token request is malformed, but not sure what else apart of identifier and secret should I have in mind? I have seen many other examples of configuration and mine seems to be correct.


Solution

  • Since you are sure your credentials are correct, I suspect there is mismatch between the authentication method being used and the methods supported by the provider. Checking the .well-known config, I see this provider only supports client_secret_post. In your omniauth config, I see no options being passed to specify the authentication method. When I dive down into the code, I see that the underlying oauth2 gem defaults to using basic auth, which uses the indentifier and secret to construct an Authentication header. See: source code here

    client_auth_method = args.first || options.delete(:client_auth_method).try(:to_sym) || :basic
    
        case client_auth_method
        when :basic
          cred = Base64.strict_encode64 [
            Util.www_form_url_encode(identifier),
            Util.www_form_url_encode(secret)
          ].join(':')
          headers.merge!(
            'Authorization' => "Basic #{cred}"
          )
    

    In the client_secret_post authentication method, instead of providing client secret in the header, the client authorizes itself providing the secret in the HTTP request body as a form parameter. So this provider is not seeing your credentials. You could verify this by looking at the logs of the token endpoint request, which won't be visible in the browser, but rather from your rails BE to the the provider's server.

    Try passing a client_auth_method in the client_options hash in your omniauth config. If you look at the case statement in the code I linked to above, there doesn't seem to be a named option for client_secret_post, but it is the default case. Any value for client_auth_method looks like it would work, but I would still use client_secret_post.