mirror of
https://github.com/openziti/ziti.git
synced 2026-10-09 06:11:34 +00:00
134 lines
4.1 KiB
Go
134 lines
4.1 KiB
Go
package oidc_auth
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"github.com/gorilla/mux"
|
|
"github.com/michaelquigley/pfxlog"
|
|
"github.com/openziti/ziti/controller/db"
|
|
"github.com/openziti/ziti/controller/model"
|
|
"github.com/pkg/errors"
|
|
"github.com/zitadel/oidc/v2/pkg/op"
|
|
"golang.org/x/text/language"
|
|
"net/http"
|
|
)
|
|
|
|
const (
|
|
pathLoggedOut = "/oidc/logged-out"
|
|
WellKnownOidcConfiguration = "/.well-known/openid-configuration"
|
|
|
|
SourceTypeOidc = "oidc_auth"
|
|
|
|
AuthMethodPassword = model.AuthMethodPassword
|
|
AuthMethodExtJwt = model.AuthMethodExtJwt
|
|
AuthMethodCert = db.MethodAuthenticatorCert
|
|
|
|
AuthMethodSecondaryTotp = "totp"
|
|
AuthMethodSecondaryExtJwt = "ejs"
|
|
|
|
DefaultNativeClientId = "native"
|
|
)
|
|
|
|
// NewNativeOnlyOP creates an OIDC Provider that allows native clients and only the AutCode PKCE flow.
|
|
func NewNativeOnlyOP(ctx context.Context, env model.Env, config Config) (http.Handler, error) {
|
|
cert, kid, method := env.GetServerCert()
|
|
config.Storage = NewStorage(kid, cert.Leaf.PublicKey, cert.PrivateKey, method, &config, env)
|
|
|
|
oidcHandler, err := newHttpRouter(ctx, config)
|
|
|
|
nativeClient := NativeClient(DefaultNativeClientId, config.RedirectURIs, config.PostLogoutURIs)
|
|
nativeClient.idTokenDuration = config.IdTokenDuration
|
|
nativeClient.loginURL = newLoginResolver(config.Storage)
|
|
config.Storage.AddClient(nativeClient)
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
|
|
r := request.WithContext(context.WithValue(request.Context(), contextKeyHttpRequest, request))
|
|
r = request.WithContext(context.WithValue(r.Context(), contextKeyTokenState, &TokenState{}))
|
|
oidcHandler.ServeHTTP(writer, r)
|
|
}), nil
|
|
|
|
}
|
|
|
|
// newHttpRouter creates an OIDC HTTP router
|
|
func newHttpRouter(ctx context.Context, config Config) (*mux.Router, error) {
|
|
if config.TokenSecret == "" {
|
|
return nil, errors.New("token secret must not be empty")
|
|
}
|
|
|
|
router := mux.NewRouter()
|
|
|
|
router.HandleFunc(pathLoggedOut, func(w http.ResponseWriter, req *http.Request) {
|
|
_, err := w.Write([]byte("signed out successfully"))
|
|
if err != nil {
|
|
pfxlog.Logger().Errorf("error serving logged out page: %v", err)
|
|
}
|
|
})
|
|
|
|
provider, err := newOidcProvider(ctx, config)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("could not create OpenIdProvider: %w", err)
|
|
}
|
|
|
|
loginRouter := newLogin(config.Storage, op.AuthCallbackURL(provider), op.NewIssuerInterceptor(provider.IssuerFromRequest))
|
|
|
|
router.Handle("/oidc/"+WellKnownOidcConfiguration, http.StripPrefix("/oidc", provider.HttpHandler()))
|
|
router.Handle(WellKnownOidcConfiguration, provider.HttpHandler())
|
|
|
|
router.PathPrefix("/oidc/login").Handler(http.StripPrefix("/oidc/login", loginRouter.router))
|
|
|
|
router.PathPrefix("/oidc").Handler(http.StripPrefix("/oidc", provider.HttpHandler()))
|
|
|
|
return router, nil
|
|
}
|
|
|
|
// newOidcProvider will create an OpenID Provider that allows refresh tokens, authentication via form post and basic auth, and support request object params
|
|
func newOidcProvider(_ context.Context, oidcConfig Config) (op.OpenIDProvider, error) {
|
|
config := &op.Config{
|
|
CryptoKey: oidcConfig.Secret(),
|
|
DefaultLogoutRedirectURI: pathLoggedOut,
|
|
CodeMethodS256: true,
|
|
AuthMethodPost: true,
|
|
AuthMethodPrivateKeyJWT: true,
|
|
GrantTypeRefreshToken: true,
|
|
RequestObjectSupported: true,
|
|
SupportedUILocales: []language.Tag{language.English},
|
|
}
|
|
|
|
handler, err := op.NewOpenIDProvider(oidcConfig.Issuer, config, oidcConfig.Storage)
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return handler, nil
|
|
}
|
|
|
|
// newLoginResolver returns func capable of determining default login URLs based on authId
|
|
func newLoginResolver(storage Storage) func(string) string {
|
|
return func(authId string) string {
|
|
authRequest, err := storage.GetAuthRequest(authId)
|
|
|
|
if err != nil || authRequest == nil {
|
|
return passwordLoginUrl + authId
|
|
}
|
|
|
|
switch authRequest.RequestedMethod {
|
|
case AuthMethodPassword:
|
|
return passwordLoginUrl + authId
|
|
case AuthMethodExtJwt:
|
|
return extJwtLoginUrl + authId
|
|
case AuthMethodCert:
|
|
return certLoginUrl + authId
|
|
}
|
|
|
|
if len(authRequest.PeerCerts) > 0 {
|
|
return certLoginUrl + authId
|
|
}
|
|
|
|
return passwordLoginUrl + authId
|
|
}
|
|
}
|