2023-10-25 15:44:05 +00:00
package oidc
import (
"context"
2024-03-11 10:43:56 +00:00
"log/slog"
2023-10-25 15:44:05 +00:00
"net/http"
2023-12-07 09:43:45 +00:00
"time"
2023-10-25 15:44:05 +00:00
2023-11-21 12:11:38 +00:00
"github.com/zitadel/logging"
2023-10-25 15:44:05 +00:00
"github.com/zitadel/oidc/v3/pkg/oidc"
"github.com/zitadel/oidc/v3/pkg/op"
2023-11-03 15:18:57 +00:00
2024-08-20 06:45:24 +00:00
"github.com/zitadel/zitadel/internal/api/authz"
2023-11-21 12:11:38 +00:00
"github.com/zitadel/zitadel/internal/auth/repository"
"github.com/zitadel/zitadel/internal/command"
"github.com/zitadel/zitadel/internal/crypto"
2023-12-05 11:12:01 +00:00
"github.com/zitadel/zitadel/internal/i18n"
2023-11-21 12:11:38 +00:00
"github.com/zitadel/zitadel/internal/query"
2023-10-25 15:44:05 +00:00
"github.com/zitadel/zitadel/internal/telemetry/tracing"
)
type Server struct {
http . Handler
* op . LegacyServer
2023-11-21 12:11:38 +00:00
2024-01-29 15:11:52 +00:00
repo repository . Repository
query * query . Queries
command * command . Commands
accessTokenKeySet * oidcKeySet
idTokenHintKeySet * oidcKeySet
2023-11-21 12:11:38 +00:00
2023-12-07 09:43:45 +00:00
defaultLoginURL string
defaultLoginURLV2 string
defaultLogoutURLV2 string
defaultAccessTokenLifetime time . Duration
defaultIdTokenLifetime time . Duration
2024-08-23 12:43:46 +00:00
jwksCacheControlMaxAge time . Duration
2023-12-05 17:01:03 +00:00
2023-11-21 12:11:38 +00:00
fallbackLogger * slog . Logger
2024-04-05 09:35:49 +00:00
hasher * crypto . Hasher
2023-11-03 15:18:57 +00:00
signingKeyAlgorithm string
2024-05-16 05:07:56 +00:00
encAlg crypto . EncryptionAlgorithm
opCrypto op . Crypto
assetAPIPrefix func ( ctx context . Context ) string
2023-10-25 15:44:05 +00:00
}
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
}
2023-11-21 12:11:38 +00:00
func ( s * Server ) getLogger ( ctx context . Context ) * slog . Logger {
if logger , ok := logging . FromContext ( ctx ) ; ok {
return logger
}
return s . fallbackLogger
}
2023-10-25 15:44:05 +00:00
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 )
2024-01-18 06:10:49 +00:00
defer func ( ) {
err = oidcError ( err )
span . EndWithError ( err )
} ( )
2023-12-05 11:12:01 +00:00
restrictions , err := s . query . GetInstanceRestrictions ( ctx )
if err != nil {
2024-08-20 06:45:24 +00:00
return nil , op . NewStatusError ( oidc . ErrServerError ( ) . WithParent ( err ) . WithReturnParentToClient ( authz . GetFeatures ( ctx ) . DebugOIDCParentError ) . WithDescription ( "internal server error" ) , http . StatusInternalServerError )
2023-12-05 11:12:01 +00:00
}
allowedLanguages := restrictions . AllowedLanguages
if len ( allowedLanguages ) == 0 {
allowedLanguages = i18n . SupportedLanguages ( )
}
return op . NewResponse ( s . createDiscoveryConfig ( ctx , allowedLanguages ) ) , nil
2023-10-25 15:44:05 +00:00
}
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 ) 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 )
}
2023-11-03 15:18:57 +00:00
2023-12-05 11:12:01 +00:00
func ( s * Server ) createDiscoveryConfig ( ctx context . Context , supportedUILocales oidc . Locales ) * oidc . DiscoveryConfiguration {
2023-11-03 15:18:57 +00:00
issuer := op . IssuerFromContext ( ctx )
2024-08-23 12:43:46 +00:00
2023-11-03 15:18:57 +00:00
return & oidc . DiscoveryConfiguration {
2024-06-17 09:50:12 +00:00
Issuer : issuer ,
AuthorizationEndpoint : s . Endpoints ( ) . Authorization . Absolute ( issuer ) ,
TokenEndpoint : s . Endpoints ( ) . Token . Absolute ( issuer ) ,
IntrospectionEndpoint : s . Endpoints ( ) . Introspection . Absolute ( issuer ) ,
UserinfoEndpoint : s . Endpoints ( ) . Userinfo . Absolute ( issuer ) ,
RevocationEndpoint : s . Endpoints ( ) . Revocation . Absolute ( issuer ) ,
EndSessionEndpoint : s . Endpoints ( ) . EndSession . Absolute ( issuer ) ,
JwksURI : s . Endpoints ( ) . JwksURI . Absolute ( issuer ) ,
DeviceAuthorizationEndpoint : s . Endpoints ( ) . DeviceAuthorization . Absolute ( issuer ) ,
ScopesSupported : op . Scopes ( s . Provider ( ) ) ,
ResponseTypesSupported : op . ResponseTypes ( s . Provider ( ) ) ,
ResponseModesSupported : [ ] string {
string ( oidc . ResponseModeQuery ) ,
string ( oidc . ResponseModeFragment ) ,
string ( oidc . ResponseModeFormPost ) ,
} ,
GrantTypesSupported : op . GrantTypes ( s . Provider ( ) ) ,
SubjectTypesSupported : op . SubjectTypes ( s . Provider ( ) ) ,
2024-08-23 12:43:46 +00:00
IDTokenSigningAlgValuesSupported : supportedSigningAlgs ( ctx ) ,
2024-06-17 09:50:12 +00:00
RequestObjectSigningAlgValuesSupported : op . RequestObjectSigAlgorithms ( s . Provider ( ) ) ,
TokenEndpointAuthMethodsSupported : op . AuthMethodsTokenEndpoint ( s . Provider ( ) ) ,
TokenEndpointAuthSigningAlgValuesSupported : op . TokenSigAlgorithms ( s . Provider ( ) ) ,
2023-11-03 15:18:57 +00:00
IntrospectionEndpointAuthSigningAlgValuesSupported : op . IntrospectionSigAlgorithms ( s . Provider ( ) ) ,
IntrospectionEndpointAuthMethodsSupported : op . AuthMethodsIntrospectionEndpoint ( s . Provider ( ) ) ,
RevocationEndpointAuthSigningAlgValuesSupported : op . RevocationSigAlgorithms ( s . Provider ( ) ) ,
RevocationEndpointAuthMethodsSupported : op . AuthMethodsRevocationEndpoint ( s . Provider ( ) ) ,
ClaimsSupported : op . SupportedClaims ( s . Provider ( ) ) ,
CodeChallengeMethodsSupported : op . CodeChallengeMethods ( s . Provider ( ) ) ,
2023-12-05 11:12:01 +00:00
UILocalesSupported : supportedUILocales ,
2023-11-03 15:18:57 +00:00
RequestParameterSupported : s . Provider ( ) . RequestObjectSupported ( ) ,
}
}
2024-05-16 05:07:56 +00:00
func response ( resp any , err error ) ( * op . Response , error ) {
if err != nil {
return nil , err
}
return op . NewResponse ( resp ) , nil
}