diff --git a/pkg/oauth2/oauth2.go b/pkg/oauth2/oauth2.go index 5709263c9f..a622beb33b 100644 --- a/pkg/oauth2/oauth2.go +++ b/pkg/oauth2/oauth2.go @@ -5,45 +5,11 @@ package oauth2 import ( "context" - "errors" mfclients "github.com/andychao217/magistrala/pkg/clients" "golang.org/x/oauth2" ) -// State is the state of the OAuth2 flow. -type State uint8 - -const ( - // SignIn is the state for the sign-in flow. - SignIn State = iota - // SignUp is the state for the sign-up flow. - SignUp -) - -func (s State) String() string { - switch s { - case SignIn: - return "signin" - case SignUp: - return "signup" - default: - return "unknown" - } -} - -// ToState converts string value to a valid OAuth2 state. -func ToState(state string) (State, error) { - switch state { - case "signin": - return SignIn, nil - case "signup": - return SignUp, nil - } - - return State(0), errors.New("invalid state") -} - // Config is the configuration for the OAuth2 provider. type Config struct { ClientID string `env:"CLIENT_ID" envDefault:""` diff --git a/users/api/clients.go b/users/api/clients.go index 3fe27b95ca..6b30964008 100644 --- a/users/api/clients.go +++ b/users/api/clients.go @@ -543,19 +543,7 @@ func oauth2CallbackHandler(oauth oauth2.Provider, svc users.Service) http.Handle http.Redirect(w, r, oauth.ErrorURL()+"?error=oauth%20provider%20is%20disabled", http.StatusSeeOther) return } - // state is prefixed with signin- or signup- to indicate which flow we should use - var state string - var flow oauth2.State - var err error - if strings.Contains(r.FormValue("state"), "-") { - state = strings.Split(r.FormValue("state"), "-")[1] - flow, err = oauth2.ToState(strings.Split(r.FormValue("state"), "-")[0]) - if err != nil { - http.Redirect(w, r, oauth.ErrorURL()+"?error="+err.Error(), http.StatusSeeOther) //nolint:goconst - return - } - } - + state := r.FormValue("state") if state != oauth.State() { http.Redirect(w, r, oauth.ErrorURL()+"?error=invalid%20state", http.StatusSeeOther) return @@ -574,7 +562,7 @@ func oauth2CallbackHandler(oauth oauth2.Provider, svc users.Service) http.Handle return } - jwt, err := svc.OAuthCallback(r.Context(), flow, client) + jwt, err := svc.OAuthCallback(r.Context(), client) if err != nil { http.Redirect(w, r, oauth.ErrorURL()+"?error="+err.Error(), http.StatusSeeOther) return diff --git a/users/api/logging.go b/users/api/logging.go index a29fd1c346..de068c4adb 100644 --- a/users/api/logging.go +++ b/users/api/logging.go @@ -10,7 +10,6 @@ import ( "github.com/andychao217/magistrala" mgclients "github.com/andychao217/magistrala/pkg/clients" - mgoauth2 "github.com/andychao217/magistrala/pkg/oauth2" "github.com/andychao217/magistrala/users" ) @@ -399,11 +398,10 @@ func (lm *loggingMiddleware) Identify(ctx context.Context, token string) (id str return lm.svc.Identify(ctx, token) } -func (lm *loggingMiddleware) OAuthCallback(ctx context.Context, state mgoauth2.State, client mgclients.Client) (token *magistrala.Token, err error) { +func (lm *loggingMiddleware) OAuthCallback(ctx context.Context, client mgclients.Client) (token *magistrala.Token, err error) { defer func(begin time.Time) { args := []any{ slog.String("duration", time.Since(begin).String()), - slog.String("state", state.String()), slog.String("user_id", client.ID), } if err != nil { @@ -413,7 +411,7 @@ func (lm *loggingMiddleware) OAuthCallback(ctx context.Context, state mgoauth2.S } lm.logger.Info("OAuth callback completed successfully", args...) }(time.Now()) - return lm.svc.OAuthCallback(ctx, state, client) + return lm.svc.OAuthCallback(ctx, client) } // DeleteClient logs the delete_client request. It logs the client id and token and the time it took to complete the request. diff --git a/users/api/metrics.go b/users/api/metrics.go index 1ead174032..34d065f9a5 100644 --- a/users/api/metrics.go +++ b/users/api/metrics.go @@ -9,7 +9,6 @@ import ( "github.com/andychao217/magistrala" mgclients "github.com/andychao217/magistrala/pkg/clients" - mgoauth2 "github.com/andychao217/magistrala/pkg/oauth2" "github.com/andychao217/magistrala/users" "github.com/go-kit/kit/metrics" ) @@ -193,13 +192,12 @@ func (ms *metricsMiddleware) Identify(ctx context.Context, token string) (string return ms.svc.Identify(ctx, token) } -func (ms *metricsMiddleware) OAuthCallback(ctx context.Context, state mgoauth2.State, client mgclients.Client) (*magistrala.Token, error) { - method := "oauth_callback_" + state.String() +func (ms *metricsMiddleware) OAuthCallback(ctx context.Context, client mgclients.Client) (*magistrala.Token, error) { defer func(begin time.Time) { - ms.counter.With("method", method).Add(1) - ms.latency.With("method", method).Observe(time.Since(begin).Seconds()) + ms.counter.With("method", "oauth_callback").Add(1) + ms.latency.With("method", "oauth_callback").Observe(time.Since(begin).Seconds()) }(time.Now()) - return ms.svc.OAuthCallback(ctx, state, client) + return ms.svc.OAuthCallback(ctx, client) } // DeleteClient instruments DeleteClient method with metrics. diff --git a/users/clients.go b/users/clients.go index 9ed5f62a8b..7f2df33ceb 100644 --- a/users/clients.go +++ b/users/clients.go @@ -8,7 +8,6 @@ import ( "github.com/andychao217/magistrala" "github.com/andychao217/magistrala/pkg/clients" - mgoauth2 "github.com/andychao217/magistrala/pkg/oauth2" ) // Service specifies an API that must be fullfiled by the domain service @@ -80,5 +79,5 @@ type Service interface { // OAuthCallback handles the callback from any supported OAuth provider. // It processes the OAuth tokens and either signs in or signs up the user based on the provided state. - OAuthCallback(ctx context.Context, state mgoauth2.State, client clients.Client) (*magistrala.Token, error) + OAuthCallback(ctx context.Context, client clients.Client) (*magistrala.Token, error) } diff --git a/users/events/events.go b/users/events/events.go index 48883cad7f..74f124304f 100644 --- a/users/events/events.go +++ b/users/events/events.go @@ -416,14 +416,12 @@ func (spre sendPasswordResetEvent) Encode() (map[string]interface{}, error) { } type oauthCallbackEvent struct { - state string clientID string } func (oce oauthCallbackEvent) Encode() (map[string]interface{}, error) { return map[string]interface{}{ "operation": oauthCallback, - "state": oce.state, "client_id": oce.clientID, }, nil } diff --git a/users/events/streams.go b/users/events/streams.go index 1f5468e9cb..cfc0524cc9 100644 --- a/users/events/streams.go +++ b/users/events/streams.go @@ -10,7 +10,6 @@ import ( mgclients "github.com/andychao217/magistrala/pkg/clients" "github.com/andychao217/magistrala/pkg/events" "github.com/andychao217/magistrala/pkg/events/store" - mgoauth2 "github.com/andychao217/magistrala/pkg/oauth2" "github.com/andychao217/magistrala/users" ) @@ -297,14 +296,13 @@ func (es *eventStore) SendPasswordReset(ctx context.Context, host, email, user, return es.Publish(ctx, event) } -func (es *eventStore) OAuthCallback(ctx context.Context, state mgoauth2.State, client mgclients.Client) (*magistrala.Token, error) { - token, err := es.svc.OAuthCallback(ctx, state, client) +func (es *eventStore) OAuthCallback(ctx context.Context, client mgclients.Client) (*magistrala.Token, error) { + token, err := es.svc.OAuthCallback(ctx, client) if err != nil { return token, err } event := oauthCallbackEvent{ - state: state.String(), clientID: client.ID, } diff --git a/users/mocks/service.go b/users/mocks/service.go index 1970edace0..77439a3286 100644 --- a/users/mocks/service.go +++ b/users/mocks/service.go @@ -12,8 +12,6 @@ import ( magistrala "github.com/andychao217/magistrala" mock "github.com/stretchr/testify/mock" - - oauth2 "github.com/andychao217/magistrala/pkg/oauth2" ) // Service is an autogenerated mock type for the Service type @@ -226,9 +224,9 @@ func (_m *Service) ListMembers(ctx context.Context, token string, objectKind str return r0, r1 } -// OAuthCallback provides a mock function with given fields: ctx, state, client -func (_m *Service) OAuthCallback(ctx context.Context, state oauth2.State, client clients.Client) (*magistrala.Token, error) { - ret := _m.Called(ctx, state, client) +// OAuthCallback provides a mock function with given fields: ctx, client +func (_m *Service) OAuthCallback(ctx context.Context, client clients.Client) (*magistrala.Token, error) { + ret := _m.Called(ctx, client) if len(ret) == 0 { panic("no return value specified for OAuthCallback") @@ -236,19 +234,19 @@ func (_m *Service) OAuthCallback(ctx context.Context, state oauth2.State, client var r0 *magistrala.Token var r1 error - if rf, ok := ret.Get(0).(func(context.Context, oauth2.State, clients.Client) (*magistrala.Token, error)); ok { - return rf(ctx, state, client) + if rf, ok := ret.Get(0).(func(context.Context, clients.Client) (*magistrala.Token, error)); ok { + return rf(ctx, client) } - if rf, ok := ret.Get(0).(func(context.Context, oauth2.State, clients.Client) *magistrala.Token); ok { - r0 = rf(ctx, state, client) + if rf, ok := ret.Get(0).(func(context.Context, clients.Client) *magistrala.Token); ok { + r0 = rf(ctx, client) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*magistrala.Token) } } - if rf, ok := ret.Get(1).(func(context.Context, oauth2.State, clients.Client) error); ok { - r1 = rf(ctx, state, client) + if rf, ok := ret.Get(1).(func(context.Context, clients.Client) error); ok { + r1 = rf(ctx, client) } else { r1 = ret.Error(1) } diff --git a/users/service.go b/users/service.go index a7e41e4fe6..1012a1201d 100644 --- a/users/service.go +++ b/users/service.go @@ -22,7 +22,6 @@ import ( "github.com/andychao217/magistrala/pkg/errors" repoerr "github.com/andychao217/magistrala/pkg/errors/repository" svcerr "github.com/andychao217/magistrala/pkg/errors/service" - mgoauth2 "github.com/andychao217/magistrala/pkg/oauth2" "github.com/andychao217/magistrala/users/postgres" "github.com/go-redis/redis/v8" "golang.org/x/sync/errgroup" @@ -30,7 +29,6 @@ import ( var ( errIssueToken = errors.New("failed to issue token") - errUserNotSignedUp = errors.New("user not signed up") errFailedPermissionsList = errors.New("failed to list permissions") errRecoveryToken = errors.New("failed to generate password recovery token") errLoginDisableUser = errors.New("failed to login in disabled user") @@ -961,37 +959,32 @@ func (svc *service) authorize(ctx context.Context, subjType, subjKind, subj, per return res.GetId(), nil } -func (svc service) OAuthCallback(ctx context.Context, state mgoauth2.State, client mgclients.Client) (*magistrala.Token, error) { - switch state { - case mgoauth2.SignIn: - rclient, err := svc.clients.RetrieveByIdentity(ctx, client.Credentials.Identity) - if err != nil { - if errors.Contains(err, repoerr.ErrNotFound) { - return &magistrala.Token{}, errors.Wrap(svcerr.ErrNotFound, errUserNotSignedUp) - } - return &magistrala.Token{}, errors.Wrap(svcerr.ErrViewEntity, err) - } - claims := &magistrala.IssueReq{ - UserId: rclient.ID, - Type: uint32(auth.AccessKey), - } - return svc.auth.Issue(ctx, claims) - case mgoauth2.SignUp: - rclient, err := svc.RegisterClient(ctx, "", client) - if err != nil { - if errors.Contains(err, repoerr.ErrConflict) { - return &magistrala.Token{}, errors.Wrap(svcerr.ErrConflict, errors.New("user already exists")) +func (svc service) OAuthCallback(ctx context.Context, client mgclients.Client) (*magistrala.Token, error) { + rclient, err := svc.clients.RetrieveByIdentity(ctx, client.Credentials.Identity) + if err != nil { + switch errors.Contains(err, repoerr.ErrNotFound) { + case true: + rclient, err = svc.RegisterClient(ctx, "", client) + if err != nil { + return &magistrala.Token{}, err } + default: return &magistrala.Token{}, err } - claims := &magistrala.IssueReq{ - UserId: rclient.ID, - Type: uint32(auth.AccessKey), + } + + if _, err = svc.authorize(ctx, auth.UserType, auth.UsersKind, rclient.ID, auth.MembershipPermission, auth.PlatformType, auth.MagistralaObject); err != nil { + if err := svc.addClientPolicy(ctx, rclient.ID, rclient.Role); err != nil { + return &magistrala.Token{}, err } - return svc.auth.Issue(ctx, claims) - default: - return &magistrala.Token{}, fmt.Errorf("unknown state %s", state) } + + claims := &magistrala.IssueReq{ + UserId: rclient.ID, + Type: uint32(auth.AccessKey), + } + + return svc.auth.Issue(ctx, claims) } func (svc service) Identify(ctx context.Context, token string) (string, error) { diff --git a/users/service_test.go b/users/service_test.go index 5a1bb1bc48..505e7736ac 100644 --- a/users/service_test.go +++ b/users/service_test.go @@ -17,7 +17,6 @@ import ( "github.com/andychao217/magistrala/pkg/errors" repoerr "github.com/andychao217/magistrala/pkg/errors/repository" svcerr "github.com/andychao217/magistrala/pkg/errors/service" - mgoauth2 "github.com/andychao217/magistrala/pkg/oauth2" "github.com/andychao217/magistrala/pkg/uuid" "github.com/andychao217/magistrala/users" "github.com/andychao217/magistrala/users/hasher" @@ -2643,31 +2642,33 @@ func TestOAuthCallback(t *testing.T) { cases := []struct { desc string - state mgoauth2.State client mgclients.Client retrieveByIdentityResponse mgclients.Client retrieveByIdentityErr error addPoliciesResponse *magistrala.AddPoliciesRes addPoliciesErr error - deletePoliciesResponse *magistrala.DeletePoliciesRes - deletePoliciesErr error saveResponse mgclients.Client saveErr error + deletePoliciesResponse *magistrala.DeletePoliciesRes + deletePoliciesErr error + authorizeResponse *magistrala.AuthorizeRes + authorizeErr error issueResponse *magistrala.Token issueErr error err error }{ { - desc: "oauth signin callback with successfully", - state: mgoauth2.SignIn, + desc: "oauth signin callback with successfully", client: mgclients.Client{ Credentials: mgclients.Credentials{ Identity: "test@example.com", }, }, retrieveByIdentityResponse: mgclients.Client{ - ID: testsutil.GenerateUUID(t), + ID: testsutil.GenerateUUID(t), + Role: mgclients.UserRole, }, + authorizeResponse: &magistrala.AuthorizeRes{Authorized: true}, issueResponse: &magistrala.Token{ AccessToken: strings.Repeat("a", 10), RefreshToken: &validToken, @@ -2676,37 +2677,63 @@ func TestOAuthCallback(t *testing.T) { err: nil, }, { - desc: "oauth signin callback with error", - state: mgoauth2.SignIn, + desc: "oauth signup callback with successfully", client: mgclients.Client{ Credentials: mgclients.Credentials{ Identity: "test@example.com", }, }, - retrieveByIdentityResponse: mgclients.Client{}, - retrieveByIdentityErr: repoerr.ErrNotFound, - issueResponse: &magistrala.Token{}, - err: errors.New("user not signed up"), + retrieveByIdentityErr: repoerr.ErrNotFound, + addPoliciesResponse: &magistrala.AddPoliciesRes{ + Added: true, + }, + saveResponse: mgclients.Client{ + ID: testsutil.GenerateUUID(t), + Role: mgclients.UserRole, + }, + issueResponse: &magistrala.Token{ + AccessToken: strings.Repeat("a", 10), + RefreshToken: &validToken, + AccessType: "Bearer", + }, + err: nil, }, { - desc: "oauth signup callback with successfully", - state: mgoauth2.SignUp, + desc: "oauth signup callback with unknown error", client: mgclients.Client{ Credentials: mgclients.Credentials{ Identity: "test@example.com", }, }, - retrieveByIdentityResponse: mgclients.Client{ - ID: testsutil.GenerateUUID(t), + retrieveByIdentityErr: repoerr.ErrMalformedEntity, + err: repoerr.ErrMalformedEntity, + }, + { + desc: "oauth signup callback with failed to register user", + client: mgclients.Client{ + Credentials: mgclients.Credentials{ + Identity: "test@example.com", + }, }, - addPoliciesResponse: &magistrala.AddPoliciesRes{ - Added: true, + retrieveByIdentityErr: repoerr.ErrNotFound, + addPoliciesResponse: &magistrala.AddPoliciesRes{Added: false}, + addPoliciesErr: svcerr.ErrAuthorization, + err: svcerr.ErrAuthorization, + }, + { + desc: "oauth signin callback with user not in the platform", + client: mgclients.Client{ + Credentials: mgclients.Credentials{ + Identity: "test@example.com", + }, }, - addPoliciesErr: nil, - saveResponse: mgclients.Client{ - ID: testsutil.GenerateUUID(t), + retrieveByIdentityResponse: mgclients.Client{ + ID: testsutil.GenerateUUID(t), + Role: mgclients.UserRole, }, - saveErr: nil, + authorizeResponse: &magistrala.AuthorizeRes{Authorized: false}, + authorizeErr: svcerr.ErrAuthorization, + addPoliciesResponse: &magistrala.AddPoliciesRes{Added: true}, issueResponse: &magistrala.Token{ AccessToken: strings.Repeat("a", 10), RefreshToken: &validToken, @@ -2715,61 +2742,68 @@ func TestOAuthCallback(t *testing.T) { err: nil, }, { - desc: "oauth signup callback with error", - state: mgoauth2.SignUp, + desc: "oauth signin callback with user not in the platform and failed to add policy", client: mgclients.Client{ Credentials: mgclients.Credentials{ Identity: "test@example.com", }, }, retrieveByIdentityResponse: mgclients.Client{ - ID: testsutil.GenerateUUID(t), + ID: testsutil.GenerateUUID(t), + Role: mgclients.UserRole, }, - addPoliciesResponse: &magistrala.AddPoliciesRes{ - Added: true, + authorizeResponse: &magistrala.AuthorizeRes{Authorized: false}, + authorizeErr: svcerr.ErrAuthorization, + addPoliciesResponse: &magistrala.AddPoliciesRes{Added: false}, + addPoliciesErr: svcerr.ErrAuthorization, + err: svcerr.ErrAuthorization, + }, + { + desc: "oauth signin callback with failed to issue token", + client: mgclients.Client{ + Credentials: mgclients.Credentials{ + Identity: "test@example.com", + }, }, - addPoliciesErr: nil, - deletePoliciesResponse: &magistrala.DeletePoliciesRes{ - Deleted: true, + retrieveByIdentityResponse: mgclients.Client{ + ID: testsutil.GenerateUUID(t), + Role: mgclients.UserRole, }, - deletePoliciesErr: nil, - saveResponse: mgclients.Client{}, - saveErr: repoerr.ErrConflict, - issueResponse: &magistrala.Token{}, - err: errors.New("user already exists"), + authorizeResponse: &magistrala.AuthorizeRes{Authorized: true}, + issueErr: svcerr.ErrAuthorization, + err: svcerr.ErrAuthorization, }, } for _, tc := range cases { - switch tc.state { - case mgoauth2.SignUp: - repoCall := cRepo.On("Save", context.Background(), mock.Anything).Return(tc.saveResponse, tc.saveErr) - repoCall1 := auth.On("AddPolicies", context.Background(), mock.Anything).Return(tc.addPoliciesResponse, tc.addPoliciesErr) - repoCall2 := auth.On("DeletePolicies", context.Background(), mock.Anything).Return(tc.deletePoliciesResponse, tc.deletePoliciesErr) - repoCall3 := auth.On("Issue", context.Background(), mock.Anything).Return(tc.issueResponse, tc.issueErr) - - token, err := svc.OAuthCallback(context.Background(), tc.state, tc.client) - if err == nil { - assert.Equal(t, tc.issueResponse.AccessToken, token.AccessToken) - assert.Equal(t, tc.issueResponse.RefreshToken, token.RefreshToken) - } - assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err)) - repoCall.Unset() - repoCall1.Unset() - repoCall2.Unset() - repoCall3.Unset() - case mgoauth2.SignIn: - repoCall := cRepo.On("RetrieveByIdentity", context.Background(), tc.client.Credentials.Identity).Return(tc.retrieveByIdentityResponse, tc.retrieveByIdentityErr) - repoCall1 := auth.On("Issue", context.Background(), mock.Anything).Return(tc.issueResponse, tc.issueErr) - token, err := svc.OAuthCallback(context.Background(), tc.state, tc.client) - if err == nil { - assert.Equal(t, tc.issueResponse.AccessToken, token.AccessToken) - assert.Equal(t, tc.issueResponse.RefreshToken, token.RefreshToken) - } - assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err)) - repoCall.Parent.AssertCalled(t, "RetrieveByIdentity", context.Background(), tc.client.Credentials.Identity) - repoCall.Unset() - repoCall1.Unset() + id := tc.saveResponse.ID + if tc.retrieveByIdentityResponse.ID != "" { + id = tc.retrieveByIdentityResponse.ID + } + authReq := &magistrala.AuthorizeReq{ + SubjectType: authsvc.UserType, + SubjectKind: authsvc.UsersKind, + Subject: id, + Permission: authsvc.MembershipPermission, + ObjectType: authsvc.PlatformType, + Object: authsvc.MagistralaObject, } + repoCall := cRepo.On("RetrieveByIdentity", context.Background(), tc.client.Credentials.Identity).Return(tc.retrieveByIdentityResponse, tc.retrieveByIdentityErr) + repoCall1 := cRepo.On("Save", context.Background(), mock.Anything).Return(tc.saveResponse, tc.saveErr) + authCall := auth.On("Issue", mock.Anything, mock.Anything).Return(tc.issueResponse, tc.issueErr) + authCall1 := auth.On("AddPolicies", mock.Anything, mock.Anything).Return(tc.addPoliciesResponse, tc.addPoliciesErr) + authCall2 := auth.On("Authorize", mock.Anything, authReq).Return(tc.authorizeResponse, tc.authorizeErr) + token, err := svc.OAuthCallback(context.Background(), tc.client) + if err == nil { + assert.Equal(t, tc.issueResponse.AccessToken, token.AccessToken) + assert.Equal(t, tc.issueResponse.RefreshToken, token.RefreshToken) + } + assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err)) + repoCall.Parent.AssertCalled(t, "RetrieveByIdentity", context.Background(), tc.client.Credentials.Identity) + repoCall.Unset() + repoCall1.Unset() + authCall.Unset() + authCall1.Unset() + authCall2.Unset() } } diff --git a/users/tracing/tracing.go b/users/tracing/tracing.go index c928a168ba..7cd05d8125 100644 --- a/users/tracing/tracing.go +++ b/users/tracing/tracing.go @@ -8,7 +8,6 @@ import ( "github.com/andychao217/magistrala" mgclients "github.com/andychao217/magistrala/pkg/clients" - mgoauth2 "github.com/andychao217/magistrala/pkg/oauth2" "github.com/andychao217/magistrala/users" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" @@ -195,14 +194,13 @@ func (tm *tracingMiddleware) Identify(ctx context.Context, token string) (string } // OAuthCallback traces the "OAuthCallback" operation of the wrapped clients.Service. -func (tm *tracingMiddleware) OAuthCallback(ctx context.Context, state mgoauth2.State, client mgclients.Client) (*magistrala.Token, error) { +func (tm *tracingMiddleware) OAuthCallback(ctx context.Context, client mgclients.Client) (*magistrala.Token, error) { ctx, span := tm.tracer.Start(ctx, "svc_oauth_callback", trace.WithAttributes( - attribute.String("state", state.String()), attribute.String("client_id", client.ID), )) defer span.End() - return tm.svc.OAuthCallback(ctx, state, client) + return tm.svc.OAuthCallback(ctx, client) } // DeleteClient traces the "DeleteClient" operation of the wrapped clients.Service.