Skip to content

User Authentication and External Identity Providers

Learn about user authentication and connecting with external identity providers in deployKF.

Related Guides

In addition to configuring how users are authenticated, you will likely want to authorize them by assigning to profiles. Note, the level of access a user has is directly determined by which profiles they are a member of.


Introduction

deployKF provides a very flexible approach to user authentication.

All user-facing components that require authentication are connected with the embedded Dex instance. Most components connect to Dex via OAuth2 Proxy with Istio EnvoyFilters (e.g. Kubeflow), but some components connect with Dex directly (e.g. MinIO, Argo Server).

When a user needs to be authenticated, they will be redirected to Dex, which then authenticates them using one (or more) of the following methods:

Authentication Method
(Click for Details)
Description
External Identity Providers Connect with external identity providers like Active Directory, Okta, GitHub, Google.
Static User/Password Combinations Define a list of static user/password combinations that are local to deployKF.

These credentials are commonly used as "service accounts" for things like Accessing the Kubeflow Pipelines API, but may also be used for regular users if you don't have an external identity provider.

External Identity Providers

Typically, organizations will have an existing identity provider like Okta, GitHub, or Google.

Dex provides connectors for many external identity providers. The following table lists some common identity providers and the Dex connector you should use to connect with them:

Provider Name
(Click for Example)
Dex Connector Type
Active Directory (LDAP) ldap
AWS Cognito oidc
GitHub github
Google Workspace google
Microsoft Identity Platform microsoft
Okta oidc
OneLogin oidc
Keycloak oidc
Generic (OpenID Connect) oidc

SAML 2.0

You should NOT use the saml connector, as it does not support refresh tokens, so users would be forced to re-login every 60 minutes. Most identity providers with SAML 2.0 also have OpenID Connect (OIDC), which supports refresh tokens.

Groups

Currently, deployKF does not use the groups claim from providers. Within deployKF, groups are virtual constructs which are defined with the profile values.

Connector Values

The deploykf_core.deploykf_auth.dex.connectors value configures the list of Dex connectors which are available for user authentication. You may define multiple connectors, users will be prompted to choose one when they login.

The generic structure of a connector list element is as follows:

deploykf_core:
  deploykf_auth:
    dex:
      connectors:
        - ## the connector type (ldap, github, oidc, etc.)
          type: <connector-type>

          ## identifier for the connector (any string)
          id: <connector-id>

          ## human-readable name for the connector (any string)
          name: <connector-name>

          ## (OPTION 1):
          ##  - set the config with values (NOT RECOMMENDED)
          ##  - see provider guides for examples
          config:
            config-key-1: config-value-1
            config-key-2: config-value-2
            config-key-3: config-value-3

          ## (OPTION 2):
          ##  - read a kubernetes secret from the 'deploykf-auth' namespace
          ##  - using this completely overrides the `config` map above
          #configExistingSecret: "my-secret-name"
          #configExistingSecretKey: "key-in-secret-with-config-yaml-string"
How do I use configExistingSecret?

To use the configExistingSecret option, you must create a Kubernetes secret with the config values.

The secret must be created in the deploykf-auth namespace, and the configExistingSecretKey key in the secret must contain a string of YAML which is formatted the same as the config map key above.

Note, the type, id, and name fields are defined in the connector list element, NOT in the secret.

For example, you may create a secret like this:

kind: Secret
apiVersion: v1
metadata:
  name: my-secret-name
  namespace: deploykf-auth
## NOTE: you may also use `data` if you base64 encode the string
stringData:
  key-in-secret-with-config-yaml-string: |
    config-key-1: config-value-1
    config-key-2: config-value-2
    config-key-3: config-value-3

The following values will then make use of the secret:

deploykf_core:
  deploykf_auth:
    dex:
      connectors:
        - type: <connector-type>
          id: <connector-id>
          name: <connector-name>
          configExistingSecret: "my-secret-name"
          configExistingSecretKey: "key-in-secret-with-config-yaml-string"
Can I use External Secrets Operator?

If you are using External Secrets Operator, you can easily include the ExternalSecret manifest in your deployKF values. The deploykf_core.deploykf_auth.extraManifests value will include additional manifests in the deploykf-auth namespace.

For example, the following values will create an ExternalSecret named my-secret-name:

deploykf_core:
  deploykf_auth:
    extraManifests:
      - |
        apiVersion: external-secrets.io/v1beta1
        kind: ExternalSecret
        metadata:
          name: my-secret-name
          namespace: deploykf-auth
        spec:
          refreshInterval: "60s"

          ## the secret store to read from
          secretStoreRef:
            name: my-secret-store
            kind: SecretStore

          ## the secret to be created
          target:
            ## the `configExistingSecret` would be this secret
            name: my-secret-name

            ## NOTE: we wrap templates in {{ `...` }} to stop helm from parsing them
            template:
              data:
                ## the `configExistingSecretKey` would be this key
                google-config: |
                  clientID: {{ `{{ .clientID | quote }}` }}
                  clientSecret: {{ `{{ .clientSecret | quote }}` }}
                  redirectURI: "https://deploykf.example.com/dex/callback"

          ## data to read from the secret store
          ##  - key: the secret to read from the secret store
          ##  - property: usually a GJSON expression to extract the value from the secret
          data:
            - secretKey: clientID
              remoteRef:
                key: /deploykf/auth/google-oidc
                property: clientID

            - secretKey: clientSecret
              remoteRef:
                key: /deploykf/auth/google-oidc
                property: clientSecret

Provider Examples

The following guides show provider-specific instructions for configuring Dex connectors:

Active Directory (LDAP)
Active Directory (LDAP)

How to connect deployKF with Active Directory will depend on the structure of your Active Directory:

The following values act as a good starting point for connecting deployKF with LDAP:

deploykf_core:
  deploykf_auth:
    dex:
      connectors:
        - type: ldap
          id: ldap
          name: LDAP
          config:
            ## the LDAP server URL and port
            host: ldap.example.com:636

            ## if we should skip TLS certificate verification
            ## WARNING: this is insecure, prefer to use `rootCAData`
            insecureSkipVerify: false

            ## a base64 encoded PEM file with your root CA certificate
            #rootCAData: ""

            ## the credentials to use for searching the directory
            ## NOTE: not required if anonymous bind is allowed
            #bindDN: uid=Administrator,cn=Users,dc=example,dc=com
            #bindPW: password

            ## text used in password-prompt box
            usernamePrompt: "Username"

            ## how users are searched for, based on the provided username
            userSearch:

              ## base search from
              baseDN: cn=Users,dc=example,dc=com

              ## an additional search filter to apply
              filter: "(objectClass=person)"

              ## how to map user attributes
              username: uid
              idAttr: uid
              emailAttr: mail
              nameAttr: name

            ## how groups are searched for, based on the found user
            groupSearch:
              ## base search from
              baseDN: cn=Groups,dc=example,dc=com

              ## an additional search filter to apply
              ## WARNING: if too many groups are returned, you will exceed the HTTP header size limit.
              ##          you may need to apply a filter like: "(&(objectClass=group)(cn=*deployKF*))" 
              ##          which will only return groups with "deployKF" in the name. However, note that
              ##          deployKF does not currently use these groups, so you are mostly preparing 
              ##          for the future.
              filter: "(objectClass=group)"

              ## list of user-attribute to group-attribute pairs
              ## the value of the found user's `userAttr` must be one of the group's `groupAttr`
              userMatchers:
                - userAttr: uid
                  groupAttr: member

              ## how to map group attributes
              nameAttr: cn

Restricting Access to Members of Specific Group

deployKF does not currently use groups from providers. If you need to restrict deployKF to members of specific group, you must extend the userSearch.filter to include a group membership check.

For example, if users are in the cn=Users,dc=example,dc=com OU, and you want to restrict access to members of the cn=deploykf,ou=Groups,dc=example,dc=com group, you could use the following:

userSearch:
  baseDN: cn=Users,dc=example,dc=com
  filter: "(&(objectClass=person)(memberOf=cn=deploykf,ou=Groups,dc=example,dc=com))"

This pattern could be extended to support multiple groups by using (|(memberOf=...)(memberOf=...)).

Use Kubernetes Secrets

Consider using configExistingSecret instead of config to avoid storing secrets in your values, see the Connector Values section for more details.

AWS Cognito
AWS Cognito

To connect deployKF with AWS Cognito, you must first create an "App Client" in AWS Cognito:

The following values will connect deployKF with your AWS Cognito application:

deploykf_core:
  deploykf_auth:
    dex:
      connectors:
        - type: oidc
          id: aws-cognito
          name: AWS Cognito
          config:
            issuer: https://cognito-idp.<AWS_REGION>.amazonaws.com/<USER_POOL_ID>

            clientID: "XXXXXXXXXXXXXXXXXXXX"
            clientSecret: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"

            ## replace with your deploykf domain
            ## NOTE: this must be an allowed redirect URI in the AWS Cognito app
            redirectURI: https://deploykf.example.com/dex/callback

            ## openid scopes to request
            scopes:
              - openid
              - email
              - profile

            ## cognito does not send the `name` claim
            userNameKey: cognito:username

            ## cognito does not always send the `email_verified` claim
            insecureSkipEmailVerified: true

Use Kubernetes Secrets

Consider using configExistingSecret instead of config to avoid storing secrets in your values, see the Connector Values section for more details.

GitHub
GitHub

To connect deployKF with GitHub, you must create an OAuth application in GitHub:

The following values will connect deployKF with your GitHub application:

deploykf_core:
  deploykf_auth:
    dex:
      connectors:
        - type: github
          id: github
          name: GitHub
          config:
            clientID: "XXXXXXXXXXXXXXXXXXXX"
            clientSecret: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"

            ## replace with your deploykf domain
            ## NOTE: this must be an allowed redirect URI in the GitHub app
            redirectURI: https://deploykf.example.com/dex/callback

            ## a list of GitHub organizations and teams to allow users from
            orgs:
              ## allow ALL users from "my-organization"
              - name: "my-organization"

              ## allow users from "my-organization" who are in the listed teams
              #- name: "my-organization"
              #  teams:
              #    - "red-team"
              #    - "blue-team"

            ## how the team names are formatted
            ##  - If "name", the full team name is used (default)
            ##    EXAMPLE: "Site Reliability Engineers"
            ##  - If "slug", the slug of the team name is used
            ##    EXAMPLE: "site-reliability-engineers"
            teamNameField: slug

            ## how to determine the user's email
            ##  - WARNING: tell users to REMOVE any PUBLIC emails from their 
            ##    GitHub profiles, currently, public emails override this setting.
            ##  - When 'preferredEmailDomain' is set, the first email matching this 
            ##    domain is returned, falling back to the primary email if no match is found.
            ##  - To allow multiple subdomains, you may specify a wildcard like "*.example.com"
            ##    which will match "aaaa.example.com" and "bbbb.example.com", but NOT "example.com".
            #preferredEmailDomain: "example.com"

            ## only required for GitHub Enterprise
            #hostName: github.example.com

Use Kubernetes Secrets

Consider using configExistingSecret instead of config to avoid storing secrets in your values, see the Connector Values section for more details.

Google Workspace
Google Workspace

To connect deployKF with Google Workspace, you must register an application in the Google API Console:

The following values will connect deployKF with your Google Workspace:

deploykf_core:
  deploykf_auth:
    dex:
      connectors:
        - type: google
          id: google
          name: Google
          config:
            clientID: "XXXXXXXXXXXXXXXXXXXX"
            clientSecret: "XXXXXXXXXXXXXXXXXXXXXXXXX"

            ## replace with your deploykf domain
            ## NOTE: this must be an allowed redirect URI in the Google app
            redirectURI: https://deploykf.example.com/dex/callback

Use Kubernetes Secrets

Consider using configExistingSecret instead of config to avoid storing secrets in your values, see the Connector Values section for more details.

Microsoft Identity Platform
Microsoft Identity Platform

To connect deployKF with Microsoft Identity Platform, register an application in Azure:

The following values will connect deployKF with your Microsoft Identity Platform application:

deploykf_core:
  deploykf_auth:
    dex:
      connectors:
        - type: microsoft
          id: microsoft
          name: Microsoft
          config:
            clientID: "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
            clientSecret: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"

            ## replace with your deploykf domain
            ## NOTE: this must be an allowed redirect URI in the Okta app
            redirectURI: "https://deploykf.example.com/dex/callback"

Use Kubernetes Secrets

Consider using configExistingSecret instead of config to avoid storing secrets in your values, see the Connector Values section for more details.

Okta
Okta

To connect deployKF with Okta, you must first create a "OIDC - Web Application" application in Okta:

The following values will connect deployKF with your Okta application:

deploykf_core:
  deploykf_auth:
    dex:
      connectors:
        - type: oidc
          id: okta
          name: Okta
          config:
            issuer: https://MY_COMPANY.okta.com

            clientID: "XXXXXXXXXXXXXXXXXXXX"
            clientSecret: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"

            ## replace with your deploykf domain
            ## NOTE: this must be an allowed redirect URI in the Okta app
            redirectURI: https://deploykf.example.com/dex/callback

            ## openid scopes to request
            scopes:
              - openid
              - email
              - profile
              ## NOTE: offline_access is required for refresh tokens
              ##  - ensure the Okta app has "Refresh Token" grant type enabled
              ##  - set the "Refresh Token Behavior" to "Rotate token after every use"
              - offline_access

            ## okta does not always send the `email_verified` claim
            insecureSkipEmailVerified: true

Use Kubernetes Secrets

Consider using configExistingSecret instead of config to avoid storing secrets in your values, see the Connector Values section for more details.

OneLogin
OneLogin

To connect deployKF with OneLogin, you must first create an "OpenID Connect" application in OneLogin:

The following values will connect deployKF with your OneLogin application:

deploykf_core:
  deploykf_auth:
    dex:
      connectors:
        - type: oidc
          id: onelogin
          name: OneLogin
          config:
            issuer: https://openid-connect.onelogin.com/oidc

            clientID: "XXXXXXXXXXXXXXXXXXXX"
            clientSecret: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"

            ## replace with your deploykf domain
            ## NOTE: this must be an allowed redirect URI in the OneLogin app
            redirectURI: https://deploykf.example.com/dex/callback

            ## openid scopes to request
            scopes:
              - openid
              - email
              - profile
              ## NOTE: offline_access is required for refresh tokens
              - offline_access

            ## onelogin does not always send the `email_verified` claim
            insecureSkipEmailVerified: true

Use Kubernetes Secrets

Consider using configExistingSecret instead of config to avoid storing secrets in your values, see the Connector Values section for more details.

Keycloak
Keycloak

To connect deployKF with Keycloak, you must first create an "OpenID Connect" client in Keycloak:

The following values will connect deployKF with your Keycloak application:

deploykf_core:
  deploykf_auth:
    dex:
      connectors:
        - type: oidc
          id: keycloak
          name: Keycloak
          config:
            issuer: https://keycloak.example.com/realms/<REALM_NAME>

            clientID: "XXXXXXXXXXXXXXXXXXXX"
            clientSecret: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"

            ## replace with your deploykf domain
            ## NOTE: this must be an allowed redirect URI in the Keycloak app
            redirectURI: https://deploykf.example.com/dex/callback

            ## openid scopes to request
            scopes:
              - openid
              - email
              - profile
              ## NOTE: offline_access is required for refresh tokens
              - offline_access

            ## keycloak does not always send the `email_verified` claim
            insecureSkipEmailVerified: true

            ## if your Keycloak uses a self-signed certificate
            #insecureSkipVerify: true

Use Kubernetes Secrets

Consider using configExistingSecret instead of config to avoid storing secrets in your values, see the Connector Values section for more details.

Generic (OpenID Connect)
Generic (OpenID Connect)

Many identity providers support the OpenID Connect (OIDC) protocol, which is an extension of OAuth2.

Dex provides an oidc connector which can be used to connect with any OIDC provider.

For example, here are generic values for connecting with an OIDC provider:

deploykf_core:
  deploykf_auth:
    dex:
      connectors:
        - type: oidc
          id: oidc
          name: Generic OIDC
          config:
            ## replace with your OIDC provider's issuer URL
            ## NOTE: the URL must expose the `.well-known/openid-configuration` endpoint 
            issuer: https://oidc.example.com

            ## credentials for the OIDC client
            clientID: "XXXXXXXXXXXXXXXXXXXX"
            clientSecret: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"

            ## replace with your deploykf domain
            ## NOTE: this must be an allowed redirect URI in the OIDC app
            redirectURI: https://deploykf.example.com/dex/callback

            ## openid scopes to request
            scopes:
              - openid
              - email
              - profile
              ## NOTE: offline_access is typically required for refresh tokens
              ##       if possible, configure your provider to only allow each
              ##       refresh token to be used once
              - offline_access

            ## set to true, if provider does not always send `email_verified` claim
            insecureSkipEmailVerified: true

Use Kubernetes Secrets

Consider using configExistingSecret instead of config to avoid storing secrets in your values, see the Connector Values section for more details.


Static User/Password Combinations

deployKF supports defining static user/password combinations which are local to itself.

These static credentials are commonly used as "service accounts" for things like Accessing the Kubeflow Pipelines API, but may also be used for regular users if you don't have an external identity provider.

Password Secret Rotation

If a user's password is defined from a Kubernetes Secret (with existingSecret), the password will be automatically rotated when the Secret is updated.

The deploykf_core.deploykf_auth.dex.staticPasswords value defines the list of credentials which are available for user authentication.

The following values show three different ways to define static credentials:

deploykf_core:
  deploykf_auth:
    dex:
      staticPasswords:
        ## (OPTION 1):
        ##  - a user with password defined as a plaintext value
        - email: "plaintext@example.com"
          password:
            value: "password"

        ## (OPTION 2):
        ##  - a user with password defined as a bcrypt hash
        ##  - a bcrypt hash for "password" can be generated with one of the following:
        ##     - echo "password" | htpasswd -BinC 10 NULL | cut -d: -f2
        ##     - python -c 'import bcrypt; print(bcrypt.hashpw(b"password", bcrypt.gensalt(10)).decode())'
        - email: "bcrypt@example.com"
          password:
            ## the bcrypt hash of the password "password"
            value: "$2y$10$z22lKMtSyC65VhMfTROkGesiS2ofrVQQdkGu.vjhIH2HM5Epmhil2"
            type: "hash"

        ## (OPTION 3):
        ##  - a user with password defined from a kubernetes secret
        - email: "kubernetes-secret@example.com"
          password:
            existingSecret: "my-secret"
            existingSecretKey: "password-key"

Last update: 2024-10-23
Created: 2023-08-16