IdentityPython / idpy-oidc

Implementation of everything OIDC and OAuth2
Apache License 2.0
37 stars 21 forks source link

id_token_signing_alg_values_supported hard-coded to RS256 #110

Open jinnatar opened 1 month ago

jinnatar commented 1 month ago

The verify code at: https://github.com/IdentityPython/idpy-oidc/blob/main/src/idpyoidc/message/oidc/__init__.py#L945 hardcodes checking that the OP supports RS256 for id_token_signing. There doesn't seem to be any config option that would allow overriding this and thus OPs that on purpose require better algorithms cannot be supported. In my case Kanidm requires ES256 be used, and signals so in discovery.

Defining a config item id_token_signing_alg_values_supported is respected and reflected in the entity registration, but said registered entity is apparently not used when checking what signing algo should be supported.

Full example that fails against Kanidm when used via SaToSa as a backend:

---
module: satosa.backends.idpy_oidc.IdpyOIDCBackend
name: !ENV OIDC_NAME
config:
  client_type: oidc
  client:
    provider_info:
      issuer: !ENV OIDC_ISSUER_URL

    scopes_supported: [openid, profile, email]
    response_types_supported: [code]
    id_token_signing_alg_values_supported: [ES256]
    subject_types_supported: [public]

    client_id: !ENV OIDC_CLIENT_ID
    client_secret: !ENV OIDC_CLIENT_SECRET
    redirect_uris: [<base_url>/<name>]

Failure via SaToSa load:

[2024-08-06 11:51:29 +0000] [1] [INFO] Starting gunicorn 22.0.0
[2024-08-06 11:51:29 +0000] [1] [INFO] Listening at: http://0.0.0.0:80 (1)
[2024-08-06 11:51:29 +0000] [1] [INFO] Using worker: sync
[2024-08-06 11:51:29 +0000] [7] [INFO] Booting worker with pid: 7
[2024-08-06 11:51:29,527][INFO][satosa.proxy_server.make_app] Running SATOSA version 8.4.0
[2024-08-06 11:51:29,527][INFO][satosa.base.__init__] Loading backend modules...
[2024-08-06 11:51:29,710][DEBUG][idpyoidc.client.claims.transform.preferred_to_registered] Entity registered: {'client_id': 'ceph', 'client_secret': 'xxxxx', 'redirect_uris': ['saml.example.com/kanidm_ceph'], 'response_types': ['code'], 'grant_types': ['authorization_code', 'refresh_token'], 'application_type': 'web', 'subject_type': 'public', 'id_token_signed_response_alg': 'ES256', 'userinfo_signed_response_alg': 'RS256', 'request_object_signing_alg': 'RS256', 'token_endpoint_auth_method': 'client_secret_basic', 'token_endpoint_auth_signing_alg': 'RS256', 'default_max_age': 86400, 'response_modes': ['query', 'fragment', 'form_post'], 'scope': ['openid', 'profile', 'email'], 'callback_uris': {'redirect_uris': {'query': ['saml.example.com/kanidm_ceph']}}, 'encrypt_request_object_supported': False, 'encrypt_userinfo_supported': False}
[2024-08-06 11:51:29,710][DEBUG][idpyoidc.client.oauth2.stand_alone_client.do_provider_info] ******************** do_provider_info ********************
[2024-08-06 11:51:29,711][DEBUG][idpyoidc.client.oauth2.dynamic_provider_info_discovery] provider_info
[2024-08-06 11:51:29,711][DEBUG][idpyoidc.client.oauth2.do_request] do_request info: {'url': 'https://idm.example.com/oauth2/openid/ceph/.well-known/openid-configuration', 'method': 'GET'}
[2024-08-06 11:51:29,711][DEBUG][idpyoidc.client.oauth2.service_request] Doing request with: URL:https://idm.example.com/oauth2/openid/ceph/.well-known/openid-configuration, method:GET, data:None, https_args:{}
[2024-08-06 11:51:29,712][DEBUG][urllib3.connectionpool._new_conn] Starting new HTTPS connection (1): idm.example.com:443
[2024-08-06 11:51:29,746][DEBUG][urllib3.connectionpool._make_request] https://idm.example.com:443 "GET /oauth2/openid/ceph/.well-known/openid-configuration HTTP/11" 200 1019
[2024-08-06 11:51:29,746][DEBUG][idpyoidc.client.oauth2.parse_request_response] response_body_type: "json"
[2024-08-06 11:51:29,746][DEBUG][idpyoidc.client.util.get_deserialization_method] resp.headers: {'Access-Control-Allow-Origin': '*', 'Cache-Control': 'no-store no-cache max-age=0', 'Content-Length': '1019', 'Content-Security-Policy': "base-uri 'self' https:; default-src 'self'; form-action 'self' https:; frame-ancestors 'none'; img-src 'self' data:; script-src 'self' 'unsafe-eval' 'sha384-xxx' 'sha384-xxx' 'sha384-xxx' 'sha384-xxx' 'sha384-xxx' 'sha384-xxx'; worker-src 'none';", 'Content-Type': 'application/json', 'Date': 'Tue, 06 Aug 2024 11:51:29 GMT', 'Permissions-Policy': 'fullscreen=(), geolocation=()', 'Pragma': 'no-cache', 'Referrer-Policy': 'no-referrer-when-downgrade', 'Set-Cookie': 'idm-prod-sticky=xxx; Path=/', 'Strict-Transport-Security': 'max-age=86400', 'X-Content-Type-Options': 'nosniff', 'X-Kanidm-Opid': '6da1850f-4012-45fa-8825-b91bfc9f6c3f', 'X-Kanidm-Version': '1.2.2'}
[2024-08-06 11:51:29,746][DEBUG][idpyoidc.client.util.get_deserialization_method] resp.txt: {"issuer":"https://idm.example.com/oauth2/openid/ceph","authorization_endpoint":"https://idm.example.com/ui/oauth2","token_endpoint":"https://idm.example.com/oauth2/token","userinfo_endpoint":"https://idm.example.com/oauth2/openid/ceph/userinfo","jwks_uri":"https://idm.example.com/oauth2/openid/ceph/public_key.jwk","scopes_supported":["email","openid","profile"],"response_types_supported":["code"],"response_modes_supported":["query"],"grant_types_supported":["authorization_code"],"subject_types_supported":["public"],"id_token_signing_alg_values_supported":["ES256"],"token_endpoint_auth_methods_supported":["client_secret_basic","client_secret_post"],"display_values_supported":["page"],"claim_types_supported":["normal"],"service_documentation":"https://kanidm.github.io/kanidm/master/integrations/oauth2.html","claims_parameter_supported":false,"request_parameter_supported":false,"request_uri_parameter_supported":false,"require_request_uri_registration":false,"code_challenge_methods_supported":["S256"]}
[2024-08-06 11:51:29,746][DEBUG][idpyoidc.client.oauth2.parse_request_response] Successful response: {"issuer":"https://idm.example.com/oauth2/openid/ceph","authorization_endpoint":"https://idm.example.com/ui/oauth2","token_endpoint":"https://idm.example.com/oauth2/token","userinfo_endpoint":"https://idm.example.com/oauth2/openid/ceph/userinfo","jwks_uri":"https://idm.example.com/oauth2/openid/ceph/public_key.jwk","scopes_supported":["email","openid","profile"],"response_types_supported":["code"],"response_modes_supported":["query"],"grant_types_supported":["authorization_code"],"subject_types_supported":["public"],"id_token_signing_alg_values_supported":["ES256"],"token_endpoint_auth_methods_supported":["client_secret_basic","client_secret_post"],"display_values_supported":["page"],"claim_types_supported":["normal"],"service_documentation":"https://kanidm.github.io/kanidm/master/integrations/oauth2.html","claims_parameter_supported":false,"request_parameter_supported":false,"request_uri_parameter_supported":false,"require_request_uri_registration":false,"code_challenge_methods_supported":["S256"]}
[2024-08-06 11:51:29,747][DEBUG][idpyoidc.client.service.parse_response] response format: json
[2024-08-06 11:51:29,747][DEBUG][idpyoidc.client.service.parse_response] response_cls: ProviderConfigurationResponse
[2024-08-06 11:51:29,747][DEBUG][idpyoidc.client.service.parse_response] Initial response parsing => "{'version': '3.0', 'token_endpoint_auth_methods_supported': ['client_secret_basic', 'client_secret_post'], 'claims_parameter_supported': False, 'request_parameter_supported': False, 'request_uri_parameter_supported': False, 'require_request_uri_registration': False, 'grant_types_supported': ['authorization_code'], 'issuer': 'https://idm.example.com/oauth2/openid/ceph', 'authorization_endpoint': 'https://idm.example.com/ui/oauth2', 'token_endpoint': 'https://idm.example.com/oauth2/token', 'userinfo_endpoint': 'https://idm.example.com/oauth2/openid/ceph/userinfo', 'jwks_uri': 'https://idm.example.com/oauth2/openid/ceph/public_key.jwk', 'scopes_supported': ['email', 'openid', 'profile'], 'response_types_supported': ['code'], 'response_modes_supported': ['query'], 'subject_types_supported': ['public'], 'id_token_signing_alg_values_supported': ['ES256'], 'display_values_supported': ['page'], 'claim_types_supported': ['normal'], 'service_documentation': 'https://kanidm.github.io/kanidm/master/integrations/oauth2.html', 'code_challenge_methods_supported': ['S256']}"
[2024-08-06 11:51:29,747][DEBUG][idpyoidc.client.service.parse_response] Verify response with {'iss': 'https://idm.example.com/oauth2/openid/ceph', 'keyjar': <KeyJar(issuers=['ceph', ''])>, 'verify': True, 'client_id': 'ceph'}
[2024-08-06 11:51:29,747][ERROR][idpyoidc.client.service.parse_response] Got exception while verifying response: RS256 missing from id_token_signing_alg_values_supported
[2024-08-06 11:51:29,747][ERROR][idpyoidc.client.oauth2.parse_request_response] RS256 missing from id_token_signing_alg_values_supported
[2024-08-06 11:51:29,748][ERROR][satosa.proxy_server.make_app] Failed to create WSGI app.
Traceback (most recent call last):
  File "/usr/local/lib/python3.12/site-packages/satosa/proxy_server.py", line 197, in make_app
    res1 = WsgiApplication(satosa_config)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/satosa/proxy_server.py", line 119, in __init__
    super().__init__(config)
  File "/usr/local/lib/python3.12/site-packages/satosa/base.py", line 56, in __init__
    backends = load_backends(self.config, self._auth_resp_callback_func,
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/satosa/plugin_loader.py", line 44, in load_backends
    backend_modules = _load_plugins(
                      ^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/satosa/plugin_loader.py", line 181, in _load_plugins
    instance = module_class(callback, internal_attributes, module_config, base_url,
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/satosa/backends/idpy_oidc.py", line 52, in __init__
    self.client.do_provider_info()
  File "/usr/local/lib/python3.12/site-packages/idpyoidc/client/oauth2/stand_alone_client.py", line 69, in do_provider_info
    dynamic_provider_info_discovery(self, behaviour_args=behaviour_args)
  File "/usr/local/lib/python3.12/site-packages/idpyoidc/client/oauth2/__init__.py", line 339, in dynamic_provider_info_discovery
    response = client.do_request(service, behaviour_args=behaviour_args)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/idpyoidc/client/oauth2/__init__.py", line 136, in do_request
    return self.service_request(
           ^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/idpyoidc/client/oauth2/__init__.py", line 223, in service_request
    response = _get_response_func(
               ^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/idpyoidc/client/oauth2/__init__.py", line 190, in get_response
    return self.parse_request_response(service, resp, response_body_type, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/idpyoidc/client/oauth2/__init__.py", line 273, in parse_request_response
    return service.parse_response(reqresp.text, value_type, state, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/idpyoidc/client/service.py", line 671, in parse_response
    resp.verify(**vargs)
  File "/usr/local/lib/python3.12/site-packages/idpyoidc/message/oidc/__init__.py", line 946, in verify
    raise ValueError("RS256 missing from id_token_signing_alg_values_supported")
ValueError: RS256 missing from id_token_signing_alg_values_supported
[2024-08-06 11:51:29 +0000] [1] [ERROR] Worker (pid:7) exited with code 3
[2024-08-06 11:51:29 +0000] [1] [ERROR] Shutting down: Master
[2024-08-06 11:51:29 +0000] [1] [ERROR] Reason: Worker failed to boot.
jinnatar commented 1 month ago

For testing I changed the line to instead look for ES256. This allows validation to pass and auth works fine after this, even if I don't manually specify id_token_signing_alg_values_supported.

If the check is necessary, it should be expanded to other algos, or perhaps take the OP discovery id_token_signing_alg_values_supported and check for the algos given there.

Ooor, the smallest dumbest fix would be to check for either RS256 or ES256, I don't have the context for why this check was necessary in the first place. :-)