Skip to content

Commit e3ad958

Browse files
authored
chore: allow running fake idp with coderd dev (#11555)
* chore: allow running fake idp with coderd dev
1 parent c91b885 commit e3ad958

File tree

4 files changed

+108
-10
lines changed

4 files changed

+108
-10
lines changed

cmd/testidp/README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# How to use
2+
3+
Start the idp service:
4+
5+
```bash
6+
$ go run main.go
7+
2024-01-10 16:48:01.415 [info] stdlib: 2024/01/10 10:48:01 IDP Issuer URL http://127.0.0.1:44517
8+
2024-01-10 16:48:01.415 [info] stdlib: 2024/01/10 10:48:01 Oauth Flags
9+
2024-01-10 16:48:01.415 [info] stdlib: 2024/01/10 10:48:01 --external-auth-providers='[{"type":"fake","client_id":"f2df566b-a1c9-407a-8b75-480db45c6476","client_secret":"55aca4e3-7b94-44b6-9f45-ecb5e81c560d","auth_url":"http://127.0.0.1:44517/oauth2/authorize","token_url":"http://127.0.0.1:44517/oauth2/token","validate_url":"http://127.0.0.1:44517/oauth2/userinfo","scopes":["openid","email","profile"]}]'
10+
2024-01-10 16:48:01.415 [info] stdlib: 2024/01/10 10:48:01 Press Ctrl+C to exit
11+
```
12+
13+
Then use the flag into your coderd instance:
14+
15+
```bash
16+
develop.sh -- --external-auth-providers='[{"type":"fake","client_id":"f2df566b-a1c9-407a-8b75-480db45c6476","client_secret":"55aca4e3-7b94-44b6-9f45-ecb5e81c560d","auth_url":"http://127.0.0.1:44517/oauth2/authorize","token_url":"http://127.0.0.1:44517/oauth2/token","validate_url":"http://127.0.0.1:44517/oauth2/userinfo","scopes":["openid","email","profile"]}]'
17+
```

cmd/testidp/main.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package main
2+
3+
import (
4+
"flag"
5+
"log"
6+
"os"
7+
"os/signal"
8+
"testing"
9+
10+
"github.com/golang-jwt/jwt/v4"
11+
12+
"github.com/coder/coder/v2/coderd/coderdtest/oidctest"
13+
)
14+
15+
func main() {
16+
testing.Init()
17+
_ = flag.Set("test.timeout", "0")
18+
19+
flag.Parse()
20+
21+
// This is just a way to run tests outside go test
22+
testing.Main(func(pat, str string) (bool, error) {
23+
return true, nil
24+
}, []testing.InternalTest{
25+
{
26+
Name: "Run Fake IDP",
27+
F: RunIDP(),
28+
},
29+
}, nil, nil)
30+
}
31+
32+
// RunIDP needs the testing.T because our oidctest package requires the
33+
// testing.T.
34+
func RunIDP() func(t *testing.T) {
35+
return func(t *testing.T) {
36+
idp := oidctest.NewFakeIDP(t,
37+
oidctest.WithServing(),
38+
oidctest.WithStaticUserInfo(jwt.MapClaims{}),
39+
oidctest.WithDefaultIDClaims(jwt.MapClaims{}),
40+
)
41+
id, sec := idp.AppCredentials()
42+
prov := idp.WellknownConfig()
43+
44+
log.Println("IDP Issuer URL", idp.IssuerURL())
45+
log.Println("Coderd Flags")
46+
log.Printf(`--external-auth-providers='[{"type":"fake","client_id":"%s","client_secret":"%s","auth_url":"%s","token_url":"%s","validate_url":"%s","scopes":["openid","email","profile"]}]'`,
47+
id, sec, prov.AuthURL, prov.TokenURL, prov.UserInfoURL,
48+
)
49+
50+
log.Println("Press Ctrl+C to exit")
51+
c := make(chan os.Signal, 1)
52+
signal.Notify(c, os.Interrupt)
53+
54+
// Block until ctl+c
55+
<-c
56+
log.Println("Closing")
57+
}
58+
}

coderd/coderdtest/oidctest/idp.go

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,12 @@ type FakeIDP struct {
7878
// "Authorized Redirect URLs". This can be used to emulate that.
7979
hookValidRedirectURL func(redirectURL string) error
8080
hookUserInfo func(email string) (jwt.MapClaims, error)
81-
hookMutateToken func(token map[string]interface{})
82-
fakeCoderd func(req *http.Request) (*http.Response, error)
83-
hookOnRefresh func(email string) error
81+
// defaultIDClaims is if a new client connects and we didn't preset
82+
// some claims.
83+
defaultIDClaims jwt.MapClaims
84+
hookMutateToken func(token map[string]interface{})
85+
fakeCoderd func(req *http.Request) (*http.Response, error)
86+
hookOnRefresh func(email string) error
8487
// Custom authentication for the client. This is useful if you want
8588
// to test something like PKI auth vs a client_secret.
8689
hookAuthenticateClient func(t testing.TB, req *http.Request) (url.Values, error)
@@ -162,6 +165,12 @@ func WithStaticUserInfo(info jwt.MapClaims) func(*FakeIDP) {
162165
}
163166
}
164167

168+
func WithDefaultIDClaims(claims jwt.MapClaims) func(*FakeIDP) {
169+
return func(f *FakeIDP) {
170+
f.defaultIDClaims = claims
171+
}
172+
}
173+
165174
func WithDynamicUserInfo(userInfoFunc func(email string) (jwt.MapClaims, error)) func(*FakeIDP) {
166175
return func(f *FakeIDP) {
167176
f.hookUserInfo = userInfoFunc
@@ -679,7 +688,7 @@ func (f *FakeIDP) httpHandler(t testing.TB) http.Handler {
679688
// Always invalidate the code after it is used.
680689
f.codeToStateMap.Delete(code)
681690

682-
idTokenClaims, ok := f.stateToIDTokenClaims.Load(stateStr)
691+
idTokenClaims, ok := f.getClaims(f.stateToIDTokenClaims, stateStr)
683692
if !ok {
684693
t.Errorf("missing id token claims")
685694
http.Error(rw, "missing id token claims", http.StatusBadRequest)
@@ -699,7 +708,7 @@ func (f *FakeIDP) httpHandler(t testing.TB) http.Handler {
699708
return
700709
}
701710

702-
idTokenClaims, ok := f.refreshIDTokenClaims.Load(refreshToken)
711+
idTokenClaims, ok := f.getClaims(f.refreshIDTokenClaims, refreshToken)
703712
if !ok {
704713
t.Errorf("missing id token claims in refresh")
705714
http.Error(rw, "missing id token claims in refresh", http.StatusBadRequest)
@@ -971,6 +980,10 @@ func (f *FakeIDP) ExternalAuthConfig(t testing.TB, id string, custom *ExternalAu
971980
return cfg
972981
}
973982

983+
func (f *FakeIDP) AppCredentials() (clientID string, clientSecret string) {
984+
return f.clientID, f.clientSecret
985+
}
986+
974987
// OIDCConfig returns the OIDC config to use for Coderd.
975988
func (f *FakeIDP) OIDCConfig(t testing.TB, scopes []string, opts ...func(cfg *coderd.OIDCConfig)) *coderd.OIDCConfig {
976989
t.Helper()
@@ -1023,6 +1036,17 @@ func (f *FakeIDP) OIDCConfig(t testing.TB, scopes []string, opts ...func(cfg *co
10231036
return cfg
10241037
}
10251038

1039+
func (f *FakeIDP) getClaims(m *syncmap.Map[string, jwt.MapClaims], key string) (jwt.MapClaims, bool) {
1040+
v, ok := m.Load(key)
1041+
if !ok {
1042+
if f.defaultIDClaims != nil {
1043+
return f.defaultIDClaims, true
1044+
}
1045+
return nil, false
1046+
}
1047+
return v, true
1048+
}
1049+
10261050
func httpErrorCode(defaultCode int, err error) int {
10271051
var stautsErr statusHookError
10281052
status := defaultCode

codersdk/deployment.go

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1790,11 +1790,10 @@ Write out the current server config as YAML to stdout.`,
17901790
// Env handling is done in cli.ReadGitAuthFromEnvironment
17911791
Name: "External Auth Providers",
17921792
Description: "External Authentication providers.",
1793-
// We need extra scrutiny to ensure this works, is documented, and
1794-
// tested before enabling.
1795-
YAML: "externalAuthProviders",
1796-
Value: &c.ExternalAuthConfigs,
1797-
Hidden: true,
1793+
YAML: "externalAuthProviders",
1794+
Flag: "external-auth-providers",
1795+
Value: &c.ExternalAuthConfigs,
1796+
Hidden: true,
17981797
},
17991798
{
18001799
Name: "Custom wgtunnel Host",

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