diff --git a/cmd/testidp/README.md b/cmd/testidp/README.md new file mode 100644 index 0000000000000..2dac79af8602b --- /dev/null +++ b/cmd/testidp/README.md @@ -0,0 +1,17 @@ +# How to use + +Start the idp service: + +```bash +$ go run main.go +2024-01-10 16:48:01.415 [info] stdlib: 2024/01/10 10:48:01 IDP Issuer URL http://127.0.0.1:44517 +2024-01-10 16:48:01.415 [info] stdlib: 2024/01/10 10:48:01 Oauth Flags +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"]}]' +2024-01-10 16:48:01.415 [info] stdlib: 2024/01/10 10:48:01 Press Ctrl+C to exit +``` + +Then use the flag into your coderd instance: + +```bash +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"]}]' +``` diff --git a/cmd/testidp/main.go b/cmd/testidp/main.go new file mode 100644 index 0000000000000..fd96d0b84a87e --- /dev/null +++ b/cmd/testidp/main.go @@ -0,0 +1,58 @@ +package main + +import ( + "flag" + "log" + "os" + "os/signal" + "testing" + + "github.com/golang-jwt/jwt/v4" + + "github.com/coder/coder/v2/coderd/coderdtest/oidctest" +) + +func main() { + testing.Init() + _ = flag.Set("test.timeout", "0") + + flag.Parse() + + // This is just a way to run tests outside go test + testing.Main(func(pat, str string) (bool, error) { + return true, nil + }, []testing.InternalTest{ + { + Name: "Run Fake IDP", + F: RunIDP(), + }, + }, nil, nil) +} + +// RunIDP needs the testing.T because our oidctest package requires the +// testing.T. +func RunIDP() func(t *testing.T) { + return func(t *testing.T) { + idp := oidctest.NewFakeIDP(t, + oidctest.WithServing(), + oidctest.WithStaticUserInfo(jwt.MapClaims{}), + oidctest.WithDefaultIDClaims(jwt.MapClaims{}), + ) + id, sec := idp.AppCredentials() + prov := idp.WellknownConfig() + + log.Println("IDP Issuer URL", idp.IssuerURL()) + log.Println("Coderd Flags") + 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"]}]'`, + id, sec, prov.AuthURL, prov.TokenURL, prov.UserInfoURL, + ) + + log.Println("Press Ctrl+C to exit") + c := make(chan os.Signal, 1) + signal.Notify(c, os.Interrupt) + + // Block until ctl+c + <-c + log.Println("Closing") + } +} diff --git a/coderd/coderdtest/oidctest/idp.go b/coderd/coderdtest/oidctest/idp.go index bb758d60f5d0a..6b6936e3465e7 100644 --- a/coderd/coderdtest/oidctest/idp.go +++ b/coderd/coderdtest/oidctest/idp.go @@ -78,9 +78,12 @@ type FakeIDP struct { // "Authorized Redirect URLs". This can be used to emulate that. hookValidRedirectURL func(redirectURL string) error hookUserInfo func(email string) (jwt.MapClaims, error) - hookMutateToken func(token map[string]interface{}) - fakeCoderd func(req *http.Request) (*http.Response, error) - hookOnRefresh func(email string) error + // defaultIDClaims is if a new client connects and we didn't preset + // some claims. + defaultIDClaims jwt.MapClaims + hookMutateToken func(token map[string]interface{}) + fakeCoderd func(req *http.Request) (*http.Response, error) + hookOnRefresh func(email string) error // Custom authentication for the client. This is useful if you want // to test something like PKI auth vs a client_secret. hookAuthenticateClient func(t testing.TB, req *http.Request) (url.Values, error) @@ -162,6 +165,12 @@ func WithStaticUserInfo(info jwt.MapClaims) func(*FakeIDP) { } } +func WithDefaultIDClaims(claims jwt.MapClaims) func(*FakeIDP) { + return func(f *FakeIDP) { + f.defaultIDClaims = claims + } +} + func WithDynamicUserInfo(userInfoFunc func(email string) (jwt.MapClaims, error)) func(*FakeIDP) { return func(f *FakeIDP) { f.hookUserInfo = userInfoFunc @@ -679,7 +688,7 @@ func (f *FakeIDP) httpHandler(t testing.TB) http.Handler { // Always invalidate the code after it is used. f.codeToStateMap.Delete(code) - idTokenClaims, ok := f.stateToIDTokenClaims.Load(stateStr) + idTokenClaims, ok := f.getClaims(f.stateToIDTokenClaims, stateStr) if !ok { t.Errorf("missing id token claims") http.Error(rw, "missing id token claims", http.StatusBadRequest) @@ -699,7 +708,7 @@ func (f *FakeIDP) httpHandler(t testing.TB) http.Handler { return } - idTokenClaims, ok := f.refreshIDTokenClaims.Load(refreshToken) + idTokenClaims, ok := f.getClaims(f.refreshIDTokenClaims, refreshToken) if !ok { t.Errorf("missing id token claims in refresh") 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 return cfg } +func (f *FakeIDP) AppCredentials() (clientID string, clientSecret string) { + return f.clientID, f.clientSecret +} + // OIDCConfig returns the OIDC config to use for Coderd. func (f *FakeIDP) OIDCConfig(t testing.TB, scopes []string, opts ...func(cfg *coderd.OIDCConfig)) *coderd.OIDCConfig { t.Helper() @@ -1023,6 +1036,17 @@ func (f *FakeIDP) OIDCConfig(t testing.TB, scopes []string, opts ...func(cfg *co return cfg } +func (f *FakeIDP) getClaims(m *syncmap.Map[string, jwt.MapClaims], key string) (jwt.MapClaims, bool) { + v, ok := m.Load(key) + if !ok { + if f.defaultIDClaims != nil { + return f.defaultIDClaims, true + } + return nil, false + } + return v, true +} + func httpErrorCode(defaultCode int, err error) int { var stautsErr statusHookError status := defaultCode diff --git a/codersdk/deployment.go b/codersdk/deployment.go index c3e2db238e4ef..e0dcc4fe4ded2 100644 --- a/codersdk/deployment.go +++ b/codersdk/deployment.go @@ -1790,11 +1790,10 @@ Write out the current server config as YAML to stdout.`, // Env handling is done in cli.ReadGitAuthFromEnvironment Name: "External Auth Providers", Description: "External Authentication providers.", - // We need extra scrutiny to ensure this works, is documented, and - // tested before enabling. - YAML: "externalAuthProviders", - Value: &c.ExternalAuthConfigs, - Hidden: true, + YAML: "externalAuthProviders", + Flag: "external-auth-providers", + Value: &c.ExternalAuthConfigs, + Hidden: true, }, { Name: "Custom wgtunnel Host", 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