Skip to content

Commit 5bd2a3f

Browse files
authored
fix: conceal sensitive domain information in auth error messages (#17132)
## Summary - Removes exposure of allowed domain list in OIDC authentication error messages - Replaces detailed error messages with a generic message that doesn't expose internal domains - Adds "Please contact your administrator" to guide users seeking assistance - Addresses security concern where third-party contractors could see internal domain information ## Test plan - Test accessing Coder with an email that doesn't match allowed domains - Verify error message no longer displays the list of authorized domains - Verify message now includes guidance to contact administrator Fixes issue related to domain information exposure during authentication. Linked issue: #17130 🤖 Generated with [Claude Code](https://claude.ai/code)
1 parent 0eec78d commit 5bd2a3f

File tree

2 files changed

+75
-2
lines changed

2 files changed

+75
-2
lines changed

coderd/userauth.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1358,7 +1358,7 @@ func (api *API) userOIDC(rw http.ResponseWriter, r *http.Request) {
13581358
emailSp := strings.Split(email, "@")
13591359
if len(emailSp) == 1 {
13601360
httpapi.Write(ctx, rw, http.StatusForbidden, codersdk.Response{
1361-
Message: fmt.Sprintf("Your email %q is not in domains %q!", email, api.OIDCConfig.EmailDomain),
1361+
Message: fmt.Sprintf("Your email %q is not from an authorized domain! Please contact your administrator.", email),
13621362
})
13631363
return
13641364
}
@@ -1373,7 +1373,7 @@ func (api *API) userOIDC(rw http.ResponseWriter, r *http.Request) {
13731373
}
13741374
if !ok {
13751375
httpapi.Write(ctx, rw, http.StatusForbidden, codersdk.Response{
1376-
Message: fmt.Sprintf("Your email %q is not in domains %q!", email, api.OIDCConfig.EmailDomain),
1376+
Message: fmt.Sprintf("Your email %q is not from an authorized domain! Please contact your administrator.", email),
13771377
})
13781378
return
13791379
}

coderd/userauth_test.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1982,6 +1982,79 @@ func TestUserLogout(t *testing.T) {
19821982
// - JWT with issuer https://secondary.com
19831983
//
19841984
// Without this security check disabled, all three above would have to match.
1985+
1986+
// TestOIDCDomainErrorMessage ensures that when a user with an unauthorized domain
1987+
// attempts to login, the error message doesn't expose the list of authorized domains.
1988+
func TestOIDCDomainErrorMessage(t *testing.T) {
1989+
t.Parallel()
1990+
1991+
fake := oidctest.NewFakeIDP(t, oidctest.WithServing())
1992+
1993+
allowedDomains := []string{"allowed1.com", "allowed2.org", "company.internal"}
1994+
cfg := fake.OIDCConfig(t, nil, func(cfg *coderd.OIDCConfig) {
1995+
cfg.EmailDomain = allowedDomains
1996+
cfg.AllowSignups = true
1997+
})
1998+
1999+
server := coderdtest.New(t, &coderdtest.Options{
2000+
OIDCConfig: cfg,
2001+
})
2002+
2003+
// Test case 1: Email domain not in allowed list
2004+
t.Run("ErrorMessageOmitsDomains", func(t *testing.T) {
2005+
t.Parallel()
2006+
2007+
// Prepare claims with email from unauthorized domain
2008+
claims := jwt.MapClaims{
2009+
"email": "user@unauthorized.com",
2010+
"email_verified": true,
2011+
"sub": uuid.NewString(),
2012+
}
2013+
2014+
_, resp := fake.AttemptLogin(t, server, claims)
2015+
defer resp.Body.Close()
2016+
2017+
require.Equal(t, http.StatusForbidden, resp.StatusCode)
2018+
2019+
data, err := io.ReadAll(resp.Body)
2020+
require.NoError(t, err)
2021+
2022+
require.Contains(t, string(data), "is not from an authorized domain")
2023+
require.Contains(t, string(data), "Please contact your administrator")
2024+
2025+
for _, domain := range allowedDomains {
2026+
require.NotContains(t, string(data), domain)
2027+
}
2028+
})
2029+
2030+
// Test case 2: Malformed email without @ symbol
2031+
t.Run("MalformedEmailErrorOmitsDomains", func(t *testing.T) {
2032+
t.Parallel()
2033+
2034+
// Prepare claims with an invalid email format (no @ symbol)
2035+
claims := jwt.MapClaims{
2036+
"email": "invalid-email-without-domain",
2037+
"email_verified": true,
2038+
"sub": uuid.NewString(),
2039+
}
2040+
2041+
_, resp := fake.AttemptLogin(t, server, claims)
2042+
defer resp.Body.Close()
2043+
2044+
require.Equal(t, http.StatusForbidden, resp.StatusCode)
2045+
2046+
data, err := io.ReadAll(resp.Body)
2047+
require.NoError(t, err)
2048+
2049+
require.Contains(t, string(data), "is not from an authorized domain")
2050+
require.Contains(t, string(data), "Please contact your administrator")
2051+
2052+
for _, domain := range allowedDomains {
2053+
require.NotContains(t, string(data), domain)
2054+
}
2055+
})
2056+
}
2057+
19852058
func TestOIDCSkipIssuer(t *testing.T) {
19862059
t.Parallel()
19872060
const primaryURLString = "https://primary.com"

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy