Made it possible to override certain example IDP endpoints

This commit is contained in:
David J. Allen 2024-04-29 18:45:30 -06:00
parent f10a771db6
commit 73e4e50d44
No known key found for this signature in database
GPG key ID: 717C593FF60A2ACC
4 changed files with 57 additions and 18 deletions

View file

@ -2,6 +2,7 @@ package cmd
import ( import (
opaal "davidallendj/opaal/internal" opaal "davidallendj/opaal/internal"
"davidallendj/opaal/internal/oidc"
"errors" "errors"
"fmt" "fmt"
"net/http" "net/http"
@ -9,23 +10,31 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
var exampleCmd = &cobra.Command{ var (
endpoints oidc.Endpoints
)
var serveCmd = &cobra.Command{
Use: "serve", 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", 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) { Run: func(cmd *cobra.Command, args []string) {
s := opaal.NewServerWithConfig(&config) s := opaal.NewServerWithConfig(&config)
// FIXME: change how the server address is set with `NewServerWithConfig` // FIXME: change how the server address is set with `NewServerWithConfig`
s.Server.Addr = fmt.Sprintf("%s:%d", s.Issuer.Host, s.Issuer.Port) 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) { if errors.Is(err, http.ErrServerClosed) {
fmt.Printf("Identity provider server closed.\n") fmt.Printf("Identity provider server closed.\n")
} else if err != nil { } else if err != nil {
fmt.Errorf("failed to start server: %v", err) fmt.Printf("failed to start server: %v", err)
} }
}, },
} }
func init() { 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)
} }

View file

@ -164,3 +164,25 @@ func (p *IdentityProvider) FetchJwks() error {
return nil 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)
}

View file

@ -22,7 +22,7 @@ import (
"github.com/lestrrat-go/jwx/v2/jwt" "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 // NOTE: this example does NOT implement CSRF tokens nor use them
// create an example identity provider // create an example identity provider
@ -38,6 +38,14 @@ func (s *Server) StartIdentityProvider() error {
callback = "/oidc/callback" 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 // generate key pair used to sign JWKS and create JWTs
privateKey, err := rsa.GenerateKey(rand.Reader, 2048) privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil { if err != nil {
@ -72,14 +80,13 @@ func (s *Server) StartIdentityProvider() error {
w.Write(b) w.Write(b)
}) })
// TODO: create .well-known openid configuration
r.HandleFunc("/.well-known/openid-configuration", func(w http.ResponseWriter, r *http.Request) { r.HandleFunc("/.well-known/openid-configuration", func(w http.ResponseWriter, r *http.Request) {
// create config JSON to serve with GET request // create config JSON to serve with GET request
config := map[string]any{ config := map[string]any{
"issuer": "http://" + s.Addr, "issuer": "http://" + s.Addr,
"authorization_endpoint": "http://" + s.Addr + "/oauth/authorize", "authorization_endpoint": eps.Authorization,
"token_endpoint": "http://" + s.Addr + "/oauth/token", "token_endpoint": eps.Token,
"jwks_uri": "http://" + s.Addr + "/.well-known/jwks.json", "jwks_uri": eps.JwksUri,
"scopes_supported": []string{ "scopes_supported": []string{
"openid", "openid",
"profile", "profile",

View file

@ -18,16 +18,17 @@ import (
type Server struct { type Server struct {
*http.Server *http.Server
Host string `yaml:"host"` Host string `yaml:"host"`
Port int `yaml:"port"` Port int `yaml:"port"`
Callback string `yaml:"callback"` Callback string `yaml:"callback"`
State string `yaml:"state"` State string `yaml:"state"`
Issuer Issuer `yaml:"issuer"` Issuer IdentityProviderServer `yaml:"issuer"`
} }
type Issuer struct { type IdentityProviderServer struct {
Host string `yaml:"host"` Host string `yaml:"host"`
Port int `yaml:"port"` Port int `yaml:"port"`
Endpoints oidc.Endpoints `yaml:"endpoints"`
} }
type ServerParams struct { type ServerParams struct {