Compare commits

..

2 commits

Author SHA1 Message Date
741e638c78 Add basic support to store auth_requests (#48)
Some checks failed
/ docker-build-only (push) Failing after 29s
/ go-test (push) Failing after 1m15s
2024-08-17 15:22:37 +02:00
13f65707e7 Init OIDC client on start with config from DB (#48) 2024-08-17 14:23:06 +02:00
8 changed files with 184 additions and 19 deletions

View file

@ -11,9 +11,6 @@ var backendCmd = &cobra.Command{
Use: "backend",
Short: "Handle authentication backends",
Long: `Add, Remove or Show currently installed authentication backends`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("backend called")
},
}
func printProperty(key, value string) {

View file

@ -9,6 +9,7 @@ import (
"git.faercol.me/faercol/polyculeconnect/polyculeconnect/cmd"
"git.faercol.me/faercol/polyculeconnect/polyculeconnect/cmd/utils"
"git.faercol.me/faercol/polyculeconnect/polyculeconnect/internal/client"
"git.faercol.me/faercol/polyculeconnect/polyculeconnect/internal/db"
"git.faercol.me/faercol/polyculeconnect/polyculeconnect/internal/middlewares"
"git.faercol.me/faercol/polyculeconnect/polyculeconnect/internal/storage"
@ -45,18 +46,6 @@ func serve() {
storageType := utils.InitStorage(conf)
logger.L.Infof("Initialized storage backend %q", conf.StorageType)
logger.L.Info("Initializing authentication backends")
// dex_server.ConnectorsConfig[connector.TypeRefuseAll] = func() dex_server.ConnectorConfig { return new(connector.RefuseAllConfig) }
// connectors, err := dexConf.Storage.ListConnectors()
// if err != nil {
// logger.L.Fatalf("Failed to get existing connectors: %s", err.Error())
// }
// var connectorIDs []string
// for _, conn := range connectors {
// connectorIDs = append(connectorIDs, conn.ID)
// }
userDB, err := db.New(*conf)
if err != nil {
utils.Failf("failed to init user DB: %s", err.Error())
@ -71,6 +60,30 @@ func serve() {
op.WithLogger(slogger),
op.WithHttpInterceptors(middlewares.WithBackendFromRequestMiddleware),
}
logger.L.Info("Initializing authentication backends")
backends := []*client.OIDCClient{}
backendConfs, err := userDB.BackendStorage().GetAllBackends(context.Background())
if err != nil {
utils.Failf("failed to get backend configs from the DB: %s", err.Error())
}
// TODO: check if we need to do it this way or
// - do a try-loop?
// - only init when using them in a request?
for _, c := range backendConfs {
b, err := client.New(context.Background(), c)
if err != nil {
utils.Failf("failed to init backend client: %s", err.Error())
}
backends = append(backends, b)
}
if len(backends) == 0 {
logger.L.Warn("No auth backend loaded")
} else {
logger.L.Infof("Initialized %d auth backends", len(backends))
}
provider, err := op.NewProvider(&opConf, &st, op.StaticIssuer(conf.Issuer), options...)
if err != nil {
utils.Failf("failed to init OIDC provider: %s", err.Error())

View file

@ -0,0 +1,23 @@
package client
import (
"context"
"fmt"
"git.faercol.me/faercol/polyculeconnect/polyculeconnect/internal/model"
"github.com/zitadel/oidc/v3/pkg/client/rp"
)
type OIDCClient struct {
Conf *model.Backend
provider rp.RelyingParty
}
func New(ctx context.Context, conf *model.Backend) (*OIDCClient, error) {
pr, err := rp.NewRelyingPartyOIDC(ctx, conf.OIDCConfig.Issuer, conf.OIDCConfig.ClientID, conf.OIDCConfig.ClientSecret, conf.OIDCConfig.RedirectURI, []string{})
if err != nil {
return nil, fmt.Errorf("failed to init relying party provider: %w", err)
}
return &OIDCClient{Conf: conf, provider: pr}, nil
}

View file

@ -3,6 +3,7 @@ package middlewares
import (
"context"
"net/http"
"strings"
)
const (
@ -15,6 +16,11 @@ type BackendFromRequestMiddleware struct {
}
func (m *BackendFromRequestMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if !strings.HasPrefix(r.RequestURI, "/authorize") {
m.h.ServeHTTP(w, r)
return
}
if err := r.ParseForm(); err != nil {
// TODO: handle this better
w.WriteHeader(http.StatusBadRequest)

View file

@ -0,0 +1,116 @@
package model
import (
"fmt"
"strings"
"time"
"github.com/google/uuid"
"github.com/zitadel/oidc/v3/pkg/oidc"
)
// AuthRequest also implements the op.AuthRequest interface
type AuthRequest struct {
ID uuid.UUID
ClientID string
Scopes []string
RedirectURI string
State string
Nonce string
ResponseType string
CreationDate time.Time
AuthTime time.Time
// TODO mapping to claims to be added I guess
CodeChallenge string
CodeChallengeMethod string
BackendID uuid.UUID
UserID uuid.UUID
done bool
}
func (a AuthRequest) GetID() string {
return a.ID.String()
}
func (a AuthRequest) GetACR() string {
return "" // TODO: the hell is ACR???
}
func (a AuthRequest) GetAMR() []string {
return []string{} // TODO: the hell is this???
}
func (a AuthRequest) GetAudience() []string {
return []string{a.ID.String()} // TODO: check if we need to return something else
}
func (a AuthRequest) GetAuthTime() time.Time {
return a.AuthTime
}
func (a AuthRequest) GetClientID() string {
return a.ClientID
}
func (a AuthRequest) GetCodeChallenge() *oidc.CodeChallenge {
return &oidc.CodeChallenge{
Challenge: a.CodeChallenge,
Method: oidc.CodeChallengeMethod(a.CodeChallengeMethod),
}
}
func (a AuthRequest) GetNonce() string {
return a.Nonce
}
func (a AuthRequest) GetRedirectURI() string {
return a.RedirectURI
}
func (a AuthRequest) GetResponseType() oidc.ResponseType {
return oidc.ResponseType(a.ResponseType)
}
func (a AuthRequest) GetResponseMode() oidc.ResponseMode {
return oidc.ResponseModeQuery // TODO: check if this is good
}
func (a AuthRequest) GetScopes() []string {
return a.Scopes
}
func (a AuthRequest) GetState() string {
return a.State
}
func (a AuthRequest) GetSubject() string {
return a.UserID.String()
}
func (a AuthRequest) Done() bool {
return a.done
}
func (a *AuthRequest) FromOIDCAuthRequest(req *oidc.AuthRequest, backendID uuid.UUID) {
fmt.Println(req)
a.ID = uuid.New()
a.ClientID = req.ClientID
a.Scopes = strings.Split(req.Scopes.String(), " ")
a.RedirectURI = req.RedirectURI
a.State = req.State
a.Nonce = req.Nonce
a.ResponseType = string(req.ResponseType)
a.CreationDate = time.Now().UTC()
a.CodeChallenge = req.CodeChallenge
a.CodeChallengeMethod = string(req.CodeChallengeMethod)
a.BackendID = backendID
fmt.Println(a)
}

View file

@ -13,6 +13,7 @@ type ClientConfig struct {
RedirectURIs []string
TrustedPeers []string
Name string
AuthRequest *AuthRequest
}
type Client struct {
@ -47,8 +48,13 @@ func (c Client) GrantTypes() []oidc.GrantType {
return []oidc.GrantType{oidc.GrantTypeCode}
}
func (c Client) LoginURL(id string) string {
return id
func (c Client) LoginURL(authRequestID string) string {
// here we have the requestID, meaning we should:
// - get the request from its ID
// - get the associated backend
// - build the correct URI to use as a redirection, which is from the backend
// - afterwards would should basically handle it as a OIDC client
return authRequestID
}
func (c Client) AccessTokenType() op.AccessTokenType {

View file

@ -7,6 +7,7 @@ import (
"time"
"git.faercol.me/faercol/polyculeconnect/polyculeconnect/internal/db"
"git.faercol.me/faercol/polyculeconnect/polyculeconnect/internal/model"
"github.com/go-jose/go-jose/v4"
"github.com/zitadel/oidc/v3/pkg/oidc"
"github.com/zitadel/oidc/v3/pkg/op"
@ -30,12 +31,15 @@ func (s *Storage) CreateAuthRequest(ctx context.Context, req *oidc.AuthRequest,
if !ok {
return nil, errors.New("no backend name provided")
}
_, err := s.LocalStorage.BackendStorage().GetBackendByName(ctx, backendName)
selectedBackend, err := s.LocalStorage.BackendStorage().GetBackendByName(ctx, backendName)
if err != nil {
return nil, fmt.Errorf("failed to get backend: %w", err)
}
return nil, ErrNotImplemented("CreateAuthRequest")
var opReq model.AuthRequest
opReq.FromOIDCAuthRequest(req, selectedBackend.ID)
return opReq, nil
}
func (s *Storage) AuthRequestByID(ctx context.Context, requestID string) (op.AuthRequest, error) {

Binary file not shown.