mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-12 03:37:34 +00:00
feat(oidc): use the new oidc server interface (#6779)
* feat(oidc): use the new oidc server interface * rename from provider to server * pin logging and oidc packages * use oidc introspection fix branch * add overloaded methods with tracing * cleanup unused code * include latest oidc fixes --------- Co-authored-by: Livio Spring <livio.a@gmail.com>
This commit is contained in:
@@ -91,7 +91,7 @@ func (s *Server) failAuthRequest(ctx context.Context, authRequestID string, ae *
|
||||
return nil, err
|
||||
}
|
||||
authReq := &oidc.AuthRequestV2{CurrentAuthRequest: aar}
|
||||
callback, err := oidc.CreateErrorCallbackURL(authReq, errorReasonToOIDC(ae.GetError()), ae.GetErrorDescription(), ae.GetErrorUri(), s.op)
|
||||
callback, err := oidc.CreateErrorCallbackURL(authReq, errorReasonToOIDC(ae.GetError()), ae.GetErrorDescription(), ae.GetErrorUri(), s.op.Provider())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -110,9 +110,9 @@ func (s *Server) linkSessionToAuthRequest(ctx context.Context, authRequestID str
|
||||
ctx = op.ContextWithIssuer(ctx, http.BuildOrigin(authz.GetInstance(ctx).RequestedHost(), s.externalSecure))
|
||||
var callback string
|
||||
if aar.ResponseType == domain.OIDCResponseTypeCode {
|
||||
callback, err = oidc.CreateCodeCallbackURL(ctx, authReq, s.op)
|
||||
callback, err = oidc.CreateCodeCallbackURL(ctx, authReq, s.op.Provider())
|
||||
} else {
|
||||
callback, err = oidc.CreateTokenCallbackURL(ctx, authReq, s.op)
|
||||
callback, err = oidc.CreateTokenCallbackURL(ctx, authReq, s.op.Provider())
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@@ -1,11 +1,11 @@
|
||||
package oidc
|
||||
|
||||
import (
|
||||
"github.com/zitadel/oidc/v3/pkg/op"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
"github.com/zitadel/zitadel/internal/api/grpc/server"
|
||||
"github.com/zitadel/zitadel/internal/api/oidc"
|
||||
"github.com/zitadel/zitadel/internal/command"
|
||||
"github.com/zitadel/zitadel/internal/query"
|
||||
oidc_pb "github.com/zitadel/zitadel/pkg/grpc/oidc/v2beta"
|
||||
@@ -18,7 +18,7 @@ type Server struct {
|
||||
command *command.Commands
|
||||
query *query.Queries
|
||||
|
||||
op op.OpenIDProvider
|
||||
op *oidc.Server
|
||||
externalSecure bool
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ type Config struct{}
|
||||
func CreateServer(
|
||||
command *command.Commands,
|
||||
query *query.Queries,
|
||||
op op.OpenIDProvider,
|
||||
op *oidc.Server,
|
||||
externalSecure bool,
|
||||
) *Server {
|
||||
return &Server{
|
||||
|
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/rakyll/statik/fs"
|
||||
"github.com/zitadel/oidc/v3/pkg/oidc"
|
||||
"github.com/zitadel/oidc/v3/pkg/op"
|
||||
"golang.org/x/exp/slog"
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/assets"
|
||||
@@ -80,7 +81,7 @@ type OPStorage struct {
|
||||
assetAPIPrefix func(ctx context.Context) string
|
||||
}
|
||||
|
||||
func NewProvider(
|
||||
func NewServer(
|
||||
config Config,
|
||||
defaultLogoutRedirectURI string,
|
||||
externalSecure bool,
|
||||
@@ -93,19 +94,17 @@ func NewProvider(
|
||||
projections *database.DB,
|
||||
userAgentCookie, instanceHandler func(http.Handler) http.Handler,
|
||||
accessHandler *middleware.AccessInterceptor,
|
||||
) (op.OpenIDProvider, error) {
|
||||
fallbackLogger *slog.Logger,
|
||||
) (*Server, error) {
|
||||
opConfig, err := createOPConfig(config, defaultLogoutRedirectURI, cryptoKey)
|
||||
if err != nil {
|
||||
return nil, caos_errs.ThrowInternal(err, "OIDC-EGrqd", "cannot create op config: %w")
|
||||
}
|
||||
storage := newStorage(config, command, query, repo, encryptionAlg, es, projections, externalSecure)
|
||||
options, err := createOptions(
|
||||
config,
|
||||
externalSecure,
|
||||
userAgentCookie,
|
||||
instanceHandler,
|
||||
accessHandler.HandleIgnorePathPrefixes(ignoredQuotaLimitEndpoint(config.CustomEndpoints)),
|
||||
)
|
||||
var options []op.Option
|
||||
if !externalSecure {
|
||||
options = append(options, op.WithAllowInsecure())
|
||||
}
|
||||
if err != nil {
|
||||
return nil, caos_errs.ThrowInternal(err, "OIDC-D3gq1", "cannot create options: %w")
|
||||
}
|
||||
@@ -118,7 +117,22 @@ func NewProvider(
|
||||
if err != nil {
|
||||
return nil, caos_errs.ThrowInternal(err, "OIDC-DAtg3", "cannot create provider")
|
||||
}
|
||||
return provider, nil
|
||||
|
||||
server := &Server{
|
||||
LegacyServer: op.NewLegacyServer(provider, endpoints(config.CustomEndpoints)),
|
||||
}
|
||||
metricTypes := []metrics.MetricType{metrics.MetricTypeRequestCount, metrics.MetricTypeStatusCode, metrics.MetricTypeTotalCount}
|
||||
server.Handler = op.RegisterLegacyServer(server, op.WithHTTPMiddleware(
|
||||
middleware.MetricsHandler(metricTypes),
|
||||
middleware.TelemetryHandler(),
|
||||
middleware.NoCacheInterceptor().Handler,
|
||||
instanceHandler,
|
||||
userAgentCookie,
|
||||
http_utils.CopyHeadersToContext,
|
||||
accessHandler.HandleIgnorePathPrefixes(ignoredQuotaLimitEndpoint(config.CustomEndpoints)),
|
||||
))
|
||||
|
||||
return server, nil
|
||||
}
|
||||
|
||||
func ignoredQuotaLimitEndpoint(endpoints *EndpointConfig) []string {
|
||||
@@ -158,61 +172,6 @@ func createOPConfig(config Config, defaultLogoutRedirectURI string, cryptoKey []
|
||||
return opConfig, nil
|
||||
}
|
||||
|
||||
func createOptions(config Config, externalSecure bool, userAgentCookie, instanceHandler, accessHandler func(http.Handler) http.Handler) ([]op.Option, error) {
|
||||
metricTypes := []metrics.MetricType{metrics.MetricTypeRequestCount, metrics.MetricTypeStatusCode, metrics.MetricTypeTotalCount}
|
||||
options := []op.Option{
|
||||
op.WithHttpInterceptors(
|
||||
middleware.MetricsHandler(metricTypes),
|
||||
middleware.TelemetryHandler(),
|
||||
middleware.NoCacheInterceptor().Handler,
|
||||
instanceHandler,
|
||||
userAgentCookie,
|
||||
http_utils.CopyHeadersToContext,
|
||||
accessHandler,
|
||||
),
|
||||
}
|
||||
if !externalSecure {
|
||||
options = append(options, op.WithAllowInsecure())
|
||||
}
|
||||
endpoints := customEndpoints(config.CustomEndpoints)
|
||||
if len(endpoints) != 0 {
|
||||
options = append(options, endpoints...)
|
||||
}
|
||||
return options, nil
|
||||
}
|
||||
|
||||
func customEndpoints(endpointConfig *EndpointConfig) []op.Option {
|
||||
if endpointConfig == nil {
|
||||
return nil
|
||||
}
|
||||
options := []op.Option{}
|
||||
if endpointConfig.Auth != nil {
|
||||
options = append(options, op.WithCustomAuthEndpoint(op.NewEndpointWithURL(endpointConfig.Auth.Path, endpointConfig.Auth.URL)))
|
||||
}
|
||||
if endpointConfig.Token != nil {
|
||||
options = append(options, op.WithCustomTokenEndpoint(op.NewEndpointWithURL(endpointConfig.Token.Path, endpointConfig.Token.URL)))
|
||||
}
|
||||
if endpointConfig.Introspection != nil {
|
||||
options = append(options, op.WithCustomIntrospectionEndpoint(op.NewEndpointWithURL(endpointConfig.Introspection.Path, endpointConfig.Introspection.URL)))
|
||||
}
|
||||
if endpointConfig.Userinfo != nil {
|
||||
options = append(options, op.WithCustomUserinfoEndpoint(op.NewEndpointWithURL(endpointConfig.Userinfo.Path, endpointConfig.Userinfo.URL)))
|
||||
}
|
||||
if endpointConfig.Revocation != nil {
|
||||
options = append(options, op.WithCustomRevocationEndpoint(op.NewEndpointWithURL(endpointConfig.Revocation.Path, endpointConfig.Revocation.URL)))
|
||||
}
|
||||
if endpointConfig.EndSession != nil {
|
||||
options = append(options, op.WithCustomEndSessionEndpoint(op.NewEndpointWithURL(endpointConfig.EndSession.Path, endpointConfig.EndSession.URL)))
|
||||
}
|
||||
if endpointConfig.Keys != nil {
|
||||
options = append(options, op.WithCustomKeysEndpoint(op.NewEndpointWithURL(endpointConfig.Keys.Path, endpointConfig.Keys.URL)))
|
||||
}
|
||||
if endpointConfig.DeviceAuth != nil {
|
||||
options = append(options, op.WithCustomDeviceAuthorizationEndpoint(op.NewEndpointWithURL(endpointConfig.DeviceAuth.Path, endpointConfig.DeviceAuth.URL)))
|
||||
}
|
||||
return options
|
||||
}
|
||||
|
||||
func newStorage(config Config, command *command.Commands, query *query.Queries, repo repository.Repository, encAlg crypto.EncryptionAlgorithm, es *eventstore.Eventstore, db *database.DB, externalSecure bool) *OPStorage {
|
||||
return &OPStorage{
|
||||
repo: repo,
|
||||
|
188
internal/api/oidc/server.go
Normal file
188
internal/api/oidc/server.go
Normal file
@@ -0,0 +1,188 @@
|
||||
package oidc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/zitadel/oidc/v3/pkg/oidc"
|
||||
"github.com/zitadel/oidc/v3/pkg/op"
|
||||
"github.com/zitadel/zitadel/internal/telemetry/tracing"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
http.Handler
|
||||
*op.LegacyServer
|
||||
}
|
||||
|
||||
func endpoints(endpointConfig *EndpointConfig) op.Endpoints {
|
||||
// some defaults. The new Server will disable enpoints that are nil.
|
||||
endpoints := op.Endpoints{
|
||||
Authorization: op.NewEndpoint("/oauth/v2/authorize"),
|
||||
Token: op.NewEndpoint("/oauth/v2/token"),
|
||||
Introspection: op.NewEndpoint("/oauth/v2/introspect"),
|
||||
Userinfo: op.NewEndpoint("/oidc/v1/userinfo"),
|
||||
Revocation: op.NewEndpoint("/oauth/v2/revoke"),
|
||||
EndSession: op.NewEndpoint("/oidc/v1/end_session"),
|
||||
JwksURI: op.NewEndpoint("/oauth/v2/keys"),
|
||||
DeviceAuthorization: op.NewEndpoint("/oauth/v2/device_authorization"),
|
||||
}
|
||||
|
||||
if endpointConfig == nil {
|
||||
return endpoints
|
||||
}
|
||||
if endpointConfig.Auth != nil {
|
||||
endpoints.Authorization = op.NewEndpointWithURL(endpointConfig.Auth.Path, endpointConfig.Auth.URL)
|
||||
}
|
||||
if endpointConfig.Token != nil {
|
||||
endpoints.Token = op.NewEndpointWithURL(endpointConfig.Token.Path, endpointConfig.Token.URL)
|
||||
}
|
||||
if endpointConfig.Introspection != nil {
|
||||
endpoints.Introspection = op.NewEndpointWithURL(endpointConfig.Introspection.Path, endpointConfig.Introspection.URL)
|
||||
}
|
||||
if endpointConfig.Userinfo != nil {
|
||||
endpoints.Userinfo = op.NewEndpointWithURL(endpointConfig.Userinfo.Path, endpointConfig.Userinfo.URL)
|
||||
}
|
||||
if endpointConfig.Revocation != nil {
|
||||
endpoints.Revocation = op.NewEndpointWithURL(endpointConfig.Revocation.Path, endpointConfig.Revocation.URL)
|
||||
}
|
||||
if endpointConfig.EndSession != nil {
|
||||
endpoints.EndSession = op.NewEndpointWithURL(endpointConfig.EndSession.Path, endpointConfig.EndSession.URL)
|
||||
}
|
||||
if endpointConfig.Keys != nil {
|
||||
endpoints.JwksURI = op.NewEndpointWithURL(endpointConfig.Keys.Path, endpointConfig.Keys.URL)
|
||||
}
|
||||
if endpointConfig.DeviceAuth != nil {
|
||||
endpoints.DeviceAuthorization = op.NewEndpointWithURL(endpointConfig.DeviceAuth.Path, endpointConfig.DeviceAuth.URL)
|
||||
}
|
||||
return endpoints
|
||||
}
|
||||
|
||||
func (s *Server) IssuerFromRequest(r *http.Request) string {
|
||||
return s.Provider().IssuerFromRequest(r)
|
||||
}
|
||||
|
||||
func (s *Server) Health(ctx context.Context, r *op.Request[struct{}]) (_ *op.Response, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
return s.LegacyServer.Health(ctx, r)
|
||||
}
|
||||
|
||||
func (s *Server) Ready(ctx context.Context, r *op.Request[struct{}]) (_ *op.Response, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
return s.LegacyServer.Ready(ctx, r)
|
||||
}
|
||||
|
||||
func (s *Server) Discovery(ctx context.Context, r *op.Request[struct{}]) (_ *op.Response, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
return s.LegacyServer.Discovery(ctx, r)
|
||||
}
|
||||
|
||||
func (s *Server) Keys(ctx context.Context, r *op.Request[struct{}]) (_ *op.Response, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
return s.LegacyServer.Keys(ctx, r)
|
||||
}
|
||||
|
||||
func (s *Server) VerifyAuthRequest(ctx context.Context, r *op.Request[oidc.AuthRequest]) (_ *op.ClientRequest[oidc.AuthRequest], err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
return s.LegacyServer.VerifyAuthRequest(ctx, r)
|
||||
}
|
||||
|
||||
func (s *Server) Authorize(ctx context.Context, r *op.ClientRequest[oidc.AuthRequest]) (_ *op.Redirect, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
return s.LegacyServer.Authorize(ctx, r)
|
||||
}
|
||||
|
||||
func (s *Server) DeviceAuthorization(ctx context.Context, r *op.ClientRequest[oidc.DeviceAuthorizationRequest]) (_ *op.Response, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
return s.LegacyServer.DeviceAuthorization(ctx, r)
|
||||
}
|
||||
|
||||
func (s *Server) VerifyClient(ctx context.Context, r *op.Request[op.ClientCredentials]) (_ op.Client, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
return s.LegacyServer.VerifyClient(ctx, r)
|
||||
}
|
||||
|
||||
func (s *Server) CodeExchange(ctx context.Context, r *op.ClientRequest[oidc.AccessTokenRequest]) (_ *op.Response, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
return s.LegacyServer.CodeExchange(ctx, r)
|
||||
}
|
||||
|
||||
func (s *Server) RefreshToken(ctx context.Context, r *op.ClientRequest[oidc.RefreshTokenRequest]) (_ *op.Response, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
return s.LegacyServer.RefreshToken(ctx, r)
|
||||
}
|
||||
|
||||
func (s *Server) JWTProfile(ctx context.Context, r *op.Request[oidc.JWTProfileGrantRequest]) (_ *op.Response, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
return s.LegacyServer.JWTProfile(ctx, r)
|
||||
}
|
||||
|
||||
func (s *Server) TokenExchange(ctx context.Context, r *op.ClientRequest[oidc.TokenExchangeRequest]) (_ *op.Response, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
return s.LegacyServer.TokenExchange(ctx, r)
|
||||
}
|
||||
|
||||
func (s *Server) ClientCredentialsExchange(ctx context.Context, r *op.ClientRequest[oidc.ClientCredentialsRequest]) (_ *op.Response, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
return s.LegacyServer.ClientCredentialsExchange(ctx, r)
|
||||
}
|
||||
|
||||
func (s *Server) DeviceToken(ctx context.Context, r *op.ClientRequest[oidc.DeviceAccessTokenRequest]) (_ *op.Response, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
return s.LegacyServer.DeviceToken(ctx, r)
|
||||
}
|
||||
|
||||
func (s *Server) Introspect(ctx context.Context, r *op.Request[op.IntrospectionRequest]) (_ *op.Response, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
return s.LegacyServer.Introspect(ctx, r)
|
||||
}
|
||||
|
||||
func (s *Server) UserInfo(ctx context.Context, r *op.Request[oidc.UserInfoRequest]) (_ *op.Response, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
return s.LegacyServer.UserInfo(ctx, r)
|
||||
}
|
||||
|
||||
func (s *Server) Revocation(ctx context.Context, r *op.ClientRequest[oidc.RevocationRequest]) (_ *op.Response, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
return s.LegacyServer.Revocation(ctx, r)
|
||||
}
|
||||
|
||||
func (s *Server) EndSession(ctx context.Context, r *op.Request[oidc.EndSessionRequest]) (_ *op.Redirect, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
return s.LegacyServer.EndSession(ctx, r)
|
||||
}
|
Reference in New Issue
Block a user