Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit3d0febd

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.mdCo-authored-by: Ammar Bandukwala <ammar@ammar.io>* Update docs/install/auth.mdCo-authored-by: Ammar Bandukwala <ammar@ammar.io>* Remove username packageCo-authored-by: Ammar Bandukwala <ammar@ammar.io>
1 parent8b17bf9 commit3d0febd

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
oauth2GithubAllowSignupsbool
88+
oidcAllowSignupsbool
89+
oidcClientIDstring
90+
oidcClientSecretstring
91+
oidcEmailDomainstring
92+
oidcIssuerURLstring
93+
oidcScopes []string
8794
telemetryEnablebool
8895
telemetryURLstring
8996
tlsCertFilestring
@@ -283,6 +290,38 @@ func server() *cobra.Command {
283290
}
284291
}
285292

293+
ifoidcClientSecret!="" {
294+
ifoidcClientID=="" {
295+
returnxerrors.Errorf("OIDC client ID be set!")
296+
}
297+
ifoidcIssuerURL=="" {
298+
returnxerrors.Errorf("OIDC issuer URL must be set!")
299+
}
300+
301+
oidcProvider,err:=oidc.NewProvider(ctx,oidcIssuerURL)
302+
iferr!=nil {
303+
returnxerrors.Errorf("configure oidc provider: %w",err)
304+
}
305+
redirectURL,err:=accessURLParsed.Parse("/api/v2/users/oidc/callback")
306+
iferr!=nil {
307+
returnxerrors.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
ifinMemoryDatabase {
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
SecureAuthCookiebool
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
APIRateLimitint
@@ -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 aboutcustomizing 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+
CREATETYPEold_login_typeAS ENUM (
2+
'password',
3+
'github'
4+
);
5+
ALTERTABLE api_keys ALTER COLUMN login_type TYPE old_login_type USING (login_type::text::old_login_type);
6+
DROPTYPE login_type;
7+
ALTERTYPE 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+
CREATETYPEnew_login_typeAS ENUM (
2+
'password',
3+
'github',
4+
'oidc'
5+
);
6+
ALTERTABLE api_keys ALTER COLUMN login_type TYPE new_login_type USING (login_type::text::new_login_type);
7+
DROPTYPE login_type;
8+
ALTERTYPE 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 aboutcustomizing how changed files appear on GitHub.

‎coderd/httpapi/httpapi.go

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77
"fmt"
88
"net/http"
99
"reflect"
10-
"regexp"
1110
"strings"
1211

1312
"github.com/go-playground/validator/v10"
@@ -16,8 +15,7 @@ import (
1615
)
1716

1817
var (
19-
validate*validator.Validate
20-
usernameRegex=regexp.MustCompile("^[a-zA-Z0-9]+(?:-[a-zA-Z0-9]+)*$")
18+
validate*validator.Validate
2119
)
2220

2321
// This init is used to create a validator and register validation-specific
@@ -39,13 +37,7 @@ func init() {
3937
if!ok {
4038
returnfalse
4139
}
42-
iflen(str)>32 {
43-
returnfalse
44-
}
45-
iflen(str)<1 {
46-
returnfalse
47-
}
48-
returnusernameRegex.MatchString(str)
40+
returnUsernameValid(str)
4941
})
5042
iferr!=nil {
5143
panic(err)

‎coderd/httpapi/httpapi_test.go

Lines changed: 0 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -81,71 +81,6 @@ func TestRead(t *testing.T) {
8181
})
8282
}
8383

84-
funcTestReadUsername(t*testing.T) {
85-
t.Parallel()
86-
// Tests whether usernames are valid or not.
87-
testCases:= []struct {
88-
Usernamestring
89-
Validbool
90-
}{
91-
{"1",true},
92-
{"12",true},
93-
{"123",true},
94-
{"12345678901234567890",true},
95-
{"123456789012345678901",true},
96-
{"a",true},
97-
{"a1",true},
98-
{"a1b2",true},
99-
{"a1b2c3d4e5f6g7h8i9j0",true},
100-
{"a1b2c3d4e5f6g7h8i9j0k",true},
101-
{"aa",true},
102-
{"abc",true},
103-
{"abcdefghijklmnopqrst",true},
104-
{"abcdefghijklmnopqrstu",true},
105-
{"wow-test",true},
106-
107-
{"",false},
108-
{" ",false},
109-
{" a",false},
110-
{" a ",false},
111-
{" 1",false},
112-
{"1 ",false},
113-
{" aa",false},
114-
{"aa ",false},
115-
{" 12",false},
116-
{"12 ",false},
117-
{" a1",false},
118-
{"a1 ",false},
119-
{" abcdefghijklmnopqrstu",false},
120-
{"abcdefghijklmnopqrstu ",false},
121-
{" 123456789012345678901",false},
122-
{" a1b2c3d4e5f6g7h8i9j0k",false},
123-
{"a1b2c3d4e5f6g7h8i9j0k ",false},
124-
{"bananas_wow",false},
125-
{"test--now",false},
126-
127-
{"123456789012345678901234567890123",false},
128-
{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",false},
129-
{"123456789012345678901234567890123123456789012345678901234567890123",false},
130-
}
131-
typetoValidatestruct {
132-
Usernamestring`json:"username" validate:"username"`
133-
}
134-
for_,testCase:=rangetestCases {
135-
testCase:=testCase
136-
t.Run(testCase.Username,func(t*testing.T) {
137-
t.Parallel()
138-
rw:=httptest.NewRecorder()
139-
data,err:=json.Marshal(toValidate{testCase.Username})
140-
require.NoError(t,err)
141-
r:=httptest.NewRequest("POST","/",bytes.NewBuffer(data))
142-
143-
varvalidatetoValidate
144-
require.Equal(t,testCase.Valid,httpapi.Read(rw,r,&validate))
145-
})
146-
}
147-
}
148-
14984
funcWebsocketCloseMsg(t*testing.T) {
15085
t.Parallel()
15186

‎coderd/httpapi/username.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package httpapi
2+
3+
import (
4+
"regexp"
5+
"strings"
6+
7+
"github.com/moby/moby/pkg/namesgenerator"
8+
)
9+
10+
var (
11+
usernameValid=regexp.MustCompile("^[a-zA-Z0-9]+(?:-[a-zA-Z0-9]+)*$")
12+
usernameReplace=regexp.MustCompile("[^a-zA-Z0-9-]*")
13+
)
14+
15+
// UsernameValid returns whether the input string is a valid username.
16+
funcUsernameValid(strstring)bool {
17+
iflen(str)>32 {
18+
returnfalse
19+
}
20+
iflen(str)<1 {
21+
returnfalse
22+
}
23+
returnusernameValid.MatchString(str)
24+
}
25+
26+
// UsernameFrom returns a best-effort username from the provided string.
27+
//
28+
// It first attempts to validate the incoming string, which will
29+
// be returned if it is valid. It then will attempt to extract
30+
// the username from an email address. If no success happens during
31+
// these steps, a random username will be returned.
32+
funcUsernameFrom(strstring)string {
33+
ifUsernameValid(str) {
34+
returnstr
35+
}
36+
emailAt:=strings.LastIndex(str,"@")
37+
ifemailAt>=0 {
38+
str=str[:emailAt]
39+
}
40+
str=usernameReplace.ReplaceAllString(str,"")
41+
ifUsernameValid(str) {
42+
returnstr
43+
}
44+
returnstrings.ReplaceAll(namesgenerator.GetRandomName(1),"_","-")
45+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp