mirror of
https://github.com/argoproj/argo-cd
synced 2026-04-21 17:07:16 +00:00
Signed-off-by: pjmanda <jhansi.manda@amadeus.com> Signed-off-by: jhansireddy01 <m.jhansireddy01@gmail.com> Co-authored-by: pjmanda <jhansi.manda@amadeus.com> Co-authored-by: Nitish Kumar <justnitish06@gmail.com> Co-authored-by: jhansireddy01 <m.jhansireddy01@gmail.com>
This commit is contained in:
parent
845d1c9c40
commit
054538b069
3 changed files with 133 additions and 2 deletions
|
|
@ -241,3 +241,33 @@ data:
|
|||
Context 'my-argo-cd-url' updated
|
||||
|
||||
You may get an warning if you are not using a correctly signed certs. Refer to [Why Am I Getting x509: certificate signed by unknown authority When Using The CLI?](https://argo-cd.readthedocs.io/en/stable/faq/#why-am-i-getting-x509-certificate-signed-by-unknown-authority-when-using-the-cli).
|
||||
|
||||
## Domain hint (optional)
|
||||
|
||||
For Microsoft identity platforms, you can set `domainHint` in `oidc.config` to provide a domain hint during sign-in.
|
||||
|
||||
When configured, Argo CD adds `domain_hint=<value>` to the authorization request sent to Microsoft.
|
||||
This can reduce account discovery prompts in multi-tenant or federated environments.
|
||||
|
||||
- **Field:** `domainHint`
|
||||
- **Type:** `string`
|
||||
- **Required:** No
|
||||
- **Default:** empty (parameter is not sent)
|
||||
|
||||
Example:
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: argocd-cm
|
||||
namespace: argocd
|
||||
data:
|
||||
oidc.config: |
|
||||
name: Microsoft
|
||||
issuer: https://login.microsoftonline.com/<tenant-id>/v2.0
|
||||
clientID: <client-id>
|
||||
clientSecret: $oidc.microsoft.clientSecret
|
||||
requestedScopes: ["openid", "profile", "email", "groups"]
|
||||
domainHint: contoso.com
|
||||
```
|
||||
|
|
|
|||
|
|
@ -100,8 +100,10 @@ type ClientApp struct {
|
|||
provider Provider
|
||||
// clientCache represent a cache of sso artifact
|
||||
clientCache cache.CacheClient
|
||||
// properties for azure workload identity.
|
||||
azure azureApp
|
||||
// domainHint is an optional hint to the identity provider about the domain the user belongs to.
|
||||
// Used to pre-fill or streamline the login experience (e.g., for Azure AD multi-tenant scenarios).
|
||||
domainHint string
|
||||
azure azureApp
|
||||
// preemptive token refresh threshold
|
||||
refreshTokenThreshold time.Duration
|
||||
}
|
||||
|
|
@ -182,6 +184,14 @@ func GetScopesOrDefault(scopes []string) []string {
|
|||
return scopes
|
||||
}
|
||||
|
||||
func getDomainHint(settings *settings.ArgoCDSettings) string {
|
||||
oidcConfig := settings.OIDCConfig()
|
||||
if oidcConfig != nil {
|
||||
return strings.TrimSpace(oidcConfig.DomainHint)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// NewClientApp will register the Argo CD client app (either via Dex or external OIDC) and return an
|
||||
// object which has HTTP handlers for handling the HTTP responses for login and callback
|
||||
func NewClientApp(settings *settings.ArgoCDSettings, dexServerAddr string, dexTLSConfig *dex.DexTLSConfig, baseHRef string, cacheClient cache.CacheClient) (*ClientApp, error) {
|
||||
|
|
@ -193,6 +203,7 @@ func NewClientApp(settings *settings.ArgoCDSettings, dexServerAddr string, dexTL
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
domainHint := getDomainHint(settings)
|
||||
a := ClientApp{
|
||||
clientID: settings.OAuth2ClientID(),
|
||||
clientSecret: settings.OAuth2ClientSecret(),
|
||||
|
|
@ -204,6 +215,7 @@ func NewClientApp(settings *settings.ArgoCDSettings, dexServerAddr string, dexTL
|
|||
encryptionKey: encryptionKey,
|
||||
clientCache: cacheClient,
|
||||
azure: azureApp{mtx: &sync.RWMutex{}},
|
||||
domainHint: domainHint,
|
||||
refreshTokenThreshold: settings.OIDCRefreshTokenThreshold,
|
||||
}
|
||||
log.Infof("Creating client app (%s)", a.clientID)
|
||||
|
|
@ -422,6 +434,9 @@ func (a *ClientApp) HandleLogin(w http.ResponseWriter, r *http.Request) {
|
|||
http.Error(w, "Invalid redirect URL: the protocol and host (including port) must match and the path must be within allowed URLs if provided", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if a.domainHint != "" {
|
||||
opts = append(opts, oauth2.SetAuthURLParam("domain_hint", a.domainHint))
|
||||
}
|
||||
if a.usePKCE {
|
||||
pkceVerifier = oauth2.GenerateVerifier()
|
||||
opts = append(opts, oauth2.S256ChallengeOption(pkceVerifier))
|
||||
|
|
|
|||
|
|
@ -49,6 +49,65 @@ func setupAzureIdentity(t *testing.T) {
|
|||
t.Setenv("AZURE_FEDERATED_TOKEN_FILE", tokenFilePath)
|
||||
}
|
||||
|
||||
func TestGetDomainHint(t *testing.T) {
|
||||
t.Run("Returns domain hint when OIDC config is set", func(t *testing.T) {
|
||||
settings := &settings.ArgoCDSettings{
|
||||
OIDCConfigRAW: `
|
||||
name: Test OIDC
|
||||
issuer: https://example.com
|
||||
clientID: test-client
|
||||
clientSecret: test-secret
|
||||
domainHint: example.com
|
||||
`,
|
||||
}
|
||||
domainHint := getDomainHint(settings)
|
||||
assert.Equal(t, "example.com", domainHint)
|
||||
})
|
||||
|
||||
t.Run("Returns empty string when domain hint is not set", func(t *testing.T) {
|
||||
settings := &settings.ArgoCDSettings{
|
||||
OIDCConfigRAW: `
|
||||
name: Test OIDC
|
||||
issuer: https://example.com
|
||||
clientID: test-client
|
||||
clientSecret: test-secret
|
||||
`,
|
||||
}
|
||||
domainHint := getDomainHint(settings)
|
||||
assert.Empty(t, domainHint)
|
||||
})
|
||||
|
||||
t.Run("Returns empty string when OIDC config is nil", func(t *testing.T) {
|
||||
settings := &settings.ArgoCDSettings{
|
||||
OIDCConfigRAW: "",
|
||||
}
|
||||
domainHint := getDomainHint(settings)
|
||||
assert.Empty(t, domainHint)
|
||||
})
|
||||
|
||||
t.Run("Returns empty string when YAML is malformed", func(t *testing.T) {
|
||||
settings := &settings.ArgoCDSettings{
|
||||
OIDCConfigRAW: `{this is not valid yaml at all]`,
|
||||
}
|
||||
domainHint := getDomainHint(settings)
|
||||
assert.Empty(t, domainHint)
|
||||
})
|
||||
|
||||
t.Run("Trims whitespaces from domain hint", func(t *testing.T) {
|
||||
settings := &settings.ArgoCDSettings{
|
||||
OIDCConfigRAW: `
|
||||
name: Test OIDC
|
||||
issuer: https://example.com
|
||||
clientID: test-client
|
||||
clientSecret: test-secret
|
||||
domainHint: " example.com "
|
||||
`,
|
||||
}
|
||||
domainHint := getDomainHint(settings)
|
||||
assert.Equal(t, "example.com", domainHint)
|
||||
})
|
||||
}
|
||||
|
||||
func TestInferGrantType(t *testing.T) {
|
||||
for _, path := range []string{"dex", "okta", "auth0", "onelogin"} {
|
||||
t.Run(path, func(t *testing.T) {
|
||||
|
|
@ -102,6 +161,33 @@ func TestIDTokenClaims(t *testing.T) {
|
|||
assert.JSONEq(t, "{\"id_token\":{\"groups\":{\"essential\":true}}}", values.Get("claims"))
|
||||
}
|
||||
|
||||
func TestHandleLogin_IncludesDomainHint(t *testing.T) {
|
||||
oidcTestServer := test.GetOIDCTestServer(t, nil)
|
||||
t.Cleanup(oidcTestServer.Close)
|
||||
|
||||
cdSettings := &settings.ArgoCDSettings{
|
||||
URL: "https://argocd.example.com",
|
||||
OIDCTLSInsecureSkipVerify: true,
|
||||
OIDCConfigRAW: fmt.Sprintf(`
|
||||
name: Test
|
||||
issuer: %s
|
||||
clientID: test-client-id
|
||||
clientSecret: test-client-secret
|
||||
domainHint: example.com
|
||||
requestedScopes: ["openid", "profile", "email", "groups"]`, oidcTestServer.URL),
|
||||
}
|
||||
app, err := NewClientApp(cdSettings, "", nil, "https://argocd.example.com", cache.NewInMemoryCache(24*time.Hour))
|
||||
require.NoError(t, err)
|
||||
|
||||
req := httptest.NewRequestWithContext(t.Context(), http.MethodGet, "https://argocd.example.com/auth/login", http.NoBody)
|
||||
w := httptest.NewRecorder()
|
||||
app.HandleLogin(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusSeeOther, w.Code)
|
||||
location := w.Header().Get("Location")
|
||||
assert.Contains(t, location, "domain_hint=example.com")
|
||||
}
|
||||
|
||||
type fakeProvider struct {
|
||||
EndpointError bool
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue