From 1b94f796eb8443f2f84c16d81b555b0df263cc40 Mon Sep 17 00:00:00 2001 From: adlerhurst Date: Wed, 13 Mar 2024 15:45:03 +0100 Subject: [PATCH] move tracer to client, add tracing in rs, client --- pkg/client/client.go | 33 ++++++++++++++++++++--- pkg/client/rp/device.go | 4 +-- pkg/client/rp/jwks.go | 11 ++++---- pkg/client/rp/relying_party.go | 30 ++++++++------------- pkg/client/rp/verifier.go | 5 ++-- pkg/client/rs/resource_server.go | 3 +++ pkg/client/tokenexchange/tokenexchange.go | 3 +++ pkg/op/op.go | 7 +---- 8 files changed, 59 insertions(+), 37 deletions(-) diff --git a/pkg/client/client.go b/pkg/client/client.go index b329b3d..8b60264 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -12,19 +12,25 @@ import ( "time" jose "github.com/go-jose/go-jose/v3" - "golang.org/x/oauth2" - "github.com/zitadel/logging" "github.com/zitadel/oidc/v3/pkg/crypto" httphelper "github.com/zitadel/oidc/v3/pkg/http" "github.com/zitadel/oidc/v3/pkg/oidc" + "go.opentelemetry.io/otel" + "golang.org/x/oauth2" ) -var Encoder = httphelper.Encoder(oidc.NewEncoder()) +var ( + Encoder = httphelper.Encoder(oidc.NewEncoder()) + Tracer = otel.Tracer("github.com/zitadel/oidc/pkg/client") +) // Discover calls the discovery endpoint of the provided issuer and returns its configuration // It accepts an optional argument "wellknownUrl" which can be used to overide the dicovery endpoint url func Discover(ctx context.Context, issuer string, httpClient *http.Client, wellKnownUrl ...string) (*oidc.DiscoveryConfiguration, error) { + ctx, span := Tracer.Start(ctx, "Discover") + defer span.End() + wellKnown := strings.TrimSuffix(issuer, "/") + oidc.DiscoveryEndpoint if len(wellKnownUrl) == 1 && wellKnownUrl[0] != "" { wellKnown = wellKnownUrl[0] @@ -58,6 +64,9 @@ func CallTokenEndpoint(ctx context.Context, request any, caller TokenEndpointCal } func callTokenEndpoint(ctx context.Context, request any, authFn any, caller TokenEndpointCaller) (newToken *oauth2.Token, err error) { + ctx, span := Tracer.Start(ctx, "callTokenEndpoint") + defer span.End() + req, err := httphelper.FormRequest(ctx, caller.TokenEndpoint(), request, Encoder, authFn) if err != nil { return nil, err @@ -86,6 +95,9 @@ type EndSessionCaller interface { } func CallEndSessionEndpoint(ctx context.Context, request any, authFn any, caller EndSessionCaller) (*url.URL, error) { + ctx, span := Tracer.Start(ctx, "CallEndSessionEndpoint") + defer span.End() + req, err := httphelper.FormRequest(ctx, caller.GetEndSessionEndpoint(), request, Encoder, authFn) if err != nil { return nil, err @@ -129,6 +141,9 @@ type RevokeRequest struct { } func CallRevokeEndpoint(ctx context.Context, request any, authFn any, caller RevokeCaller) error { + ctx, span := Tracer.Start(ctx, "CallRevokeEndpoint") + defer span.End() + req, err := httphelper.FormRequest(ctx, caller.GetRevokeEndpoint(), request, Encoder, authFn) if err != nil { return err @@ -157,6 +172,9 @@ func CallRevokeEndpoint(ctx context.Context, request any, authFn any, caller Rev } func CallTokenExchangeEndpoint(ctx context.Context, request any, authFn any, caller TokenEndpointCaller) (resp *oidc.TokenExchangeResponse, err error) { + ctx, span := Tracer.Start(ctx, "CallTokenExchangeEndpoint") + defer span.End() + req, err := httphelper.FormRequest(ctx, caller.TokenEndpoint(), request, Encoder, authFn) if err != nil { return nil, err @@ -198,6 +216,9 @@ type DeviceAuthorizationCaller interface { } func CallDeviceAuthorizationEndpoint(ctx context.Context, request *oidc.ClientCredentialsRequest, caller DeviceAuthorizationCaller, authFn any) (*oidc.DeviceAuthorizationResponse, error) { + ctx, span := Tracer.Start(ctx, "CallDeviceAuthorizationEndpoint") + defer span.End() + req, err := httphelper.FormRequest(ctx, caller.GetDeviceAuthorizationEndpoint(), request, Encoder, authFn) if err != nil { return nil, err @@ -219,6 +240,9 @@ type DeviceAccessTokenRequest struct { } func CallDeviceAccessTokenEndpoint(ctx context.Context, request *DeviceAccessTokenRequest, caller TokenEndpointCaller) (*oidc.AccessTokenResponse, error) { + ctx, span := Tracer.Start(ctx, "CallDeviceAccessTokenEndpoint") + defer span.End() + req, err := httphelper.FormRequest(ctx, caller.TokenEndpoint(), request, Encoder, nil) if err != nil { return nil, err @@ -249,6 +273,9 @@ func CallDeviceAccessTokenEndpoint(ctx context.Context, request *DeviceAccessTok } func PollDeviceAccessTokenEndpoint(ctx context.Context, interval time.Duration, request *DeviceAccessTokenRequest, caller TokenEndpointCaller) (*oidc.AccessTokenResponse, error) { + ctx, span := Tracer.Start(ctx, "PollDeviceAccessTokenEndpoint") + defer span.End() + for { timer := time.After(interval) select { diff --git a/pkg/client/rp/device.go b/pkg/client/rp/device.go index ec4dabe..c2d1f8a 100644 --- a/pkg/client/rp/device.go +++ b/pkg/client/rp/device.go @@ -33,7 +33,7 @@ func newDeviceClientCredentialsRequest(scopes []string, rp RelyingParty) (*oidc. // in RFC 8628, section 3.1 and 3.2: // https://www.rfc-editor.org/rfc/rfc8628#section-3.1 func DeviceAuthorization(ctx context.Context, scopes []string, rp RelyingParty, authFn any) (*oidc.DeviceAuthorizationResponse, error) { - ctx, span := tracer.Start(ctx, "DeviceAuthorization") + ctx, span := client.Tracer.Start(ctx, "DeviceAuthorization") defer span.End() ctx = logCtxWithRPData(ctx, rp, "function", "DeviceAuthorization") @@ -49,7 +49,7 @@ func DeviceAuthorization(ctx context.Context, scopes []string, rp RelyingParty, // by means of polling as defined in RFC, section 3.3 and 3.4: // https://www.rfc-editor.org/rfc/rfc8628#section-3.4 func DeviceAccessToken(ctx context.Context, deviceCode string, interval time.Duration, rp RelyingParty) (resp *oidc.AccessTokenResponse, err error) { - ctx, span := tracer.Start(ctx, "DeviceAccessToken") + ctx, span := client.Tracer.Start(ctx, "DeviceAccessToken") defer span.End() ctx = logCtxWithRPData(ctx, rp, "function", "DeviceAccessToken") diff --git a/pkg/client/rp/jwks.go b/pkg/client/rp/jwks.go index a3c1647..a061777 100644 --- a/pkg/client/rp/jwks.go +++ b/pkg/client/rp/jwks.go @@ -9,6 +9,7 @@ import ( jose "github.com/go-jose/go-jose/v3" + "github.com/zitadel/oidc/v3/pkg/client" httphelper "github.com/zitadel/oidc/v3/pkg/http" "github.com/zitadel/oidc/v3/pkg/oidc" ) @@ -83,7 +84,7 @@ func (i *inflight) result() ([]jose.JSONWebKey, error) { } func (r *remoteKeySet) VerifySignature(ctx context.Context, jws *jose.JSONWebSignature) ([]byte, error) { - ctx, span := tracer.Start(ctx, "VerifySignature") + ctx, span := client.Tracer.Start(ctx, "VerifySignature") defer span.End() keyID, alg := oidc.GetKeyIDAndAlg(jws) @@ -138,7 +139,7 @@ func (r *remoteKeySet) exactMatch(jwkID, jwsID string) bool { } func (r *remoteKeySet) verifySignatureRemote(ctx context.Context, jws *jose.JSONWebSignature, keyID, alg string) ([]byte, error) { - ctx, span := tracer.Start(ctx, "verifySignatureRemote") + ctx, span := client.Tracer.Start(ctx, "verifySignatureRemote") defer span.End() keys, err := r.keysFromRemote(ctx) @@ -165,7 +166,7 @@ func (r *remoteKeySet) keysFromCache() (keys []jose.JSONWebKey) { // keysFromRemote syncs the key set from the remote set, records the values in the // cache, and returns the key set. func (r *remoteKeySet) keysFromRemote(ctx context.Context) ([]jose.JSONWebKey, error) { - ctx, span := tracer.Start(ctx, "keysFromRemote") + ctx, span := client.Tracer.Start(ctx, "keysFromRemote") defer span.End() // Need to lock to inspect the inflight request field. @@ -191,7 +192,7 @@ func (r *remoteKeySet) keysFromRemote(ctx context.Context) ([]jose.JSONWebKey, e } func (r *remoteKeySet) updateKeys(ctx context.Context) { - ctx, span := tracer.Start(ctx, "updateKeys") + ctx, span := client.Tracer.Start(ctx, "updateKeys") defer span.End() // Sync keys and finish inflight when that's done. @@ -213,7 +214,7 @@ func (r *remoteKeySet) updateKeys(ctx context.Context) { } func (r *remoteKeySet) fetchRemoteKeys(ctx context.Context) ([]jose.JSONWebKey, error) { - ctx, span := tracer.Start(ctx, "fetchRemoteKeys") + ctx, span := client.Tracer.Start(ctx, "fetchRemoteKeys") defer span.End() req, err := http.NewRequest("GET", r.jwksURL, nil) diff --git a/pkg/client/rp/relying_party.go b/pkg/client/rp/relying_party.go index 3022035..62c650e 100644 --- a/pkg/client/rp/relying_party.go +++ b/pkg/client/rp/relying_party.go @@ -11,12 +11,10 @@ import ( "github.com/go-jose/go-jose/v3" "github.com/google/uuid" - "github.com/zitadel/logging" - "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/trace" "golang.org/x/oauth2" "golang.org/x/oauth2/clientcredentials" + "github.com/zitadel/logging" "github.com/zitadel/oidc/v3/pkg/client" httphelper "github.com/zitadel/oidc/v3/pkg/http" "github.com/zitadel/oidc/v3/pkg/oidc" @@ -30,12 +28,6 @@ const ( var ErrUserInfoSubNotMatching = errors.New("sub from userinfo does not match the sub from the id_token") -var tracer trace.Tracer - -func init() { - tracer = otel.Tracer("github.com/zitadel/oidc/pkg/client/rp") -} - // RelyingParty declares the minimal interface for oidc clients type RelyingParty interface { // OAuthConfig returns the oauth2 Config @@ -436,7 +428,7 @@ func GenerateAndStoreCodeChallenge(w http.ResponseWriter, rp RelyingParty) (stri var ErrMissingIDToken = errors.New("id_token missing") func verifyTokenResponse[C oidc.IDClaims](ctx context.Context, token *oauth2.Token, rp RelyingParty) (*oidc.Tokens[C], error) { - ctx, span := tracer.Start(ctx, "verifyTokenResponse") + ctx, span := client.Tracer.Start(ctx, "verifyTokenResponse") defer span.End() if rp.IsOAuth2Only() { @@ -456,7 +448,7 @@ func verifyTokenResponse[C oidc.IDClaims](ctx context.Context, token *oauth2.Tok // CodeExchange handles the oauth2 code exchange, extracting and validating the id_token // returning it parsed together with the oauth2 tokens (access, refresh) func CodeExchange[C oidc.IDClaims](ctx context.Context, code string, rp RelyingParty, opts ...CodeExchangeOpt) (tokens *oidc.Tokens[C], err error) { - ctx, codeExchangeSpan := tracer.Start(ctx, "CodeExchange") + ctx, codeExchangeSpan := client.Tracer.Start(ctx, "CodeExchange") defer codeExchangeSpan.End() ctx = logCtxWithRPData(ctx, rp, "function", "CodeExchange") @@ -466,7 +458,7 @@ func CodeExchange[C oidc.IDClaims](ctx context.Context, code string, rp RelyingP codeOpts = append(codeOpts, opt()...) } - ctx, oauthExchangeSpan := tracer.Start(ctx, "OAuthExchange") + ctx, oauthExchangeSpan := client.Tracer.Start(ctx, "OAuthExchange") token, err := rp.OAuthConfig().Exchange(ctx, code, codeOpts...) if err != nil { return nil, err @@ -485,7 +477,7 @@ func CodeExchange[C oidc.IDClaims](ctx context.Context, code string, rp RelyingP // [RFC 6749, section 4.4]: https://datatracker.ietf.org/doc/html/rfc6749#section-4.4 func ClientCredentials(ctx context.Context, rp RelyingParty, endpointParams url.Values) (token *oauth2.Token, err error) { ctx = logCtxWithRPData(ctx, rp, "function", "ClientCredentials") - ctx, span := tracer.Start(ctx, "ClientCredentials") + ctx, span := client.Tracer.Start(ctx, "ClientCredentials") defer span.End() ctx = context.WithValue(ctx, oauth2.HTTPClient, rp.HttpClient()) @@ -508,7 +500,7 @@ type CodeExchangeCallback[C oidc.IDClaims] func(w http.ResponseWriter, r *http.R // Custom parameters can optionally be set to the token URL. func CodeExchangeHandler[C oidc.IDClaims](callback CodeExchangeCallback[C], rp RelyingParty, urlParam ...URLParamOpt) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - ctx, span := tracer.Start(r.Context(), "CodeExchangeHandler") + ctx, span := client.Tracer.Start(r.Context(), "CodeExchangeHandler") r = r.WithContext(ctx) defer span.End() @@ -563,7 +555,7 @@ type CodeExchangeUserinfoCallback[C oidc.IDClaims, U SubjectGetter] func(w http. // on success it will pass the userinfo into its callback function as well func UserinfoCallback[C oidc.IDClaims, U SubjectGetter](f CodeExchangeUserinfoCallback[C, U]) CodeExchangeCallback[C] { return func(w http.ResponseWriter, r *http.Request, tokens *oidc.Tokens[C], state string, rp RelyingParty) { - ctx, span := tracer.Start(r.Context(), "UserinfoCallback") + ctx, span := client.Tracer.Start(r.Context(), "UserinfoCallback") r = r.WithContext(ctx) defer span.End() @@ -585,7 +577,7 @@ func UserinfoCallback[C oidc.IDClaims, U SubjectGetter](f CodeExchangeUserinfoCa func Userinfo[U SubjectGetter](ctx context.Context, token, tokenType, subject string, rp RelyingParty) (userinfo U, err error) { var nilU U ctx = logCtxWithRPData(ctx, rp, "function", "Userinfo") - ctx, span := tracer.Start(ctx, "Userinfo") + ctx, span := client.Tracer.Start(ctx, "Userinfo") defer span.End() req, err := http.NewRequestWithContext(ctx, http.MethodGet, rp.UserinfoEndpoint(), nil) @@ -745,7 +737,7 @@ type RefreshTokenRequest struct { // the IDToken and AccessToken will be verfied // and the IDToken and IDTokenClaims fields will be populated in the returned object. func RefreshTokens[C oidc.IDClaims](ctx context.Context, rp RelyingParty, refreshToken, clientAssertion, clientAssertionType string) (*oidc.Tokens[C], error) { - ctx, span := tracer.Start(ctx, "RefreshTokens") + ctx, span := client.Tracer.Start(ctx, "RefreshTokens") defer span.End() ctx = logCtxWithRPData(ctx, rp, "function", "RefreshTokens") @@ -773,7 +765,7 @@ func RefreshTokens[C oidc.IDClaims](ctx context.Context, rp RelyingParty, refres func EndSession(ctx context.Context, rp RelyingParty, idToken, optionalRedirectURI, optionalState string) (*url.URL, error) { ctx = logCtxWithRPData(ctx, rp, "function", "EndSession") - ctx, span := tracer.Start(ctx, "RefreshTokens") + ctx, span := client.Tracer.Start(ctx, "RefreshTokens") defer span.End() request := oidc.EndSessionRequest{ @@ -792,7 +784,7 @@ func EndSession(ctx context.Context, rp RelyingParty, idToken, optionalRedirectU // tokenTypeHint should be either "id_token" or "refresh_token". func RevokeToken(ctx context.Context, rp RelyingParty, token string, tokenTypeHint string) error { ctx = logCtxWithRPData(ctx, rp, "function", "RevokeToken") - ctx, span := tracer.Start(ctx, "RefreshTokens") + ctx, span := client.Tracer.Start(ctx, "RefreshTokens") defer span.End() request := client.RevokeRequest{ Token: token, diff --git a/pkg/client/rp/verifier.go b/pkg/client/rp/verifier.go index ebc10cc..94be079 100644 --- a/pkg/client/rp/verifier.go +++ b/pkg/client/rp/verifier.go @@ -6,13 +6,14 @@ import ( jose "github.com/go-jose/go-jose/v3" + "github.com/zitadel/oidc/v3/pkg/client" "github.com/zitadel/oidc/v3/pkg/oidc" ) // VerifyTokens implement the Token Response Validation as defined in OIDC specification // https://openid.net/specs/openid-connect-core-1_0.html#TokenResponseValidation func VerifyTokens[C oidc.IDClaims](ctx context.Context, accessToken, idToken string, v *IDTokenVerifier) (claims C, err error) { - ctx, span := tracer.Start(ctx, "VerifyTokens") + ctx, span := client.Tracer.Start(ctx, "VerifyTokens") defer span.End() var nilClaims C @@ -30,7 +31,7 @@ func VerifyTokens[C oidc.IDClaims](ctx context.Context, accessToken, idToken str // VerifyIDToken validates the id token according to // https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation func VerifyIDToken[C oidc.Claims](ctx context.Context, token string, v *IDTokenVerifier) (claims C, err error) { - ctx, span := tracer.Start(ctx, "VerifyIDToken") + ctx, span := client.Tracer.Start(ctx, "VerifyIDToken") defer span.End() var nilClaims C diff --git a/pkg/client/rs/resource_server.go b/pkg/client/rs/resource_server.go index 57925d5..962af7e 100644 --- a/pkg/client/rs/resource_server.go +++ b/pkg/client/rs/resource_server.go @@ -123,6 +123,9 @@ func WithStaticEndpoints(tokenURL, introspectURL string) Option { // // [RFC7662]: https://www.rfc-editor.org/rfc/rfc7662 func Introspect[R any](ctx context.Context, rp ResourceServer, token string) (resp R, err error) { + ctx, span := client.Tracer.Start(ctx, "Introspect") + defer span.End() + if rp.IntrospectionURL() == "" { return resp, errors.New("resource server: introspection URL is empty") } diff --git a/pkg/client/tokenexchange/tokenexchange.go b/pkg/client/tokenexchange/tokenexchange.go index fdac833..c62f38c 100644 --- a/pkg/client/tokenexchange/tokenexchange.go +++ b/pkg/client/tokenexchange/tokenexchange.go @@ -101,6 +101,9 @@ func ExchangeToken( Scopes []string, RequestedTokenType oidc.TokenType, ) (*oidc.TokenExchangeResponse, error) { + ctx, span := client.Tracer.Start(ctx, "ExchangeToken") + defer span.End() + if SubjectToken == "" { return nil, errors.New("empty subject_token") } diff --git a/pkg/op/op.go b/pkg/op/op.go index 326737a..3248317 100644 --- a/pkg/op/op.go +++ b/pkg/op/op.go @@ -12,7 +12,6 @@ import ( "github.com/rs/cors" "github.com/zitadel/schema" "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/trace" "golang.org/x/text/language" httphelper "github.com/zitadel/oidc/v3/pkg/http" @@ -97,11 +96,7 @@ var ( } ) -var tracer trace.Tracer - -func init() { - tracer = otel.Tracer("github.com/zitadel/oidc/pkg/op") -} +var tracer = otel.Tracer("github.com/zitadel/oidc/pkg/op") type OpenIDProvider interface { http.Handler