ansibleguy76 / ansibleforms

A webapplication to create pretty advanced forms to run ansible playbooks or awx templates.
https://ansibleforms.com/
GNU General Public License v3.0
66 stars 10 forks source link

Feature OIDC integration #178

Closed mdaugs closed 3 months ago

mdaugs commented 3 months ago

This PR implements generic Open ID Connect login mechanism via the openid-client package.

I tried to reuse as much from the Azure AD flow as possible. It would be nice to merge the tables and generalize a bit further, but this would need a lot more code modifications.

It was tested against keycloak as identity provider.

AnsibleForms configuration: image

In keycloak, create a realm: image

Then create a client: image

image

image

Get secret key in keycloak: image

Add realm roles client scope for group mapping:

image

mdaugs commented 3 months ago

The changes on package.json are coming from my local setup. Seems to not break anything, but please verify.

Also, please verify, that I didn't break the Azure login mechanism.

ansibleguy76 commented 3 months ago

I have merged and I am now building a new docker image.

I'm not really familiar with OIDC, but you code looks pretty solid. Any chance I can test this with some opensource OID somewhere ?

With my azureAD, I added a little helpers pane, you think it's required for OID ? Some specific roles/features to enable for it to work ? like enabling the mapper "groups" ?

ansibleguy76 commented 3 months ago

Add realm roles client scope for group mapping:

=> I'm stuck at this one. Was able to spin up a keycloak with docker => but I don't find any "mappings"

ansibleguy76 commented 3 months ago

looks like the logout is broken now... it always wants to redirect to keycloak somehow

ansibleguy76 commented 3 months ago

looks like the logout is broken now... it always wants to redirect to keycloak somehow

Fixed this... only doing a "logout" if oidc. The other don't really need an explicit logout, we simple remove the jwt-token and go back to login. to be check if this is even needed for oidc.

ansibleguy76 commented 3 months ago

The changes on package.json are coming from my local setup. Seems to not break anything, but please verify.

Also, please verify, that I didn't break the Azure login mechanism.

AzureAd still working

mdaugs commented 3 months ago

I have merged and I am now building a new docker image.

I'm not really familiar with OIDC, but you code looks pretty solid. Any chance I can test this with some opensource OID somewhere ?

With my azureAD, I added a little helpers pane, you think it's required for OID ? Some specific roles/features to enable for it to work ? like enabling the mapper "groups" ?

You mean the reference guide? Could be useful for users that are not familiar with keycloak. Can you create an issue and assign me? I will tackle this once I have time.

Add realm roles client scope for group mapping:

=> I'm stuck at this one. Was able to spin up a keycloak with docker => but I don't find any "mappings"

The "Mapper" is hidden within the "Client Scope"

image

image

There is a predefined mapper "groups", that maps the realm roles to a groups attribute in the token.

FYI: I've setup keycloak with this docker compose file:

x-logging: &logging
  driver: "json-file"
  options:
    max-size: "10m"
    max-file: "3"

volumes:
  postgres:

services:
  postgres:
    image: postgres:${POSTGRES_VERSION}
    restart: unless-stopped
    container_name: keycloak_db
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "keycloak"]
    environment:
      POSTGRES_DB: keycloak
      POSTGRES_USER: keycloak
      POSTGRES_PASSWORD: password
    volumes:
      - postgres:/var/lib/postgresql/data
    logging: *logging

  keycloak:
    image: quay.io/keycloak/keycloak:${KC_VERSION}
    command: ["start-dev", "--import-realm"]
    restart: unless-stopped
    container_name: keycloak_app
    environment:
      KC_DB: postgres
      KC_DB_USERNAME: keycloak
      KC_DB_PASSWORD: password
      KC_DB_URL: "jdbc:postgresql://postgres:5432/keycloak"
      KC_METRICS_ENABLED: false
      KC_LOG_LEVEL: ${KC_LOG_LEVEL}
      KEYCLOAK_ADMIN: ${KEYCLOAK_ADMIN}
      KEYCLOAK_ADMIN_PASSWORD: ${KEYCLOAK_ADMIN_PASSWORD}
    ports:
      - ${KC_PORT}:8080
    logging: *logging

And this .env file:

# releases: https://www.postgresql.org/docs/release
POSTGRES_VERSION=15.6-alpine

# releases: https://github.com/keycloak/keycloak/releases
KC_VERSION=24.0.4
# The link that will be used by the Grafana to redirect to the Keycloak
KC_HOSTNAME=http://localhost
KC_PORT=8080
KC_LOG_LEVEL=INFO
KEYCLOAK_ADMIN=admin
KEYCLOAK_ADMIN_PASSWORD=keycloak

looks like the logout is broken now... it always wants to redirect to keycloak somehow

Fixed this... only doing a "logout" if oidc. The other don't really need an explicit logout, we simple remove the jwt-token and go back to login. to be check if this is even needed for oidc.

Thanks! In our case, the logout is needed to terminate the keycloak session and log out from all other services in our system.

ansibleguy76 commented 3 months ago

Meanwhile I found the mapper. But I get the error "cannot get groups"

ansibleguy76 commented 3 months ago

I also encounter that the application tries to read the authenticators before the mysql is up... failing to initialize the authenticator

ansibleguy76 commented 3 months ago

Meanwhile I found the mapper. But I get the error "cannot get groups"

Found it, you cannot have Azure and openid at the same time.

          if(token && this.azureAdEnabled){
            this.getGroupsAndLogin(token)
          } else if (token && this.oidcEnabled) {
            this.getGroupsAndLogin(token, `${this.oidcIssuer}/protocol/openid-connect/userinfo`, 'oidc')
          }

=> if azure is enabled, it will try to grab azure groups. I believe we should track what issuer we used.

ansibleguy76 commented 3 months ago

image

=> commiting the code now

ansibleguy76 commented 3 months ago

I also encounter that the application tries to read the authenticators before the mysql is up... failing to initialize the authenticator

This is now fixed as well. If the express app was faster than the mysql, the authenticators failed. AF will now wait for the mysql to be ready.

ansibleguy76 commented 3 months ago

Meanwhile I found the mapper. But I get the error "cannot get groups"

Found it, you cannot have Azure and openid at the same time.

          if(token && this.azureAdEnabled){
            this.getGroupsAndLogin(token)
          } else if (token && this.oidcEnabled) {
            this.getGroupsAndLogin(token, `${this.oidcIssuer}/protocol/openid-connect/userinfo`, 'oidc')
          }

=> if azure is enabled, it will try to grab azure groups. I believe we should track what issuer we used.

is fixed, I store a cookie now to remember what authentication we used.

mdaugs commented 3 months ago

I was pretty busy the last days. Sorry that I couldn't help on fixing your findings.

Nice that you now have all up and running!

The major features needed for our system are now working as we need them :-) If we implement any more features or fixes, we will definetly file a pull request for you.

Thank you for your responsiveness!