feat: allow configuring OIDC email claim and OIDC auth url parameters (#6867)

This commit:

- Allows configuring the OIDC claim Coder uses for email addresses (by default, this is still email)
- Allows customising the parameters sent to the upstream identity provider when requesting a token. This is still access_type=offline by default.
- Updates documentation related to the above.
This commit is contained in:
Cian Johnston
2023-03-30 09:36:57 +01:00
committed by GitHub
parent 6981f89cd8
commit 563c3ade06
17 changed files with 379 additions and 22 deletions
+2
View File
@@ -726,6 +726,8 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
EmailDomain: cfg.OIDC.EmailDomain,
AllowSignups: cfg.OIDC.AllowSignups.Value(),
UsernameField: cfg.OIDC.UsernameField.String(),
EmailField: cfg.OIDC.EmailField.String(),
AuthURLParams: cfg.OIDC.AuthURLParams.Value,
GroupField: cfg.OIDC.GroupField.String(),
GroupMapping: cfg.OIDC.GroupMapping.Value,
SignInText: cfg.OIDC.SignInText.String(),
+161
View File
@@ -37,6 +37,7 @@ import (
"github.com/coder/coder/coderd/database/postgres"
"github.com/coder/coder/coderd/telemetry"
"github.com/coder/coder/codersdk"
"github.com/coder/coder/cryptorand"
"github.com/coder/coder/pty/ptytest"
"github.com/coder/coder/testutil"
)
@@ -1016,6 +1017,166 @@ func TestServer(t *testing.T) {
require.True(t, strings.HasPrefix(fakeURL.String(), fakeRedirect), fakeURL.String())
})
t.Run("OIDC", func(t *testing.T) {
t.Parallel()
t.Run("Defaults", func(t *testing.T) {
t.Parallel()
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitMedium)
defer cancel()
// Startup a fake server that just responds to .well-known/openid-configuration
// This is just needed to get Coder to start up.
oidcServer := httptest.NewServer(nil)
fakeWellKnownHandler := func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
payload := fmt.Sprintf("{\"issuer\": %q}", oidcServer.URL)
_, _ = w.Write([]byte(payload))
}
oidcServer.Config.Handler = http.HandlerFunc(fakeWellKnownHandler)
t.Cleanup(oidcServer.Close)
inv, cfg := clitest.New(t,
"server",
"--in-memory",
"--http-address", ":0",
"--access-url", "http://example.com",
"--oidc-client-id", "fake",
"--oidc-client-secret", "fake",
"--oidc-issuer-url", oidcServer.URL,
// Leaving the rest of the flags as defaults.
)
// Ensure that the server starts up without error.
clitest.Start(t, inv)
accessURL := waitAccessURL(t, cfg)
client := codersdk.New(accessURL)
randPassword, err := cryptorand.String(24)
require.NoError(t, err)
_, err = client.CreateFirstUser(ctx, codersdk.CreateFirstUserRequest{
Email: "admin@coder.com",
Password: randPassword,
Username: "admin",
Trial: true,
})
require.NoError(t, err)
loginResp, err := client.LoginWithPassword(ctx, codersdk.LoginWithPasswordRequest{
Email: "admin@coder.com",
Password: randPassword,
})
require.NoError(t, err)
client.SetSessionToken(loginResp.SessionToken)
deploymentConfig, err := client.DeploymentConfig(ctx)
require.NoError(t, err)
// Ensure that the OIDC provider is configured correctly.
require.Equal(t, "fake", deploymentConfig.Values.OIDC.ClientID.Value())
// The client secret is not returned from the API.
require.Empty(t, deploymentConfig.Values.OIDC.ClientSecret.Value())
require.Equal(t, oidcServer.URL, deploymentConfig.Values.OIDC.IssuerURL.Value())
// These are the default values returned from the API. See codersdk/deployment.go for the default values.
require.True(t, deploymentConfig.Values.OIDC.AllowSignups.Value())
require.Empty(t, deploymentConfig.Values.OIDC.EmailDomain.Value())
require.Equal(t, []string{"openid", "profile", "email"}, deploymentConfig.Values.OIDC.Scopes.Value())
require.False(t, deploymentConfig.Values.OIDC.IgnoreEmailVerified.Value())
require.Equal(t, "preferred_username", deploymentConfig.Values.OIDC.UsernameField.Value())
require.Equal(t, "email", deploymentConfig.Values.OIDC.EmailField.Value())
require.Equal(t, map[string]string{"access_type": "offline"}, deploymentConfig.Values.OIDC.AuthURLParams.Value)
require.Empty(t, deploymentConfig.Values.OIDC.GroupField.Value())
require.Empty(t, deploymentConfig.Values.OIDC.GroupMapping.Value)
require.Equal(t, "OpenID Connect", deploymentConfig.Values.OIDC.SignInText.Value())
require.Empty(t, deploymentConfig.Values.OIDC.IconURL.Value())
})
t.Run("Overrides", func(t *testing.T) {
t.Parallel()
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitMedium)
defer cancel()
// Startup a fake server that just responds to .well-known/openid-configuration
// This is just needed to get Coder to start up.
oidcServer := httptest.NewServer(nil)
fakeWellKnownHandler := func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
payload := fmt.Sprintf("{\"issuer\": %q}", oidcServer.URL)
_, _ = w.Write([]byte(payload))
}
oidcServer.Config.Handler = http.HandlerFunc(fakeWellKnownHandler)
t.Cleanup(oidcServer.Close)
inv, cfg := clitest.New(t,
"server",
"--in-memory",
"--http-address", ":0",
"--access-url", "http://example.com",
"--oidc-client-id", "fake",
"--oidc-client-secret", "fake",
"--oidc-issuer-url", oidcServer.URL,
// The following values have defaults that we want to override.
"--oidc-allow-signups=false",
"--oidc-email-domain", "example.com",
"--oidc-scopes", "360noscope",
"--oidc-ignore-email-verified",
"--oidc-username-field", "not_preferred_username",
"--oidc-email-field", "not_email",
"--oidc-auth-url-params", `{"prompt":"consent"}`,
"--oidc-group-field", "serious_business_unit",
"--oidc-group-mapping", `{"serious_business_unit": "serious_business_unit"}`,
"--oidc-sign-in-text", "Sign In With Coder",
"--oidc-icon-url", "https://example.com/icon.png",
)
// Ensure that the server starts up without error.
clitest.Start(t, inv)
accessURL := waitAccessURL(t, cfg)
client := codersdk.New(accessURL)
randPassword, err := cryptorand.String(24)
require.NoError(t, err)
_, err = client.CreateFirstUser(ctx, codersdk.CreateFirstUserRequest{
Email: "admin@coder.com",
Password: randPassword,
Username: "admin",
Trial: true,
})
require.NoError(t, err)
loginResp, err := client.LoginWithPassword(ctx, codersdk.LoginWithPasswordRequest{
Email: "admin@coder.com",
Password: randPassword,
})
require.NoError(t, err)
client.SetSessionToken(loginResp.SessionToken)
deploymentConfig, err := client.DeploymentConfig(ctx)
require.NoError(t, err)
// Ensure that the OIDC provider is configured correctly.
require.Equal(t, "fake", deploymentConfig.Values.OIDC.ClientID.Value())
// The client secret is not returned from the API.
require.Empty(t, deploymentConfig.Values.OIDC.ClientSecret.Value())
require.Equal(t, oidcServer.URL, deploymentConfig.Values.OIDC.IssuerURL.Value())
// These are values that we want to make sure were overridden.
require.False(t, deploymentConfig.Values.OIDC.AllowSignups.Value())
require.Equal(t, []string{"example.com"}, deploymentConfig.Values.OIDC.EmailDomain.Value())
require.Equal(t, []string{"360noscope"}, deploymentConfig.Values.OIDC.Scopes.Value())
require.True(t, deploymentConfig.Values.OIDC.IgnoreEmailVerified.Value())
require.Equal(t, "not_preferred_username", deploymentConfig.Values.OIDC.UsernameField.Value())
require.Equal(t, "not_email", deploymentConfig.Values.OIDC.EmailField.Value())
require.Equal(t, map[string]string{"access_type": "offline", "prompt": "consent"}, deploymentConfig.Values.OIDC.AuthURLParams.Value)
require.Equal(t, "serious_business_unit", deploymentConfig.Values.OIDC.GroupField.Value())
require.Equal(t, map[string]string{"serious_business_unit": "serious_business_unit"}, deploymentConfig.Values.OIDC.GroupMapping.Value)
require.Equal(t, "Sign In With Coder", deploymentConfig.Values.OIDC.SignInText.Value())
require.Equal(t, "https://example.com/icon.png", deploymentConfig.Values.OIDC.IconURL.Value().String())
})
})
t.Run("RateLimit", func(t *testing.T) {
t.Parallel()
+6
View File
@@ -261,6 +261,9 @@ can safely ignore these settings.
--oidc-allow-signups bool, $CODER_OIDC_ALLOW_SIGNUPS (default: true)
Whether new users can sign up with OIDC.
--oidc-auth-url-params struct[map[string]string], $CODER_OIDC_AUTH_URL_PARAMS (default: {"access_type": "offline"})
OIDC auth URL parameters to pass to the upstream provider.
--oidc-client-id string, $CODER_OIDC_CLIENT_ID
Client ID to use for Login with OIDC.
@@ -270,6 +273,9 @@ can safely ignore these settings.
--oidc-email-domain string-array, $CODER_OIDC_EMAIL_DOMAIN
Email domains that clients logging in with OIDC must match.
--oidc-email-field string, $CODER_OIDC_EMAIL_FIELD (default: email)
OIDC claim field to use as the email.
--oidc-group-field string, $CODER_OIDC_GROUP_FIELD
Change the OIDC default 'groups' claim field. By default, will be
'groups' if present in the oidc scopes argument.