diff --git a/internal/flows/jwt_bearer.go b/internal/flows/jwt_bearer.go index a0287d9..2e93265 100644 --- a/internal/flows/jwt_bearer.go +++ b/internal/flows/jwt_bearer.go @@ -51,9 +51,6 @@ 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 { diff --git a/internal/new.go b/internal/new.go index 2799d88..7e3e35c 100644 --- a/internal/new.go +++ b/internal/new.go @@ -94,7 +94,6 @@ 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/oauth/authenticate.go b/internal/oauth/authenticate.go index 4af65cb..b579e8e 100644 --- a/internal/oauth/authenticate.go +++ b/internal/oauth/authenticate.go @@ -109,14 +109,12 @@ 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: %v", err) + return nil, fmt.Errorf("failed to get ID token: %s", 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() - return b, nil + // domain, _ := url.Parse("http://127.0.0.1") + // client.Jar.SetCookies(domain, res.Cookies()) + + return io.ReadAll(res.Body) } diff --git a/internal/oidc/oidc.go b/internal/oidc/oidc.go index f52e0c4..206f68f 100644 --- a/internal/oidc/oidc.go +++ b/internal/oidc/oidc.go @@ -175,6 +175,7 @@ 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 bb82a28..35b4051 100644 --- a/internal/server/idp.go +++ b/internal/server/idp.go @@ -3,6 +3,7 @@ package server import ( "crypto/rand" "crypto/rsa" + "davidallendj/opaal/internal/oauth" "davidallendj/opaal/internal/oidc" "encoding/json" "fmt" @@ -21,22 +22,6 @@ 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 @@ -44,13 +29,19 @@ 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 + "/oauth2/authorize", - Token: "http://" + s.Addr + "/oauth2/token", + Authorization: "http://" + s.Addr + "/oauth/authorize", + Token: "http://" + s.Addr + "/oauth/token", JwksUri: "http://" + s.Addr + "/.well-known/jwks.json", } oidc.UpdateEndpoints(&s.Issuer.Endpoints, &defaultEps) @@ -147,18 +138,21 @@ 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 == "openchami" && password == "openchami" { - client := s.Issuer.Clients[0] + 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)}, + } // check if there are any redirect URIs supplied if len(client.RedirectUris) <= 0 { - fmt.Printf("no redirect URIs found for client %s (ID: %s)\n", client.Name, client.Id) + fmt.Printf("no redirect URIs found") return } for _, url := range client.RedirectUris { @@ -266,13 +260,9 @@ func (s *Server) StartIdentityProvider() error { return } - // 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") + // check that we're using the default registered client + if clientId != "ochami" { + fmt.Printf("invalid client\n") return } diff --git a/internal/server/server.go b/internal/server/server.go index 3fdae97..c829a34 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -25,6 +25,12 @@ 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 @@ -57,7 +63,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 an identity provider: \n") + fmt.Printf("Login with external identity providers: \n") for i, client := range clients { // fetch provider configuration before adding button p, err := oidc.FetchServerConfig(client.Provider.Issuer) @@ -74,7 +80,8 @@ 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) - fmt.Printf("\t%s: /login?sso=%s\n", client.Name, client.Id) + url := client.BuildAuthorizationUrl(s.State) + fmt.Printf("\t%s\n", url) } var code string @@ -114,9 +121,7 @@ func (s *Server) StartLogin(clients []oauth.Client, params ServerParams) error { client = &clients[index] url := client.BuildAuthorizationUrl(s.State) - if params.Verbose { - fmt.Printf("Redirect URL: %s\n", url) - } + fmt.Printf("Redirect URL: %s\n", url) http.Redirect(w, r, url, http.StatusFound) return } @@ -141,47 +146,38 @@ func (s *Server) StartLogin(clients []oauth.Client, params ServerParams) error { p = params.AuthProvider jwks []byte ) - - fetchAndMarshal := func() (err error) { - err = p.FetchJwks() + // try and get the JWKS from param first + if p.Endpoints.JwksUri != "" { + err := p.FetchJwks() if err != nil { - fmt.Printf("failed to fetch keys: %v\n", err) - return + fmt.Printf("failed to fetch keys using JWKS url...trying to fetch config and try again...\n") } jwks, err = json.Marshal(p.KeySet) if err != nil { fmt.Printf("failed to marshal JWKS: %v\n", err) } - return - } - - // try and get the JWKS from param first - if p.Endpoints.JwksUri != "" { - if err := fetchAndMarshal(); err != nil { - w.Write(jwks) - return - } - } - - // 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 { + } 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.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) + 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 } - } 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) + // 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) return } - w.Write(jwks) }) r.HandleFunc("/token", func(w http.ResponseWriter, r *http.Request) { diff --git a/pages/login.html b/pages/login.html index c25f8c3..e55f5cf 100644 --- a/pages/login.html +++ b/pages/login.html @@ -7,7 +7,7 @@

Forgot Username?
- + \ No newline at end of file