argoproj / argo-cd

Declarative Continuous Deployment for Kubernetes
https://argo-cd.readthedocs.io
Apache License 2.0
17.58k stars 5.36k forks source link

Username mapping from JWT claim #6390

Open Piroddi opened 3 years ago

Piroddi commented 3 years ago

Describe the bug

The default logic for setting up login through a 3rd party Identity provider maps the ArgoCD username from the email field of the JWT claim as seen in the code:

func Username(ctx context.Context) string {
    mapClaims, ok := mapClaims(ctx)
    if !ok {
        return ""
    }
    switch jwtutil.StringField(mapClaims, "iss") {
    case SessionManagerClaimsIssuer:
        return jwtutil.StringField(mapClaims, "sub")
    default:
        return jwtutil.StringField(mapClaims, "email")
    }
}

However with Microsoft active directory, not all accounts are associated with an email address and therefore even with an email requested scope, the email field is missing in the claim.

https://docs.microsoft.com/en-gb/azure/active-directory/develop/v2-permissions-and-consent#email

The following results in a successful login, however the username is unknown.

This is problematic since tracking and auditibility of activity on ArgoCD is not possible.

To Reproduce

Config oidc with Microsoft:

  oidc.config: |
    name: Azure
    issuer: ***
    clientID: ***
    clientSecret: $oidc.azure.clientSecret
    requestedIDTokenClaims:
        groups:
            essential: true
    requestedScopes:
        - openid
        - profile
        - email

And login with user whose accounts have not been linked to an email address.

Expected behavior

The ability to configure the claim field that ArgoCD uses to map the username.

Screenshots

image

image

Version

v2.0.3

duncan485 commented 2 years ago

Hi @Piroddi , did you ever find a work around for this issue ? I'm having the same issue.

Piroddi commented 2 years ago

Hi @duncan485, Currently running a fork of argoCD with a small code change to resolve the above. Hoping this issue can be acknowledged/triaged so that I can submit a PR.

duncan485 commented 2 years ago

@Piroddi Good to know, thanks ! fyi: as an alternative, I was able to work around this issue using the claimMapping option from Dex and setting the preferred_username as email claim.

dex.config: |
          connectors:
          - type: oidc
            id: Azure
            name: Azure
            config:
              issuer: "https://login.microsoftonline.com/<TENANT_ID>/v2.0"
              clientID: <CLIENT_ID>
              clientSecret: <CLIENT_SECRET>
              redirectURI: https://<HOST>/api/dex/callback
              insecureSkipEmailVerified: true 
              insecureEnableGroups: true
              scopes:
               - profile
               - email
               - openid
              claimMapping:
                email: preferred_username
ltmleo commented 2 years ago

@Piroddi Good to know, thanks ! fyi: as an alternative, I was able to work around this issue using the claimMapping option from Dex and setting the preferred_username as email claim.

dex.config: |
          connectors:
          - type: oidc
            id: Azure
            name: Azure
            config:
              issuer: "https://login.microsoftonline.com/<TENANT_ID>/v2.0"
              clientID: <CLIENT_ID>
              clientSecret: <CLIENT_SECRET>
              redirectURI: https://<HOST>/api/dex/callback
              insecureSkipEmailVerified: true 
              insecureEnableGroups: true
              scopes:
               - profile
               - email
               - openid
              claimMapping:
                email: preferred_username

Thanks!!! This works for me!

ricardojdsilva87 commented 1 year ago

Hello everyone, is there anyway of doing the same but not for dex? Already tried several different notations and it's not doing nothing. If we have this on the helm chart:

          oidc.config: |
            name: OIDC name
            issuer: https://oidc.com url
            clientID:  $oidc-secret:oidc.clientID
            clientSecret: $oidc-secret:oidc.clientSecret
            requestedScopes: ["openid"]

The User Info tab appears only with the issuer info with no username, but if the requestedScopes are:

requestedScopes: ["openid","email","profile"]

The email appears on the username and the groups field

Even adding the following options to the configuration doesn't seem to be changing anything:

            requestedScopes: ["profile","email","openid"]
            userNameKey: sub
            overrideClaimMapping: true
            claimMapping:
              email: sub

This was to use the userid instead of the email in the User Name identification. Also tried with other fields returned by the jwt response but with no luck.

{
  "grpc.method": "Watch",
  "grpc.request.claims": '{"acr":"gas:strong","aud":"audience-id","auth_time":1678176969,"email":"email@domain","email_verified":true,"exp":1678206184,"family_name":"last name","given_name":"first name","iat":1678205284,"iss":"https://sso domain","jti":"audinzxxxxx","name":"Full name","pi.sri" :"random id","sub":"user id","updated_at":20220721}',
  "grpc.request.content":
    { "resourceVersion": "247440034", "selector": "", "appNamespace": "" },
  "grpc.service": "application.ApplicationService",
  "grpc.start_time": "2023-03-07T16:08:06Z",
  "level": "info",
  "msg": "received streaming call /application.ApplicationService/Watch",
  "span.kind": "server",
  "system": "grpc",
  "time": "2023-03-07T16:08:06Z",
}

As per the initial message the sub field should have been used case it exists and revert to the email only as a default.

Also cannot find anything regarding documentation for configuring this option, the examples that exist only have the scopes examples and nothing related to mappings of the claims. Also found some examples only for dex in the official pages.

Thanks for all the help

ricardojdsilva87 commented 1 year ago

Just an update regarding the issue, If in the requested scopes the email field is there, it will be automatically be considered to be the Username field. I suppose that this is extracted from the OIDC response in this part of the code https://github.com/argoproj/argo-cd/blob/master/pkg/apiclient/session/session.pb.go#L229C1-L230C1

Everything matches except the username field does not exist, it exists the name field. It's possible to show every field under the groups on ArgoCD by adding the following snippet on the argocd-rbac-cm:

scopes: '[sub,name]'

This will show up like the following: image

Also tried to map the claim like explained in the previous messages like the following with no success:

oidc.config: |
    name: OIDC
    issuer: https://xxxxx.com
    clientID: $argocd-secret:oidc.clientID
    clientSecret: $argocd-secret:oidc.clientSecret
    requestedScopes: ["openid","profile"]
    claimMapping:
      username: name

This works in dex, but not when usingthe builtin ArgoCD oidc configuration, it should remap the name to the username used by ArgoCD on the UI (Also tried to invert the order with the same result).

Without this field every user interaction will appear empty on the applications (fyi we cannot use email field because not all users have one in the AD, that would prevent them from logging in but an ID everyone has one). image

Thanks

oldboys92 commented 8 months ago
  • type: oidc id: Azure name: Azure config: issuer: "https://login.microsoftonline.com//v2.0" clientID: clientSecret: redirectURI: https:///api/dex/callback insecureSkipEmailVerified: true insecureEnableGroups: true scopes:
    • profile
    • email
    • openid claimMapping: email: preferred_username

this indeed solves my Azure AD specific organization setup, when users don't have an email, but according to a colleague the DEX setup is not HA able.

I will love to see this PR accepted, looks to me pretty strait forward. and I can confirm it helps to fix the empty username issue.

laiminhtrung1997 commented 4 months ago

Any news on this issue when configuring the OIDC? I want the username field to be a username instead of an email :(