Keystone with SAML IDP, auth works but error 403

Hi, I would like to configure the authentication of my Openstack instance with an external IDP ( Keycloak ) using the subordinate charm “keystone-saml-mellon”.

1 - Issue :

I can log in to Horizon with the admin user on the admin_domain, but I can’t log in to the Horizon dashboard with my external IDP ( Keycloak ), authentication seems to work but Keystone returns a 403 error :
"You are not authorized to perform the requested action."

2 - setup :

  • opentsack is deployed with Juju and Maas.
  • keycloak instance is working.
  • keystone-saml-mellon instance is deployed via the bundle.

3 - My Bundle :

series: focal
applications:
  keystone:
    charm: cs:keystone-323
    channel: stable
    num_units: 1
    to:
    - lxd:6
    options:
      admin-password: password
      debug: true
      openstack-origin: distro
      preferred-api-version: 3
      region: RMV
      token-expiration: 86400
      token-provider: fernet
      worker-multiplier: 0.25
    bindings:
      "": maas-infralab
      admin: maas-infralab
      certificates: maas-infralab
      cluster: maas-infralab
      domain-backend: maas-infralab
      ha: maas-infralab
      identity-admin: maas-infralab
      identity-credentials: maas-infralab
      identity-notifications: maas-infralab
      identity-service: maas-infralab
      internal: maas-infralab
      keystone-fid-service-provider: maas-infralab
      keystone-middleware: maas-infralab
      nrpe-external-master: maas-infralab
      public: maas-infralab
      shared-db: maas-infralab
      websso-trusted-dashboard: maas-infralab
  keystone-mysql-router:
    charm: cs:mysql-router-4
    channel: stable
    bindings:
      "": maas-infralab
      certificates: maas-infralab
      db-router: maas-infralab
      juju-info: maas-infralab
      shared-db: maas-infralab
  mysql-innodb-cluster:
    charm: cs:mysql-innodb-cluster-7
    channel: stable
    num_units: 3
    to:
    - lxd:6
    - lxd:6
    - lxd:6
    options:
      enable-binlogs: true
      innodb-buffer-pool-size: 8G
      max-connections: 4000
      tuning-level: safest
      wait-timeout: 3600
    bindings:
      "": maas-infralab
      certificates: maas-infralab
      cluster: maas-infralab
      coordinator: maas-infralab
      db-router: maas-infralab
      shared-db: maas-infralab
  openstack-dashboard:
    charm: cs:openstack-dashboard-313
    channel: stable
    num_units: 1
    to:
    - lxd:6
    options:
      cinder-backup: false
      endpoint-type: publicURL
      enforce-ssl: false
      neutron-network-l3ha: true
      neutron-network-lb: true
      openstack-origin: distro
      password-retrieve: true
      webroot: /
    constraints: spaces=maas-infralab
    bindings:
      "": maas-infralab
      certificates: maas-infralab
      cluster: maas-infralab
      dashboard-plugin: maas-infralab
      ha: maas-infralab
      identity-service: maas-infralab
      nrpe-external-master: maas-infralab
      public: maas-infralab
      shared-db: maas-infralab
      website: maas-infralab
      websso-fid-service-provider: maas-infralab
      websso-trusted-dashboard: maas-infralab
  openstack-dashboard-mysql-router:
    charm: cs:mysql-router-4
    channel: stable
    bindings:
      "": maas-infralab
      certificates: maas-infralab
      db-router: maas-infralab
      juju-info: maas-infralab
      shared-db: maas-infralab
  public-policy-routing:
    charm: cs:advanced-routing-4
    channel: stable
    options:
      action-managed-update: false
      advanced-routing-config: |-
        [{
            "type": "table",
            "table": "public"
        },{
            "type": "route",
            "default_route": true,
            "gateway": "10.140.8.1",
            "table": "public",
            "metric": "101"
        },{
            "type": "rule",
            "from-net": "10.140.8.0/22",
            "table": "public",
            "priority": "101"
        },{
            "type": "rule",
            "from-net": "10.140.8.0/22",
            "to-net": "10.140.8.0/22"
        }]
      enable-advanced-routing: true
    bindings:
      "": alpha
      juju-info: alpha
  rabbitmq-server:
    charm: cs:rabbitmq-server-110
    channel: stable
    num_units: 1
    to:
    - lxd:6
    options:
      min-cluster-size: 1
      source: distro
    bindings:
      "": maas-infralab
      amqp: maas-infralab
      certificates: maas-infralab
      cluster: maas-infralab
      ha: maas-infralab
      nrpe-external-master: maas-infralab
  keystone-saml-mellon1:
    charm: cs:~openstack-charmers-next/keystone-saml-mellon
    num_units: 0
    options:
      idp-name: 'test-saml-idp1'
      protocol-name: 'mapped'
      user-facing-name: "Keycloak"
      subject-confirmation-data-address-check: False
      nameid-formats: "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
    bindings:
      "": maas-infralab
      certificates: maas-infralab
      container: maas-infralab
      keystone-fid-service-provider: maas-infralab
      websso-fid-service-provider: maas-infralab
  keycloak1:
    charm: cs:ubuntu
    bindings:
      "": maas-infralab
    num_units: 1
    to:
    - lxd:6
machines:
  "6":
    constraints: tags=mellon zones=default
relations:
- - openstack-dashboard:identity-service
  - keystone:identity-service
- - public-policy-routing:juju-info
  - keystone:juju-info
- - public-policy-routing:juju-info
  - openstack-dashboard:juju-info
- - keystone:shared-db
  - keystone-mysql-router:shared-db
- - openstack-dashboard:shared-db
  - openstack-dashboard-mysql-router:shared-db
- - mysql-innodb-cluster:db-router
  - keystone-mysql-router:db-router
- - mysql-innodb-cluster:db-router
  - openstack-dashboard-mysql-router:db-router
- - 'keystone'
  - 'keystone-saml-mellon1'
- - 'openstack-dashboard'
  - 'keystone-saml-mellon1'
- - 'keystone:websso-trusted-dashboard'
  - 'openstack-dashboard:websso-trusted-dashboard'

4 - Keystone :

I have created the following items in Keystone:

  • domain
  • group
  • projects
  • identity provider
  • mapping
  • federation protocol
openstack domain show 61470a6fc93f42a39c35c1c271295329
+-------------+----------------------------------+
| Field       | Value                            |
+-------------+----------------------------------+
| description |                                  |
| enabled     | True                             |
| id          | 61470a6fc93f42a39c35c1c271295329 |
| name        | federated_domain                 |
| options     | {}                               |
| tags        | []                               |
+-------------+----------------------------------+

openstack group show  d3cb5d52bced4a85992469f48a5e292d --domain federated_domain
+-------------+----------------------------------+
| Field       | Value                            |
+-------------+----------------------------------+
| description |                                  |
| domain_id   | 61470a6fc93f42a39c35c1c271295329 |
| id          | d3cb5d52bced4a85992469f48a5e292d |
| name        | federated_users                  |
+-------------+----------------------------------+

openstack project show a0aaabaec75f4e76b65b681486be5410
+-------------+----------------------------------+
| Field       | Value                            |
+-------------+----------------------------------+
| description |                                  |
| domain_id   | 61470a6fc93f42a39c35c1c271295329 |
| enabled     | True                             |
| id          | a0aaabaec75f4e76b65b681486be5410 |
| is_domain   | False                            |
| name        | federated_project                |
| options     | {}                               |
| parent_id   | 61470a6fc93f42a39c35c1c271295329 |
| tags        | []                               |
+-------------+----------------------------------+

openstack identity provider show test-saml-idp1
+-------------------+----------------------------------+
| Field             | Value                            |
+-------------------+----------------------------------+
| authorization_ttl | None                             |
| description       | None                             |
| domain_id         | 61470a6fc93f42a39c35c1c271295329 |
| enabled           | True                             |
| id                | test-saml-idp1                   |
| remote_ids        | test-saml-idp1                   |
+-------------------+----------------------------------+

openstack mapping show samltest_mapping
+-------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Field | Value                                                                                                                                                                                                  |
+-------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| id    | samltest_mapping                                                                                                                                                                                       |
| rules | [{'local': [{'user': {'name': '{0}', 'domain': {'name': 'federated_domain'}}, 'group': {'domain': {'name': 'federated_domain'}, 'name': 'federated_users'}}], 'remote': [{'type': 'MELLON_NAME_ID'}]}] |
+-------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

openstack federation protocol show mapped --identity-provider test-saml-idp1
+---------+------------------+
| Field   | Value            |
+---------+------------------+
| id      | mapped           |
| mapping | samltest_mapping |
+---------+------------------+

My full log output of keystone after a login attempt :

(keystone.server.flask.request_processing.req_logging): 2022-03-01 13:05:11,600 DEBUG REQUEST_METHOD: `GET`
(keystone.server.flask.request_processing.req_logging): 2022-03-01 13:05:11,600 DEBUG SCRIPT_NAME: ``
(keystone.server.flask.request_processing.req_logging): 2022-03-01 13:05:11,600 DEBUG PATH_INFO: `/v3/auth/OS-FEDERATION/identity_providers/test-saml-idp1/protocols/mapped/websso`
(keystone.api._shared.authentication): 2022-03-01 13:05:11,602 DEBUG No 'external' plugin is registered.
(keystone.federation.utils): 2022-03-01 13:05:11,603 DEBUG Environment variables: {'MELLON_NAME_ID': 'karim@cloud.local', 'MELLON_Role': 'default-roles-master;view-profile;uma_authorization;offline_access;manage-account;manage-account-links', 'MELLON_IDP': 'http://10.140.8.45:8080/auth/realms/master', 'GATEWAY_INTERFACE': 'CGI/1.1', 'SERVER_PROTOCOL': 'HTTP/1.1', 'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'origin=http://10.140.8.39/auth/websso/', 'REQUEST_URI': '/v3/auth/OS-FEDERATION/identity_providers/test-saml-idp1/protocols/mapped/websso?origin=http://10.140.8.39/auth/websso/', 'SCRIPT_NAME': '', 'PATH_INFO': '/v3/auth/OS-FEDERATION/identity_providers/test-saml-idp1/protocols/mapped/websso', 'PATH_TRANSLATED': '/usr/bin/keystone-wsgi-public/v3/auth/OS-FEDERATION/identity_providers/test-saml-idp1/protocols/mapped/websso', 'HTTP_HOST': '10.140.8.44:5000', 'HTTP_CONNECTION': 'keep-alive', 'HTTP_CACHE_CONTROL': 'max-age=0', 'HTTP_UPGRADE_INSECURE_REQUESTS': '1', 'HTTP_USER_AGENT': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36', 'HTTP_ACCEPT': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9', 'HTTP_ACCEPT_ENCODING': 'gzip, deflate', 'HTTP_ACCEPT_LANGUAGE': 'fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7', 'HTTP_COOKIE': 'mellon-cookie=c5ac749baa950cd91e0023d02e358222', 'SERVER_SIGNATURE': '<address>Apache/2.4.41 (Ubuntu) Server at 10.140.8.44 Port 5000</address>\n', 'SERVER_SOFTWARE': 'Apache/2.4.41 (Ubuntu)', 'SERVER_NAME': '10.140.8.44', 'SERVER_ADDR': '10.140.8.44', 'SERVER_PORT': '5000', 'REMOTE_ADDR': '10.140.8.44', 'DOCUMENT_ROOT': '/var/www/html', 'REQUEST_SCHEME': 'http', 'CONTEXT_PREFIX': '', 'CONTEXT_DOCUMENT_ROOT': '/var/www/html', 'SERVER_ADMIN': '[no address given]', 'SCRIPT_FILENAME': '/usr/bin/keystone-wsgi-public', 'REMOTE_PORT': '34316', 'REMOTE_USER': 'karim@cloud.local', 'AUTH_TYPE': 'Mellon', 'mod_wsgi.script_name': '', 'mod_wsgi.path_info': '/v3/auth/OS-FEDERATION/identity_providers/test-saml-idp1/protocols/mapped/websso', 'mod_wsgi.process_group': 'keystone-public', 'mod_wsgi.application_group': '', 'mod_wsgi.callable_object': 'application', 'mod_wsgi.request_handler': 'wsgi-script', 'mod_wsgi.handler_script': '', 'mod_wsgi.script_reloading': '1', 'mod_wsgi.listener_host': '', 'mod_wsgi.listener_port': '4990', 'mod_wsgi.enable_sendfile': '0', 'mod_wsgi.ignore_activity': '0', 'mod_wsgi.request_start': '1646139911593747', 'mod_wsgi.request_id': 'E1+u0Ee3gqs', 'mod_wsgi.queue_start': '1646139911594016', 'mod_wsgi.daemon_connects': '1', 'mod_wsgi.daemon_restarts': '0', 'mod_wsgi.daemon_start': '1646139911594458', 'mod_wsgi.script_start': '1646139911595436', 'wsgi.version': (1, 0), 'wsgi.multithread': False, 'wsgi.multiprocess': True, 'wsgi.run_once': False, 'wsgi.url_scheme': 'http', 'wsgi.errors': <_io.TextIOWrapper name='<wsgi.errors>' encoding='utf-8'>, 'wsgi.input': <oslo_middleware.sizelimit.LimitingReader object at 0x7f20b83c2550>, 'wsgi.input_terminated': True, 'wsgi.file_wrapper': <class 'mod_wsgi.FileWrapper'>, 'apache.version': (2, 4, 41), 'mod_wsgi.version': (4, 6, 8), 'mod_wsgi.total_requests': 2, 'mod_wsgi.thread_id': 1, 'mod_wsgi.thread_requests': 2, 'werkzeug.proxy_fix.orig': {'REMOTE_ADDR': '10.140.8.44', 'wsgi.url_scheme': 'http', 'HTTP_HOST': '10.140.8.44:5000', 'SERVER_NAME': '10.140.8.44', 'SERVER_PORT': '5000', 'SCRIPT_NAME': ''}, 'werkzeug.proxy_fix.orig_remote_addr': '10.140.8.44', 'werkzeug.proxy_fix.orig_wsgi_url_scheme': 'http', 'werkzeug.proxy_fix.orig_http_host': '10.140.8.44:5000', 'webob.adhoc_attrs': {'response': <_AuthTokenResponse at 0x7f20b83c2e50 200 OK>}, 'webob.is_body_seekable': False, 'openstack.request_id': 'req-a71cce8e-9aae-4cc3-9d90-8bc6e590b141', 'keystone.token_auth': <keystonemiddleware.auth_token._user_plugin.UserAuthPlugin object at 0x7f20b83c28b0>, 'keystone.oslo_request_context': <keystone.common.context.RequestContext object at 0x7f20b83c24f0>, 'werkzeug.request': <Request 'http://10.140.8.44:5000/v3/auth/OS-FEDERATION/identity_providers/test-saml-idp1/protocols/mapped/websso?origin=http:%2F%2F10.140.8.39%2Fauth%2Fwebsso%2F' [GET]>}
(sqlalchemy.orm.path_registry): 2022-03-01 13:05:11,606 DEBUG set 'memoized_setups' on path 'EntityRegistry((<Mapper at 0x7f20b8a90af0; IdentityProviderModel>,))' to '{}'
(sqlalchemy.pool.impl.QueuePool): 2022-03-01 13:05:11,607 INFO Connection <pymysql.connections.Connection object at 0x7f20b8715310> exceeded timeout; recycling
(sqlalchemy.pool.impl.QueuePool): 2022-03-01 13:05:11,607 DEBUG Closing connection <pymysql.connections.Connection object at 0x7f20b8715310>
(sqlalchemy.pool.impl.QueuePool): 2022-03-01 13:05:11,613 DEBUG Created new connection <pymysql.connections.Connection object at 0x7f20b8715520>
(sqlalchemy.pool.impl.QueuePool): 2022-03-01 13:05:11,616 DEBUG Connection <pymysql.connections.Connection object at 0x7f20b8715520> checked out from pool
(sqlalchemy.orm.path_registry): 2022-03-01 13:05:11,624 DEBUG set 'memoized_setups' on path 'EntityRegistry((<Mapper at 0x7f20b8aa0550; IdPRemoteIdsModel>,))' to '{}'
(sqlalchemy.pool.impl.QueuePool): 2022-03-01 13:05:11,628 DEBUG Connection <pymysql.connections.Connection object at 0x7f20b8715520> being returned to pool
(sqlalchemy.pool.impl.QueuePool): 2022-03-01 13:05:11,629 DEBUG Connection <pymysql.connections.Connection object at 0x7f20b8715520> rollback-on-return, via agent
(sqlalchemy.orm.path_registry): 2022-03-01 13:05:11,630 DEBUG set 'memoized_setups' on path 'EntityRegistry((<Mapper at 0x7f20b8a90af0; IdentityProviderModel>,))' to '{}'
(sqlalchemy.pool.impl.QueuePool): 2022-03-01 13:05:11,631 DEBUG Connection <pymysql.connections.Connection object at 0x7f20b8715520> checked out from pool
(sqlalchemy.pool.impl.QueuePool): 2022-03-01 13:05:11,636 DEBUG Connection <pymysql.connections.Connection object at 0x7f20b8715520> being returned to pool
(sqlalchemy.pool.impl.QueuePool): 2022-03-01 13:05:11,636 DEBUG Connection <pymysql.connections.Connection object at 0x7f20b8715520> rollback-on-return, via agent
(sqlalchemy.orm.path_registry): 2022-03-01 13:05:11,638 DEBUG set 'memoized_setups' on path 'EntityRegistry((<Mapper at 0x7f20b8a900a0; FederationProtocolModel>,))' to '{}'
(sqlalchemy.pool.impl.QueuePool): 2022-03-01 13:05:11,639 DEBUG Connection <pymysql.connections.Connection object at 0x7f20b8715520> checked out from pool
(sqlalchemy.pool.impl.QueuePool): 2022-03-01 13:05:11,643 DEBUG Connection <pymysql.connections.Connection object at 0x7f20b8715520> being returned to pool
(sqlalchemy.pool.impl.QueuePool): 2022-03-01 13:05:11,643 DEBUG Connection <pymysql.connections.Connection object at 0x7f20b8715520> rollback-on-return, via agent
(sqlalchemy.orm.path_registry): 2022-03-01 13:05:11,646 DEBUG set 'memoized_setups' on path 'EntityRegistry((<Mapper at 0x7f20b8af7be0; Service>,))' to '{}'
(sqlalchemy.pool.impl.QueuePool): 2022-03-01 13:05:11,646 DEBUG Connection <pymysql.connections.Connection object at 0x7f20b8715520> checked out from pool
(sqlalchemy.pool.impl.QueuePool): 2022-03-01 13:05:11,650 DEBUG Connection <pymysql.connections.Connection object at 0x7f20b8715520> being returned to pool
(sqlalchemy.pool.impl.QueuePool): 2022-03-01 13:05:11,650 DEBUG Connection <pymysql.connections.Connection object at 0x7f20b8715520> rollback-on-return, via agent
(keystone.server.flask.application): 2022-03-01 13:05:11,657 WARNING You are not authorized to perform the requested action.

My Juju status :

Model      Controller   Cloud/Region          Version  SLA          Timestamp
kbessad-5  juju-liquid  brane-liquid/default  2.9.17   unsupported  14:05:57+01:00

App                               Version  Status  Scale  Charm                 Store       Channel  Rev  OS      Message
keycloak1                         20.04    active      1  ubuntu                charmstore  stable    18  ubuntu
keystone                          17.0.1   active      1  keystone              charmstore  stable   323  ubuntu  Application Ready
keystone-mysql-router             8.0.28   active      1  mysql-router          charmstore  stable     4  ubuntu  Unit is ready
keystone-saml-mellon1             17.0.1   active      1  keystone-saml-mellon  charmstore  stable    50  ubuntu  Unit is ready
mysql-innodb-cluster              8.0.28   active      3  mysql-innodb-cluster  charmstore  stable     7  ubuntu  Unit is ready: Mode: R/O, Cluster is ONLINE and can tolerate up to ONE failure.
openstack-dashboard               18.3.4   active      1  openstack-dashboard   charmstore  stable   313  ubuntu  Unit is ready
openstack-dashboard-mysql-router  8.0.28   active      1  mysql-router          charmstore  stable     4  ubuntu  Unit is ready
public-policy-routing                      active      2  advanced-routing      charmstore  stable     4  ubuntu  Unit is ready
rabbitmq-server                   3.8.2    active      1  rabbitmq-server       charmstore  stable   110  ubuntu  Unit is ready

Unit                                   Workload  Agent  Machine  Public address  Ports           Message
keycloak1/1*                           active    idle   0/lxd/7  10.140.8.45
keystone/0*                            active    idle   0/lxd/0  10.140.8.44     5000/tcp        Unit is ready
  keystone-mysql-router/0*             active    idle            10.140.8.44                     Unit is ready
  keystone-saml-mellon1/0*             active    idle            10.140.8.44                     Unit is ready
  public-policy-routing/0*             active    idle            10.140.8.44                     Unit is ready
mysql-innodb-cluster/0                 active    idle   0/lxd/1  10.140.8.42                     Unit is ready: Mode: R/O, Cluster is ONLINE and can tolerate up to ONE failure.
mysql-innodb-cluster/1*                active    idle   0/lxd/2  10.140.8.43                     Unit is ready: Mode: R/W, Cluster is ONLINE and can tolerate up to ONE failure.
mysql-innodb-cluster/2                 active    idle   0/lxd/3  10.140.8.40                     Unit is ready: Mode: R/O, Cluster is ONLINE and can tolerate up to ONE failure.
openstack-dashboard/0*                 active    idle   0/lxd/4  10.140.8.39     80/tcp,443/tcp  Unit is ready
  openstack-dashboard-mysql-router/0*  active    idle            10.140.8.39                     Unit is ready
  public-policy-routing/1              active    idle            10.140.8.39                     Unit is ready
rabbitmq-server/0*                     active    idle   0/lxd/5  10.140.8.41     5672/tcp        Unit is ready

Machine  State    DNS          Inst id              Series  AZ       Message
0        started  10.140.8.38  mellon-latest        focal   default  Deployed
0/lxd/0  started  10.140.8.44  juju-00960e-0-lxd-0  focal   default  Container started
0/lxd/1  started  10.140.8.42  juju-00960e-0-lxd-1  focal   default  Container started
0/lxd/2  started  10.140.8.43  juju-00960e-0-lxd-2  focal   default  Container started
0/lxd/3  started  10.140.8.40  juju-00960e-0-lxd-3  focal   default  Container started
0/lxd/4  started  10.140.8.39  juju-00960e-0-lxd-4  focal   default  Container started
0/lxd/5  started  10.140.8.41  juju-00960e-0-lxd-5  focal   default  Container started
0/lxd/7  started  10.140.8.45  juju-00960e-0-lxd-7  focal   default  Container started

Any help will be welcome,
Thank you

Did you add a role for the federated domains? e.g.

$ openstack role add --group federated_users --domain federated_domain Member
$ openstack role add --group federated_users --project federated_project Member

Hi Billy, thank you for your help.

Yes, I had added a role for the federated domains.

Here is my history :

$ openstack domain create federated_domain
$ openstack project create --domain federated_domain federated_project
$ openstack group create federated_users --domain federated_domain
$ openstack group show federated_users --domain federated_domain
+-------------+----------------------------------+
| Field       | Value                            |
+-------------+----------------------------------+
| description |                                  |
| domain_id   | 61470a6fc93f42a39c35c1c271295329 |
| id          | d3cb5d52bced4a85992469f48a5e292d |
| name        | federated_users                  |
+-------------+----------------------------------+
$ openstack role add --group d3cb5d52bced4a85992469f48a5e292d --domain federated_domain member
$ openstack role add --group d3cb5d52bced4a85992469f48a5e292d --project federated_project member
$ openstack identity provider create --remote-id test-saml-idp1  --domain federated_domain test-saml-idp1

I’m not sure what is going on but if this helps I followed this example using keycloak as my provider. https://cloud.garr.it/support/kb/cloud/federated_auth_juju/

The main thing is to make sure your certs match and that the remote-ID points to the correct url other wise you get many many errors. Another guide I used was the readme from the source code https://opendev.org/openstack/charm-keystone-saml-mellon/src/branch/master/src/README.md

I arrived at this very same breakpoint. Configured on Focal-Xena Openstack.
Keystone only returns a 403 when IdP (AzureAD) replies back.

Currently looking at debugging Keystone and Saml-Mellon, followed Garr.it guide and referenced README.