From 73e4e50d44e75e0232ea6858ca839b4d86dcd548 Mon Sep 17 00:00:00 2001 From: "David J. Allen" Date: Mon, 29 Apr 2024 18:45:30 -0600 Subject: [PATCH 01/12] Made it possible to override certain example IDP endpoints --- cmd/serve.go | 19 ++++++++++++++----- internal/oidc/oidc.go | 22 ++++++++++++++++++++++ internal/server/idp.go | 17 ++++++++++++----- internal/server/server.go | 17 +++++++++-------- 4 files changed, 57 insertions(+), 18 deletions(-) diff --git a/cmd/serve.go b/cmd/serve.go index 67cec1a..5318c87 100644 --- a/cmd/serve.go +++ b/cmd/serve.go @@ -2,6 +2,7 @@ package cmd import ( opaal "davidallendj/opaal/internal" + "davidallendj/opaal/internal/oidc" "errors" "fmt" "net/http" @@ -9,23 +10,31 @@ import ( "github.com/spf13/cobra" ) -var exampleCmd = &cobra.Command{ +var ( + endpoints oidc.Endpoints +) + +var serveCmd = &cobra.Command{ Use: "serve", - Short: "Start an simple identity provider server", + Short: "Start an simple, bare minimal identity provider server", Long: "The built-in identity provider is not (nor meant to be) a complete OIDC implementation and behaves like an external IdP", Run: func(cmd *cobra.Command, args []string) { s := opaal.NewServerWithConfig(&config) // FIXME: change how the server address is set with `NewServerWithConfig` s.Server.Addr = fmt.Sprintf("%s:%d", s.Issuer.Host, s.Issuer.Port) - err := s.StartIdentityProvider() + err := s.StartIdentityProvider(endpoints) if errors.Is(err, http.ErrServerClosed) { fmt.Printf("Identity provider server closed.\n") } else if err != nil { - fmt.Errorf("failed to start server: %v", err) + fmt.Printf("failed to start server: %v", err) } }, } func init() { - rootCmd.AddCommand(exampleCmd) + serveCmd.Flags().StringVar(&endpoints.Authorization, "endpoints.authorization", "", "set the authorization endpoint for the identity provider") + serveCmd.Flags().StringVar(&endpoints.Token, "endpoints.token", "", "set the token endpoint for the identity provider") + serveCmd.Flags().StringVar(&endpoints.JwksUri, "endpoints.jwks_uri", "", "set the JWKS endpoints for the identity provider") + + rootCmd.AddCommand(serveCmd) } diff --git a/internal/oidc/oidc.go b/internal/oidc/oidc.go index 2ec1f9c..f3382fc 100644 --- a/internal/oidc/oidc.go +++ b/internal/oidc/oidc.go @@ -164,3 +164,25 @@ func (p *IdentityProvider) FetchJwks() error { return nil } + +func (p *IdentityProvider) UpdateEndpoints(other *IdentityProvider) { + UpdateEndpoints(&p.Endpoints, &other.Endpoints) +} + +func UpdateEndpoints(eps *Endpoints, other *Endpoints) { + // only update endpoints that are not empty + var UpdateIf = func(ep *string, s string) { + if ep != nil { + if *ep != "" { + *ep = s + } + } + } + UpdateIf(&eps.Config, other.Config) + UpdateIf(&eps.Authorization, other.Authorization) + UpdateIf(&eps.Token, other.Token) + UpdateIf(&eps.Revocation, other.Revocation) + UpdateIf(&eps.Introspection, other.Introspection) + UpdateIf(&eps.UserInfo, other.UserInfo) + UpdateIf(&eps.JwksUri, other.JwksUri) +} diff --git a/internal/server/idp.go b/internal/server/idp.go index 67c70b1..4822e91 100644 --- a/internal/server/idp.go +++ b/internal/server/idp.go @@ -22,7 +22,7 @@ import ( "github.com/lestrrat-go/jwx/v2/jwt" ) -func (s *Server) StartIdentityProvider() error { +func (s *Server) StartIdentityProvider(eps oidc.Endpoints) error { // NOTE: this example does NOT implement CSRF tokens nor use them // create an example identity provider @@ -38,6 +38,14 @@ func (s *Server) StartIdentityProvider() error { callback = "/oidc/callback" } + // update endpoints that have values set + defaultEps := oidc.Endpoints{ + Authorization: "http://" + s.Addr + "/oauth/authorize", + Token: "http://" + s.Addr + "/oauth/token", + JwksUri: "http://" + s.Addr + "/.well-known/jwks.json", + } + oidc.UpdateEndpoints(&eps, &defaultEps) + // generate key pair used to sign JWKS and create JWTs privateKey, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { @@ -72,14 +80,13 @@ func (s *Server) StartIdentityProvider() error { w.Write(b) }) - // TODO: create .well-known openid configuration r.HandleFunc("/.well-known/openid-configuration", func(w http.ResponseWriter, r *http.Request) { // create config JSON to serve with GET request config := map[string]any{ "issuer": "http://" + s.Addr, - "authorization_endpoint": "http://" + s.Addr + "/oauth/authorize", - "token_endpoint": "http://" + s.Addr + "/oauth/token", - "jwks_uri": "http://" + s.Addr + "/.well-known/jwks.json", + "authorization_endpoint": eps.Authorization, + "token_endpoint": eps.Token, + "jwks_uri": eps.JwksUri, "scopes_supported": []string{ "openid", "profile", diff --git a/internal/server/server.go b/internal/server/server.go index 3c7507b..c829a34 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -18,16 +18,17 @@ import ( type Server struct { *http.Server - Host string `yaml:"host"` - Port int `yaml:"port"` - Callback string `yaml:"callback"` - State string `yaml:"state"` - Issuer Issuer `yaml:"issuer"` + Host string `yaml:"host"` + Port int `yaml:"port"` + Callback string `yaml:"callback"` + State string `yaml:"state"` + Issuer IdentityProviderServer `yaml:"issuer"` } -type Issuer struct { - Host string `yaml:"host"` - Port int `yaml:"port"` +type IdentityProviderServer struct { + Host string `yaml:"host"` + Port int `yaml:"port"` + Endpoints oidc.Endpoints `yaml:"endpoints"` } type ServerParams struct { From 67683e9fcaefc4a09536e5a6ec3dcbfdf845e949 Mon Sep 17 00:00:00 2001 From: "David J. Allen" Date: Tue, 30 Apr 2024 09:01:05 -0600 Subject: [PATCH 02/12] Fixed small issues with not building --- internal/login.go | 5 +++-- internal/new.go | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/internal/login.go b/internal/login.go index ab51747..b6aaf84 100644 --- a/internal/login.go +++ b/internal/login.go @@ -42,8 +42,9 @@ func Login(config *Config) error { AuthProvider: &oidc.IdentityProvider{ Issuer: config.Authorization.Endpoints.Issuer, Endpoints: oidc.Endpoints{ - Config: config.Authorization.Endpoints.Config, - JwksUri: config.Authorization.Endpoints.JwksUri, + Config: config.Authorization.Endpoints.Config, + Authorization: config.Authorization.Endpoints.Authorize, + JwksUri: config.Authorization.Endpoints.JwksUri, }, }, JwtBearerEndpoints: flows.JwtBearerFlowEndpoints{ diff --git a/internal/new.go b/internal/new.go index 55d9cda..ce20aee 100644 --- a/internal/new.go +++ b/internal/new.go @@ -90,7 +90,7 @@ func NewServerWithConfig(conf *Config) *server.Server { }, Host: host, Port: port, - Issuer: server.Issuer{ + Issuer: server.IdentityProviderServer{ Host: conf.Server.Issuer.Host, Port: conf.Server.Issuer.Port, }, From e940dc2dd9a51ec38ec8179d1127dc57956668ce Mon Sep 17 00:00:00 2001 From: "David J. Allen" Date: Tue, 30 Apr 2024 11:32:28 -0600 Subject: [PATCH 03/12] Fixed IDP endpoint overrides not working correctly --- cmd/serve.go | 2 +- internal/config.go | 11 ++++++++++- internal/new.go | 5 +++-- internal/oidc/oidc.go | 19 ++++++++++--------- internal/server/idp.go | 10 +++++----- 5 files changed, 29 insertions(+), 18 deletions(-) diff --git a/cmd/serve.go b/cmd/serve.go index 5318c87..b68b814 100644 --- a/cmd/serve.go +++ b/cmd/serve.go @@ -22,7 +22,7 @@ var serveCmd = &cobra.Command{ s := opaal.NewServerWithConfig(&config) // FIXME: change how the server address is set with `NewServerWithConfig` s.Server.Addr = fmt.Sprintf("%s:%d", s.Issuer.Host, s.Issuer.Port) - err := s.StartIdentityProvider(endpoints) + err := s.StartIdentityProvider() if errors.Is(err, http.ErrServerClosed) { fmt.Printf("Identity provider server closed.\n") } else if err != nil { diff --git a/internal/config.go b/internal/config.go index 8e2139f..cb7bef1 100644 --- a/internal/config.go +++ b/internal/config.go @@ -2,6 +2,7 @@ package opaal import ( "davidallendj/opaal/internal/oauth" + "davidallendj/opaal/internal/oidc" "log" "os" "path/filepath" @@ -72,11 +73,18 @@ type Config struct { } func NewConfig() Config { - return Config{ + config := Config{ Version: goutil.GetCommit(), Server: server.Server{ Host: "127.0.0.1", Port: 3333, + Issuer: server.IdentityProviderServer{ + Endpoints: oidc.Endpoints{ + Authorization: "http://127.0.0.1/oauth/authorize", + Token: "http://127.0.0.1/oauth/token", + JwksUri: "http://127.0.0.1/.well-known/jwks.json", + }, + }, }, Options: Options{ RunOnce: true, @@ -99,6 +107,7 @@ func NewConfig() Config { }, }, } + return config } func LoadConfig(path string) Config { diff --git a/internal/new.go b/internal/new.go index ce20aee..7e3e35c 100644 --- a/internal/new.go +++ b/internal/new.go @@ -91,8 +91,9 @@ func NewServerWithConfig(conf *Config) *server.Server { Host: host, Port: port, Issuer: server.IdentityProviderServer{ - Host: conf.Server.Issuer.Host, - Port: conf.Server.Issuer.Port, + Host: conf.Server.Issuer.Host, + Port: conf.Server.Issuer.Port, + Endpoints: conf.Server.Issuer.Endpoints, }, } return server diff --git a/internal/oidc/oidc.go b/internal/oidc/oidc.go index f3382fc..206f68f 100644 --- a/internal/oidc/oidc.go +++ b/internal/oidc/oidc.go @@ -171,18 +171,19 @@ func (p *IdentityProvider) UpdateEndpoints(other *IdentityProvider) { func UpdateEndpoints(eps *Endpoints, other *Endpoints) { // only update endpoints that are not empty - var UpdateIf = func(ep *string, s string) { + var UpdateIfEmpty = func(ep *string, s string) { if ep != nil { - if *ep != "" { + if *ep == "" { *ep = s + fmt.Printf("updated %s\n", s) } } } - UpdateIf(&eps.Config, other.Config) - UpdateIf(&eps.Authorization, other.Authorization) - UpdateIf(&eps.Token, other.Token) - UpdateIf(&eps.Revocation, other.Revocation) - UpdateIf(&eps.Introspection, other.Introspection) - UpdateIf(&eps.UserInfo, other.UserInfo) - UpdateIf(&eps.JwksUri, other.JwksUri) + UpdateIfEmpty(&eps.Config, other.Config) + UpdateIfEmpty(&eps.Authorization, other.Authorization) + UpdateIfEmpty(&eps.Token, other.Token) + UpdateIfEmpty(&eps.Revocation, other.Revocation) + UpdateIfEmpty(&eps.Introspection, other.Introspection) + UpdateIfEmpty(&eps.UserInfo, other.UserInfo) + UpdateIfEmpty(&eps.JwksUri, other.JwksUri) } diff --git a/internal/server/idp.go b/internal/server/idp.go index 4822e91..f763778 100644 --- a/internal/server/idp.go +++ b/internal/server/idp.go @@ -22,7 +22,7 @@ import ( "github.com/lestrrat-go/jwx/v2/jwt" ) -func (s *Server) StartIdentityProvider(eps oidc.Endpoints) error { +func (s *Server) StartIdentityProvider() error { // NOTE: this example does NOT implement CSRF tokens nor use them // create an example identity provider @@ -44,7 +44,7 @@ func (s *Server) StartIdentityProvider(eps oidc.Endpoints) error { Token: "http://" + s.Addr + "/oauth/token", JwksUri: "http://" + s.Addr + "/.well-known/jwks.json", } - oidc.UpdateEndpoints(&eps, &defaultEps) + oidc.UpdateEndpoints(&s.Issuer.Endpoints, &defaultEps) // generate key pair used to sign JWKS and create JWTs privateKey, err := rsa.GenerateKey(rand.Reader, 2048) @@ -84,9 +84,9 @@ func (s *Server) StartIdentityProvider(eps oidc.Endpoints) error { // create config JSON to serve with GET request config := map[string]any{ "issuer": "http://" + s.Addr, - "authorization_endpoint": eps.Authorization, - "token_endpoint": eps.Token, - "jwks_uri": eps.JwksUri, + "authorization_endpoint": s.Issuer.Endpoints.Authorization, + "token_endpoint": s.Issuer.Endpoints.Token, + "jwks_uri": s.Issuer.Endpoints.JwksUri, "scopes_supported": []string{ "openid", "profile", From 2edc624c017b4a55210b9db82b499f6dcbe63fb3 Mon Sep 17 00:00:00 2001 From: "David J. Allen" Date: Tue, 30 Apr 2024 12:28:19 -0600 Subject: [PATCH 04/12] Resetted the default IDP endpoint values --- internal/config.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/config.go b/internal/config.go index cb7bef1..9108ac1 100644 --- a/internal/config.go +++ b/internal/config.go @@ -80,9 +80,9 @@ func NewConfig() Config { Port: 3333, Issuer: server.IdentityProviderServer{ Endpoints: oidc.Endpoints{ - Authorization: "http://127.0.0.1/oauth/authorize", - Token: "http://127.0.0.1/oauth/token", - JwksUri: "http://127.0.0.1/.well-known/jwks.json", + Authorization: "", + Token: "", + JwksUri: "", }, }, }, From bc5e6934254e236a0e67192d5c95045ff89ea352 Mon Sep 17 00:00:00 2001 From: "David J. Allen" Date: Tue, 30 Apr 2024 12:45:02 -0600 Subject: [PATCH 05/12] Changed the IDP oauth endpoints to oauth2 --- internal/server/idp.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/server/idp.go b/internal/server/idp.go index f763778..35b4051 100644 --- a/internal/server/idp.go +++ b/internal/server/idp.go @@ -173,7 +173,7 @@ func (s *Server) StartIdentityProvider() error { http.Redirect(w, r, "/browser/login", http.StatusUnauthorized) } }) - r.HandleFunc("/oauth/token", func(w http.ResponseWriter, r *http.Request) { + r.HandleFunc("/oauth2/token", func(w http.ResponseWriter, r *http.Request) { r.ParseForm() // check for authorization code and make sure it's valid @@ -247,7 +247,7 @@ func (s *Server) StartIdentityProvider() error { fmt.Printf("bearer: %s\n", string(b)) w.Write(b) }) - r.HandleFunc("/oauth/authorize", func(w http.ResponseWriter, r *http.Request) { + r.HandleFunc("/oauth2/authorize", func(w http.ResponseWriter, r *http.Request) { var ( responseType = r.URL.Query().Get("response_type") clientId = r.URL.Query().Get("client_id") From cbb3e6f851d54c2fb5bc3161dbd0e77e822bc8d7 Mon Sep 17 00:00:00 2001 From: "David J. Allen" Date: Tue, 30 Apr 2024 14:43:50 -0600 Subject: [PATCH 06/12] Updated login page hint --- pages/login.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/login.html b/pages/login.html index e55f5cf..c25f8c3 100644 --- a/pages/login.html +++ b/pages/login.html @@ -7,7 +7,7 @@

Forgot Username?
- + \ No newline at end of file From 7022801fe92cd45e86491d61626107e59d433ce3 Mon Sep 17 00:00:00 2001 From: "David J. Allen" Date: Tue, 30 Apr 2024 14:44:57 -0600 Subject: [PATCH 07/12] Implemented IDP registered clients and callbacks --- internal/server/idp.go | 42 ++++++++++++++++++++++----------------- internal/server/server.go | 6 ------ 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/internal/server/idp.go b/internal/server/idp.go index 35b4051..e7fea40 100644 --- a/internal/server/idp.go +++ b/internal/server/idp.go @@ -3,7 +3,6 @@ package server import ( "crypto/rand" "crypto/rsa" - "davidallendj/opaal/internal/oauth" "davidallendj/opaal/internal/oidc" "encoding/json" "fmt" @@ -22,6 +21,22 @@ import ( "github.com/lestrrat-go/jwx/v2/jwt" ) +// TODO: make this a completely separate server +type IdentityProviderServer struct { + Host string `yaml:"host"` + Port int `yaml:"port"` + Endpoints oidc.Endpoints `yaml:"endpoints"` + Clients []RegisteredClient `yaml:"clients"` +} + +// NOTE: could we use a oauth.Client here instead?? +type RegisteredClient struct { + Id string `yaml:"id"` + Secret string `yaml:"secret"` + Name string `yaml:"name"` + RedirectUris []string `yaml:"redirect-uris"` +} + func (s *Server) StartIdentityProvider() error { // NOTE: this example does NOT implement CSRF tokens nor use them @@ -29,15 +44,9 @@ func (s *Server) StartIdentityProvider() error { var ( r = chi.NewRouter() // clients = []oauth.Client{} - callback = "" activeCodes = []string{} ) - // check if callback is set - if s.Callback == "" { - callback = "/oidc/callback" - } - // update endpoints that have values set defaultEps := oidc.Endpoints{ Authorization: "http://" + s.Addr + "/oauth/authorize", @@ -138,21 +147,18 @@ func (s *Server) StartIdentityProvider() error { username := r.Form.Get("username") password := r.Form.Get("password") + if len(s.Issuer.Clients) <= 0 { + fmt.Printf("no registered clients found with identity provider (add them in config)\n") + return + } + // example username and password so do simplified authorization code flow - if username == "ochami" && password == "ochami" { - client := oauth.Client{ - Id: "ochami", - Secret: "ochami", - Name: "ochami", - Provider: oidc.IdentityProvider{ - Issuer: "http://127.0.0.1:3333", - }, - RedirectUris: []string{fmt.Sprintf("http://%s:%d%s", s.Host, s.Port, callback)}, - } + if username == "openchami" && password == "openchami" { + client := s.Issuer.Clients[0] // check if there are any redirect URIs supplied if len(client.RedirectUris) <= 0 { - fmt.Printf("no redirect URIs found") + fmt.Printf("no redirect URIs found for client %s (ID: %s)\n", client.Name, client.Id) return } for _, url := range client.RedirectUris { diff --git a/internal/server/server.go b/internal/server/server.go index c829a34..30eac4d 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -25,12 +25,6 @@ type Server struct { Issuer IdentityProviderServer `yaml:"issuer"` } -type IdentityProviderServer struct { - Host string `yaml:"host"` - Port int `yaml:"port"` - Endpoints oidc.Endpoints `yaml:"endpoints"` -} - type ServerParams struct { AuthProvider *oidc.IdentityProvider Verbose bool From e929fac09e7af89ab639180ccdf68296543cc6cd Mon Sep 17 00:00:00 2001 From: "David J. Allen" Date: Tue, 30 Apr 2024 16:03:23 -0600 Subject: [PATCH 08/12] Fixed some minor issues --- internal/new.go | 1 + internal/oidc/oidc.go | 1 - internal/server/idp.go | 14 +++++++++----- internal/server/server.go | 9 +++++---- 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/internal/new.go b/internal/new.go index 7e3e35c..2799d88 100644 --- a/internal/new.go +++ b/internal/new.go @@ -94,6 +94,7 @@ func NewServerWithConfig(conf *Config) *server.Server { Host: conf.Server.Issuer.Host, Port: conf.Server.Issuer.Port, Endpoints: conf.Server.Issuer.Endpoints, + Clients: conf.Server.Issuer.Clients, }, } return server diff --git a/internal/oidc/oidc.go b/internal/oidc/oidc.go index 206f68f..f52e0c4 100644 --- a/internal/oidc/oidc.go +++ b/internal/oidc/oidc.go @@ -175,7 +175,6 @@ func UpdateEndpoints(eps *Endpoints, other *Endpoints) { if ep != nil { if *ep == "" { *ep = s - fmt.Printf("updated %s\n", s) } } } diff --git a/internal/server/idp.go b/internal/server/idp.go index e7fea40..bb82a28 100644 --- a/internal/server/idp.go +++ b/internal/server/idp.go @@ -49,8 +49,8 @@ func (s *Server) StartIdentityProvider() error { // update endpoints that have values set defaultEps := oidc.Endpoints{ - Authorization: "http://" + s.Addr + "/oauth/authorize", - Token: "http://" + s.Addr + "/oauth/token", + Authorization: "http://" + s.Addr + "/oauth2/authorize", + Token: "http://" + s.Addr + "/oauth2/token", JwksUri: "http://" + s.Addr + "/.well-known/jwks.json", } oidc.UpdateEndpoints(&s.Issuer.Endpoints, &defaultEps) @@ -266,9 +266,13 @@ func (s *Server) StartIdentityProvider() error { return } - // check that we're using the default registered client - if clientId != "ochami" { - fmt.Printf("invalid client\n") + // find a valid client + index := slices.IndexFunc(s.Issuer.Clients, func(c RegisteredClient) bool { + fmt.Printf("%s ? %s\n", c.Id, clientId) + return c.Id == clientId + }) + if index < 0 { + fmt.Printf("no valid client found") return } diff --git a/internal/server/server.go b/internal/server/server.go index 30eac4d..16b7b51 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -57,7 +57,7 @@ func (s *Server) StartLogin(clients []oauth.Client, params ServerParams) error { // make the login page SSO buttons and authorization URLs to write to stdout buttons := "" - fmt.Printf("Login with external identity providers: \n") + fmt.Printf("Login with an identity provider: \n") for i, client := range clients { // fetch provider configuration before adding button p, err := oidc.FetchServerConfig(client.Provider.Issuer) @@ -74,8 +74,7 @@ func (s *Server) StartLogin(clients []oauth.Client, params ServerParams) error { clients[i].Provider = *p buttons += makeButton(fmt.Sprintf("/login?sso=%s", client.Id), client.Name) - url := client.BuildAuthorizationUrl(s.State) - fmt.Printf("\t%s\n", url) + fmt.Printf("\t%s: /login?sso=%s\n", client.Name, client.Id) } var code string @@ -115,7 +114,9 @@ func (s *Server) StartLogin(clients []oauth.Client, params ServerParams) error { client = &clients[index] url := client.BuildAuthorizationUrl(s.State) - fmt.Printf("Redirect URL: %s\n", url) + if params.Verbose { + fmt.Printf("Redirect URL: %s\n", url) + } http.Redirect(w, r, url, http.StatusFound) return } From b304361ce9085665af0bcbf4ac2a6b1f0e75cab9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tiziano=20M=C3=BCller?= Date: Fri, 24 May 2024 16:34:38 +0200 Subject: [PATCH 09/12] server: fix error reporting and logic for /keys handler restores proper error reporting to include the host dialed, and fixes the tautological comparison `jwks == nil` in the recovery path to properly refetch the server config and try again as intended --- internal/server/server.go | 49 +++++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/internal/server/server.go b/internal/server/server.go index 16b7b51..3fdae97 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -141,38 +141,47 @@ func (s *Server) StartLogin(clients []oauth.Client, params ServerParams) error { p = params.AuthProvider jwks []byte ) - // try and get the JWKS from param first - if p.Endpoints.JwksUri != "" { - err := p.FetchJwks() + + fetchAndMarshal := func() (err error) { + err = p.FetchJwks() if err != nil { - fmt.Printf("failed to fetch keys using JWKS url...trying to fetch config and try again...\n") + fmt.Printf("failed to fetch keys: %v\n", err) + return } jwks, err = json.Marshal(p.KeySet) if err != nil { fmt.Printf("failed to marshal JWKS: %v\n", err) } - } else if p.Endpoints.Config != "" && jwks == nil { - // otherwise, try and fetch the whole config and try again - err := p.FetchServerConfig() - if err != nil { - fmt.Printf("failed to fetch server config: %v\n", err) - http.Redirect(w, r, "/error", http.StatusInternalServerError) - return - } - err = p.FetchJwks() - if err != nil { - fmt.Printf("failed to fetch JWKS after fetching server config: %v\n", err) - http.Redirect(w, r, "/error", http.StatusInternalServerError) + return + } + + // try and get the JWKS from param first + if p.Endpoints.JwksUri != "" { + if err := fetchAndMarshal(); err != nil { + w.Write(jwks) return } } - // forward the JWKS from the authorization server - if jwks == nil { - fmt.Printf("no JWKS was fetched from authorization server\n") - http.Redirect(w, r, "/error", http.StatusInternalServerError) + // otherwise or if fetching the JWKS failed, try and fetch the whole config first and try again + if p.Endpoints.Config != "" { + if err := p.FetchServerConfig(); err != nil { + fmt.Printf("failed to fetch server config: %v\n", err) + http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) + return + } + } else { + fmt.Printf("getting JWKS from param failed and endpoints config unavailable\n") + http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } + + if err := fetchAndMarshal(); err != nil { + fmt.Printf("failed to fetch and marshal JWKS after config update: %v\n", err) + http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) + return + } + w.Write(jwks) }) r.HandleFunc("/token", func(w http.ResponseWriter, r *http.Request) { From 8c01ba897ff72c81cc49b8ced6f1f310c1b930a1 Mon Sep 17 00:00:00 2001 From: David Allen Date: Wed, 12 Jun 2024 12:50:53 -0600 Subject: [PATCH 10/12] Added verbose print to show ID and access tokens from IDP --- internal/flows/jwt_bearer.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/internal/flows/jwt_bearer.go b/internal/flows/jwt_bearer.go index 2e93265..a0287d9 100644 --- a/internal/flows/jwt_bearer.go +++ b/internal/flows/jwt_bearer.go @@ -51,6 +51,9 @@ func NewJwtBearerFlow(eps JwtBearerFlowEndpoints, params JwtBearerFlowParams) (s if client == nil { return "", fmt.Errorf("invalid client (client is nil)") } + if verbose { + fmt.Printf("ID token (IDP): %s\n access token (IDP): %s", accessToken, idToken) + } if accessToken != "" { _, err := jws.Verify([]byte(accessToken), jws.WithKeySet(client.Provider.KeySet), jws.WithValidateKey(true)) if err != nil { From a7e0e73e4560a979151a0c57037034c64b61810b Mon Sep 17 00:00:00 2001 From: David Allen Date: Wed, 12 Jun 2024 14:01:24 -0600 Subject: [PATCH 11/12] Added response body print to debug ID token --- internal/oauth/authenticate.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/internal/oauth/authenticate.go b/internal/oauth/authenticate.go index b579e8e..175aad1 100644 --- a/internal/oauth/authenticate.go +++ b/internal/oauth/authenticate.go @@ -109,12 +109,14 @@ func (client *Client) FetchTokenFromAuthenticationServer(code string, state stri } res, err := http.PostForm(client.Provider.Endpoints.Token, body) if err != nil { - return nil, fmt.Errorf("failed to get ID token: %s", err) + return nil, fmt.Errorf("failed to get ID token: %v", err) } + b, err := io.ReadAll(res.Body) + if err != nil { + return nil, fmt.Errorf("failed to read response body: %v", err) + } + fmt.Printf("%s\n", string(b)) defer res.Body.Close() - // domain, _ := url.Parse("http://127.0.0.1") - // client.Jar.SetCookies(domain, res.Cookies()) - return io.ReadAll(res.Body) } From e0a8d434211c29be3b4808e3dfcedaa2a713bf8a Mon Sep 17 00:00:00 2001 From: David Allen Date: Wed, 12 Jun 2024 14:42:47 -0600 Subject: [PATCH 12/12] Fixed token fetch from IDP --- internal/oauth/authenticate.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/oauth/authenticate.go b/internal/oauth/authenticate.go index 175aad1..4af65cb 100644 --- a/internal/oauth/authenticate.go +++ b/internal/oauth/authenticate.go @@ -118,5 +118,5 @@ func (client *Client) FetchTokenFromAuthenticationServer(code string, state stri fmt.Printf("%s\n", string(b)) defer res.Body.Close() - return io.ReadAll(res.Body) + return b, nil }