Skip to content

Commit 3d0febd

Browse files
kylecarbsammario
andauthored
feat: Add OIDC authentication (#3314)
* feat: Add OIDC authentication * Extract username into a separate package and add OIDC tests * Add test case for invalid tokens * Add test case for username as email * Add OIDC to the frontend * Improve comments from self-review * Add authentication docs * Add telemetry * Update docs/install/auth.md Co-authored-by: Ammar Bandukwala <ammar@ammar.io> * Update docs/install/auth.md Co-authored-by: Ammar Bandukwala <ammar@ammar.io> * Remove username package Co-authored-by: Ammar Bandukwala <ammar@ammar.io>
1 parent 8b17bf9 commit 3d0febd

File tree

28 files changed

+733
-137
lines changed

28 files changed

+733
-137
lines changed

.vscode/settings.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
"mattn",
4343
"mitchellh",
4444
"moby",
45+
"namesgenerator",
4546
"nfpms",
4647
"nhooyr",
4748
"nolint",

cli/server.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"sync"
2424
"time"
2525

26+
"github.com/coreos/go-oidc/v3/oidc"
2627
"github.com/coreos/go-systemd/daemon"
2728
embeddedpostgres "github.com/fergusstrange/embedded-postgres"
2829
"github.com/google/go-github/v43/github"
@@ -84,6 +85,12 @@ func server() *cobra.Command {
8485
oauth2GithubAllowedOrganizations []string
8586
oauth2GithubAllowedTeams []string
8687
oauth2GithubAllowSignups bool
88+
oidcAllowSignups bool
89+
oidcClientID string
90+
oidcClientSecret string
91+
oidcEmailDomain string
92+
oidcIssuerURL string
93+
oidcScopes []string
8794
telemetryEnable bool
8895
telemetryURL string
8996
tlsCertFile string
@@ -283,6 +290,38 @@ func server() *cobra.Command {
283290
}
284291
}
285292

293+
if oidcClientSecret != "" {
294+
if oidcClientID == "" {
295+
return xerrors.Errorf("OIDC client ID be set!")
296+
}
297+
if oidcIssuerURL == "" {
298+
return xerrors.Errorf("OIDC issuer URL must be set!")
299+
}
300+
301+
oidcProvider, err := oidc.NewProvider(ctx, oidcIssuerURL)
302+
if err != nil {
303+
return xerrors.Errorf("configure oidc provider: %w", err)
304+
}
305+
redirectURL, err := accessURLParsed.Parse("/api/v2/users/oidc/callback")
306+
if err != nil {
307+
return xerrors.Errorf("parse oidc oauth callback url: %w", err)
308+
}
309+
options.OIDCConfig = &coderd.OIDCConfig{
310+
OAuth2Config: &oauth2.Config{
311+
ClientID: oidcClientID,
312+
ClientSecret: oidcClientSecret,
313+
RedirectURL: redirectURL.String(),
314+
Endpoint: oidcProvider.Endpoint(),
315+
Scopes: oidcScopes,
316+
},
317+
Verifier: oidcProvider.Verifier(&oidc.Config{
318+
ClientID: oidcClientID,
319+
}),
320+
EmailDomain: oidcEmailDomain,
321+
AllowSignups: oidcAllowSignups,
322+
}
323+
}
324+
286325
if inMemoryDatabase {
287326
options.Database = databasefake.New()
288327
options.Pubsub = database.NewPubsubInMemory()
@@ -341,6 +380,8 @@ func server() *cobra.Command {
341380
Logger: logger.Named("telemetry"),
342381
URL: telemetryURL,
343382
GitHubOAuth: oauth2GithubClientID != "",
383+
OIDCAuth: oidcClientID != "",
384+
OIDCIssuerURL: oidcIssuerURL,
344385
Prometheus: promEnabled,
345386
STUN: len(stunServers) != 0,
346387
Tunnel: tunnel,
@@ -637,6 +678,18 @@ func server() *cobra.Command {
637678
"Specifies teams inside organizations the user must be a member of to authenticate with GitHub. Formatted as: <organization-name>/<team-slug>.")
638679
cliflag.BoolVarP(root.Flags(), &oauth2GithubAllowSignups, "oauth2-github-allow-signups", "", "CODER_OAUTH2_GITHUB_ALLOW_SIGNUPS", false,
639680
"Specifies whether new users can sign up with GitHub.")
681+
cliflag.BoolVarP(root.Flags(), &oidcAllowSignups, "oidc-allow-signups", "", "CODER_OIDC_ALLOW_SIGNUPS", true,
682+
"Specifies whether new users can sign up with OIDC.")
683+
cliflag.StringVarP(root.Flags(), &oidcClientID, "oidc-client-id", "", "CODER_OIDC_CLIENT_ID", "",
684+
"Specifies a client ID to use for OIDC.")
685+
cliflag.StringVarP(root.Flags(), &oidcClientSecret, "oidc-client-secret", "", "CODER_OIDC_CLIENT_SECRET", "",
686+
"Specifies a client secret to use for OIDC.")
687+
cliflag.StringVarP(root.Flags(), &oidcEmailDomain, "oidc-email-domain", "", "CODER_OIDC_EMAIL_DOMAIN", "",
688+
"Specifies an email domain that clients authenticating with OIDC must match.")
689+
cliflag.StringVarP(root.Flags(), &oidcIssuerURL, "oidc-issuer-url", "", "CODER_OIDC_ISSUER_URL", "",
690+
"Specifies an issuer URL to use for OIDC.")
691+
cliflag.StringArrayVarP(root.Flags(), &oidcScopes, "oidc-scopes", "", "CODER_OIDC_SCOPES", []string{oidc.ScopeOpenID, "profile", "email"},
692+
"Specifies scopes to grant when authenticating with OIDC.")
640693
enableTelemetryByDefault := !isTest()
641694
cliflag.BoolVarP(root.Flags(), &telemetryEnable, "telemetry", "", "CODER_TELEMETRY", enableTelemetryByDefault, "Specifies whether telemetry is enabled or not. Coder collects anonymized usage data to help improve our product.")
642695
cliflag.StringVarP(root.Flags(), &telemetryURL, "telemetry-url", "", "CODER_TELEMETRY_URL", "https://telemetry.coder.com", "Specifies a URL to send telemetry to.")

coderd/coderd.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ type Options struct {
5757
AzureCertificates x509.VerifyOptions
5858
GoogleTokenValidator *idtoken.Validator
5959
GithubOAuth2Config *GithubOAuth2Config
60+
OIDCConfig *OIDCConfig
6061
ICEServers []webrtc.ICEServer
6162
SecureAuthCookie bool
6263
SSHKeygenAlgorithm gitsshkey.Algorithm
@@ -105,6 +106,7 @@ func New(options *Options) *API {
105106
api.workspaceAgentCache = wsconncache.New(api.dialWorkspaceAgent, 0)
106107
oauthConfigs := &httpmw.OAuth2Configs{
107108
Github: options.GithubOAuth2Config,
109+
OIDC: options.OIDCConfig,
108110
}
109111
apiKeyMiddleware := httpmw.ExtractAPIKey(options.Database, oauthConfigs, false)
110112

@@ -259,6 +261,10 @@ func New(options *Options) *API {
259261
r.Get("/callback", api.userOAuth2Github)
260262
})
261263
})
264+
r.Route("/oidc/callback", func(r chi.Router) {
265+
r.Use(httpmw.ExtractOAuth2(options.OIDCConfig))
266+
r.Get("/", api.userOIDC)
267+
})
262268
r.Group(func(r chi.Router) {
263269
r.Use(
264270
apiKeyMiddleware,

coderd/coderd_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@ func TestAuthorizeAllEndpoints(t *testing.T) {
248248

249249
// Has it's own auth
250250
"GET:/api/v2/users/oauth2/github/callback": {NoAuthorize: true},
251+
"GET:/api/v2/users/oidc/callback": {NoAuthorize: true},
251252

252253
// All workspaceagents endpoints do not use rbac
253254
"POST:/api/v2/workspaceagents/aws-instance-identity": {NoAuthorize: true},

coderd/coderdtest/coderdtest.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ type Options struct {
6363
Authorizer rbac.Authorizer
6464
AzureCertificates x509.VerifyOptions
6565
GithubOAuth2Config *coderd.GithubOAuth2Config
66+
OIDCConfig *coderd.OIDCConfig
6667
GoogleTokenValidator *idtoken.Validator
6768
SSHKeygenAlgorithm gitsshkey.Algorithm
6869
APIRateLimit int
@@ -189,6 +190,7 @@ func newWithCloser(t *testing.T, options *Options) (*codersdk.Client, io.Closer)
189190
AWSCertificates: options.AWSCertificates,
190191
AzureCertificates: options.AzureCertificates,
191192
GithubOAuth2Config: options.GithubOAuth2Config,
193+
OIDCConfig: options.OIDCConfig,
192194
GoogleTokenValidator: options.GoogleTokenValidator,
193195
SSHKeygenAlgorithm: options.SSHKeygenAlgorithm,
194196
TURNServer: turnServer,

coderd/database/dump.sql

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/dump/main.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ func main() {
3131
}
3232

3333
cmd := exec.Command(
34+
"docker",
35+
"run",
36+
"--rm",
37+
"--network=host",
38+
"postgres:13",
3439
"pg_dump",
3540
"--schema-only",
3641
connection,
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
CREATE TYPE old_login_type AS ENUM (
2+
'password',
3+
'github'
4+
);
5+
ALTER TABLE api_keys ALTER COLUMN login_type TYPE old_login_type USING (login_type::text::old_login_type);
6+
DROP TYPE login_type;
7+
ALTER TYPE old_login_type RENAME TO login_type;
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CREATE TYPE new_login_type AS ENUM (
2+
'password',
3+
'github',
4+
'oidc'
5+
);
6+
ALTER TABLE api_keys ALTER COLUMN login_type TYPE new_login_type USING (login_type::text::new_login_type);
7+
DROP TYPE login_type;
8+
ALTER TYPE new_login_type RENAME TO login_type;

coderd/database/models.go

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

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