package command import ( "context" "encoding/base64" "testing" "time" "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" "github.com/zitadel/oidc/v2/pkg/oidc" "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/id" id_mock "github.com/zitadel/zitadel/internal/id/mock" "github.com/zitadel/zitadel/internal/repository/user" ) func TestCommands_AddAccessAndRefreshToken(t *testing.T) { type fields struct { eventstore *eventstore.Eventstore idGenerator id.Generator keyAlgorithm crypto.EncryptionAlgorithm } type args struct { ctx context.Context orgID string agentID string clientID string userID string refreshToken string audience []string scopes []string authMethodsReferences []string lifetime time.Duration authTime time.Time refreshIdleExpiration time.Duration refreshExpiration time.Duration } type res struct { token *domain.Token refreshToken string err func(error) bool } tests := []struct { name string fields fields args args res res }{ { name: "missing ID, error", fields: fields{ eventstore: eventstoreExpect(t), }, args: args{}, res: res{ err: caos_errs.IsErrorInvalidArgument, }, }, { name: "add refresh token, user deactivated, error", fields: fields{ eventstore: eventstoreExpect(t, expectFilter( eventFromEventPusher( user.NewUserDeactivatedEvent(context.Background(), &user.NewAggregate("userID", "orgID").Aggregate, ), ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "refreshTokenID1"), }, args: args{ ctx: context.Background(), orgID: "orgID", agentID: "agentID", userID: "userID", clientID: "clientID", }, res: res{ err: caos_errs.IsNotFound, }, }, { name: "renew refresh token, invalid token, error", fields: fields{ eventstore: eventstoreExpect(t), keyAlgorithm: refreshTokenEncryptionAlgorithm(gomock.NewController(t)), }, args: args{ ctx: context.Background(), refreshToken: "invalid", }, res: res{ err: caos_errs.IsErrorInvalidArgument, }, }, { name: "renew refresh token, invalid token (invalid userID), error", fields: fields{ eventstore: eventstoreExpect(t), keyAlgorithm: refreshTokenEncryptionAlgorithm(gomock.NewController(t)), }, args: args{ ctx: context.Background(), userID: "userID", orgID: "orgID", refreshToken: base64.RawURLEncoding.EncodeToString([]byte("userID2:tokenID:token")), }, res: res{ err: caos_errs.IsErrorInvalidArgument, }, }, { name: "renew refresh token, token inactive, error", fields: fields{ eventstore: eventstoreExpect(t, expectFilter( eventFromEventPusher(user.NewHumanRefreshTokenAddedEvent( context.Background(), &user.NewAggregate("userID", "orgID").Aggregate, "tokenID", "applicationID", "userAgentID", "de", []string{"clientID1"}, []string{oidc.ScopeOpenID, oidc.ScopeProfile, oidc.ScopeEmail, oidc.ScopeOfflineAccess}, []string{"password"}, time.Now(), 1*time.Hour, 24*time.Hour, )), eventFromEventPusher(user.NewHumanRefreshTokenRemovedEvent( context.Background(), &user.NewAggregate("userID", "orgID").Aggregate, "tokenID", )), ), ), keyAlgorithm: refreshTokenEncryptionAlgorithm(gomock.NewController(t)), }, args: args{ ctx: context.Background(), userID: "userID", orgID: "orgID", refreshToken: base64.RawURLEncoding.EncodeToString([]byte("userID:tokenID:token")), }, res: res{ err: caos_errs.IsErrorInvalidArgument, }, }, { name: "renew refresh token, token expired, error", fields: fields{ eventstore: eventstoreExpect(t, expectFilter( eventFromEventPusher(user.NewHumanRefreshTokenAddedEvent( context.Background(), &user.NewAggregate("userID", "orgID").Aggregate, "tokenID", "applicationID", "userAgentID", "de", []string{"clientID1"}, []string{oidc.ScopeOpenID, oidc.ScopeProfile, oidc.ScopeEmail, oidc.ScopeOfflineAccess}, []string{"password"}, time.Now(), -1*time.Hour, 24*time.Hour, )), ), ), keyAlgorithm: refreshTokenEncryptionAlgorithm(gomock.NewController(t)), }, args: args{ ctx: context.Background(), userID: "userID", orgID: "orgID", refreshToken: base64.RawURLEncoding.EncodeToString([]byte("userID:tokenID:tokenID")), }, res: res{ err: caos_errs.IsErrorInvalidArgument, }, }, //fails because of timestamp equality //{ // name: "push failed, error", // fields: fields{ // eventstore: eventstoreExpect(t, // expectFilter( // eventFromEventPusher(user.NewHumanAddedEvent( // context.Background(), // &user.NewAggregate("userID", "orgID").Aggregate, // "username", // "firstname", // "lastname", // "nickname", // "displayname", // language.German, // domain.GenderUnspecified, // "email", // true, // )), // ), // expectFilter( // eventFromEventPusherWithCreationDateNow(user.NewHumanAddedEvent( // context.Background(), // &user.NewAggregate("userID", "orgID").Aggregate, // "username", // "firstname", // "lastname", // "nickname", // "displayname", // language.German, // domain.GenderUnspecified, // "email", // true, // )), // ), // expectFilter( // eventFromEventPusherWithCreationDateNow(user.NewHumanRefreshTokenAddedEvent( // context.Background(), // &user.NewAggregate("userID", "orgID").Aggregate, // "tokenID", // "applicationID", // "userAgentID", // "de", // []string{"clientID1"}, // []string{oidc.ScopeOpenID, oidc.ScopeProfile, oidc.ScopeEmail, oidc.ScopeOfflineAccess}, // []string{"password"}, // time.Now(), // 1*time.Hour, // 24*time.Hour, // )), // ), // expectPushFailed( // caos_errs.ThrowInternal(nil, "ERROR", "internal"), // []*repository.Event{ // eventFromEventPusher(user.NewUserTokenAddedEvent( // context.Background(), // &user.NewAggregate("userID", "orgID").Aggregate, // "accessTokenID1", // "clientID", // "agentID", // "de", // []string{"clientID1"}, // []string{oidc.ScopeOpenID, oidc.ScopeProfile, oidc.ScopeEmail, oidc.ScopeOfflineAccess}, // time.Now().Add(5*time.Minute), // )), // eventFromEventPusher(user.NewHumanRefreshTokenRenewedEvent( // context.Background(), // &user.NewAggregate("userID", "orgID").Aggregate, // "tokenID", // "refreshToken1", // 1*time.Hour, // )), // }, // ), // ), // idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "accessTokenID1", "refreshToken1"), // keyAlgorithm: refreshTokenEncryptionAlgorithm(gomock.NewController(t)), // }, // args: args{ // ctx: context.Background(), // orgID: "orgID", // agentID: "agentID", // clientID: "clientID", // userID: "userID", // refreshToken: base64.RawURLEncoding.EncodeToString([]byte("userID:tokenID:tokenID")), // audience: []string{"clientID1"}, // scopes: []string{oidc.ScopeOpenID, oidc.ScopeProfile, oidc.ScopeEmail, oidc.ScopeOfflineAccess}, // authMethodsReferences: []string{"password"}, // lifetime: 5 * time.Minute, // authTime: time.Now(), // }, // res: res{ // err: caos_errs.IsInternal, // }, //}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { c := &Commands{ eventstore: tt.fields.eventstore, idGenerator: tt.fields.idGenerator, keyAlgorithm: tt.fields.keyAlgorithm, } got, gotRefresh, err := c.AddAccessAndRefreshToken(tt.args.ctx, tt.args.orgID, tt.args.agentID, tt.args.clientID, tt.args.userID, tt.args.refreshToken, tt.args.audience, tt.args.scopes, tt.args.authMethodsReferences, tt.args.lifetime, tt.args.refreshIdleExpiration, tt.args.refreshExpiration, tt.args.authTime) if tt.res.err == nil { assert.NoError(t, err) } if tt.res.err != nil && !tt.res.err(err) { t.Errorf("got wrong err: %v ", err) } if tt.res.err == nil { assert.Equal(t, tt.res.token, got) assert.Equal(t, tt.res.refreshToken, gotRefresh) } }) } } func TestCommands_RevokeRefreshToken(t *testing.T) { type fields struct { eventstore *eventstore.Eventstore } type args struct { ctx context.Context userID string orgID string tokenID string } type res struct { want *domain.ObjectDetails err func(error) bool } tests := []struct { name string fields fields args args res res }{ { "missing param, error", fields{ eventstore: eventstoreExpect(t), }, args{}, res{ err: caos_errs.IsErrorInvalidArgument, }, }, { "token not active, error", fields{ eventstore: eventstoreExpect(t, expectFilter(), ), }, args{ context.Background(), "userID", "orgID", "tokenID", }, res{ err: caos_errs.IsNotFound, }, }, { "push failed, error", fields{ eventstore: eventstoreExpect(t, expectFilter( eventFromEventPusher(user.NewHumanRefreshTokenAddedEvent( context.Background(), &user.NewAggregate("userID", "orgID").Aggregate, "tokenID", "clientID", "agentID", "de", []string{"clientID1"}, []string{oidc.ScopeOpenID, oidc.ScopeProfile, oidc.ScopeEmail, oidc.ScopeOfflineAccess}, []string{"password"}, time.Now(), 1*time.Hour, 10*time.Hour, )), ), expectPushFailed(caos_errs.ThrowInternal(nil, "ERROR", "internal"), []*repository.Event{ eventFromEventPusher(user.NewHumanRefreshTokenRemovedEvent( context.Background(), &user.NewAggregate("userID", "orgID").Aggregate, "tokenID", )), }, ), ), }, args{ context.Background(), "userID", "orgID", "tokenID", }, res{ err: caos_errs.IsInternal, }, }, { "revoke, ok", fields{ eventstore: eventstoreExpect(t, expectFilter( eventFromEventPusher(user.NewHumanRefreshTokenAddedEvent( context.Background(), &user.NewAggregate("userID", "orgID").Aggregate, "tokenID", "clientID", "agentID", "de", []string{"clientID1"}, []string{oidc.ScopeOpenID, oidc.ScopeProfile, oidc.ScopeEmail, oidc.ScopeOfflineAccess}, []string{"password"}, time.Now(), 1*time.Hour, 10*time.Hour, )), ), expectPush( []*repository.Event{ eventFromEventPusher(user.NewHumanRefreshTokenRemovedEvent( context.Background(), &user.NewAggregate("userID", "orgID").Aggregate, "tokenID", )), }, ), ), }, args{ context.Background(), "userID", "orgID", "tokenID", }, res{ want: &domain.ObjectDetails{ ResourceOwner: "orgID", }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { c := &Commands{ eventstore: tt.fields.eventstore, } got, err := c.RevokeRefreshToken(tt.args.ctx, tt.args.userID, tt.args.orgID, tt.args.tokenID) if tt.res.err == nil { assert.NoError(t, err) } if tt.res.err != nil && !tt.res.err(err) { t.Errorf("got wrong err: %v ", err) } if tt.res.err == nil { assert.Equal(t, tt.res.want, got) } }) } } func TestCommands_RevokeRefreshTokens(t *testing.T) { type fields struct { eventstore *eventstore.Eventstore } type args struct { ctx context.Context userID string orgID string tokenIDs []string } type res struct { err func(error) bool } tests := []struct { name string fields fields args args res res }{ { "missing tokenIDs, error", fields{ eventstore: eventstoreExpect(t), }, args{ context.Background(), "userID", "orgID", nil, }, res{ err: caos_errs.IsErrorInvalidArgument, }, }, { "one token not active, error", fields{ eventstore: eventstoreExpect(t, expectFilter( eventFromEventPusher(user.NewHumanRefreshTokenAddedEvent( context.Background(), &user.NewAggregate("userID", "orgID").Aggregate, "tokenID", "clientID", "agentID", "de", []string{"clientID1"}, []string{oidc.ScopeOpenID, oidc.ScopeProfile, oidc.ScopeEmail, oidc.ScopeOfflineAccess}, []string{"password"}, time.Now(), 1*time.Hour, 10*time.Hour, )), ), expectFilter(), ), }, args{ context.Background(), "userID", "orgID", []string{"tokenID", "tokenID2"}, }, res{ err: caos_errs.IsNotFound, }, }, { "push failed, error", fields{ eventstore: eventstoreExpect(t, expectFilter( eventFromEventPusher(user.NewHumanRefreshTokenAddedEvent( context.Background(), &user.NewAggregate("userID", "orgID").Aggregate, "tokenID", "clientID", "agentID", "de", []string{"clientID"}, []string{oidc.ScopeOpenID, oidc.ScopeProfile, oidc.ScopeEmail, oidc.ScopeOfflineAccess}, []string{"password"}, time.Now(), 1*time.Hour, 10*time.Hour, )), ), expectFilter( eventFromEventPusher(user.NewHumanRefreshTokenAddedEvent( context.Background(), &user.NewAggregate("userID", "orgID").Aggregate, "tokenID2", "clientID2", "agentID", "de", []string{"clientID2"}, []string{oidc.ScopeOpenID, oidc.ScopeProfile, oidc.ScopeEmail, oidc.ScopeOfflineAccess}, []string{"password"}, time.Now(), 1*time.Hour, 10*time.Hour, )), ), expectPushFailed(caos_errs.ThrowInternal(nil, "ERROR", "internal"), []*repository.Event{ eventFromEventPusher(user.NewHumanRefreshTokenRemovedEvent( context.Background(), &user.NewAggregate("userID", "orgID").Aggregate, "tokenID", )), eventFromEventPusher(user.NewHumanRefreshTokenRemovedEvent( context.Background(), &user.NewAggregate("userID", "orgID").Aggregate, "tokenID2", )), }, ), ), }, args{ context.Background(), "userID", "orgID", []string{"tokenID", "tokenID2"}, }, res{ err: caos_errs.IsInternal, }, }, { "revoke, ok", fields{ eventstore: eventstoreExpect(t, expectFilter( eventFromEventPusher(user.NewHumanRefreshTokenAddedEvent( context.Background(), &user.NewAggregate("userID", "orgID").Aggregate, "tokenID", "clientID", "agentID", "de", []string{"clientID1"}, []string{oidc.ScopeOpenID, oidc.ScopeProfile, oidc.ScopeEmail, oidc.ScopeOfflineAccess}, []string{"password"}, time.Now(), 1*time.Hour, 10*time.Hour, )), ), expectFilter( eventFromEventPusher(user.NewHumanRefreshTokenAddedEvent( context.Background(), &user.NewAggregate("userID", "orgID").Aggregate, "tokenID2", "clientID2", "agentID", "de", []string{"clientID2"}, []string{oidc.ScopeOpenID, oidc.ScopeProfile, oidc.ScopeEmail, oidc.ScopeOfflineAccess}, []string{"password"}, time.Now(), 1*time.Hour, 10*time.Hour, )), ), expectPush( []*repository.Event{ eventFromEventPusher(user.NewHumanRefreshTokenRemovedEvent( context.Background(), &user.NewAggregate("userID", "orgID").Aggregate, "tokenID", )), eventFromEventPusher(user.NewHumanRefreshTokenRemovedEvent( context.Background(), &user.NewAggregate("userID", "orgID").Aggregate, "tokenID2", )), }, ), ), }, args{ context.Background(), "userID", "orgID", []string{"tokenID", "tokenID2"}, }, res{ err: nil, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { c := &Commands{ eventstore: tt.fields.eventstore, } err := c.RevokeRefreshTokens(tt.args.ctx, tt.args.userID, tt.args.orgID, tt.args.tokenIDs) if tt.res.err == nil { assert.NoError(t, err) } if tt.res.err != nil && !tt.res.err(err) { t.Errorf("got wrong err: %v ", err) } }) } } func refreshTokenEncryptionAlgorithm(ctrl *gomock.Controller) crypto.EncryptionAlgorithm { mCrypto := crypto.NewMockEncryptionAlgorithm(ctrl) mCrypto.EXPECT().Algorithm().AnyTimes().Return("enc") mCrypto.EXPECT().EncryptionKeyID().AnyTimes().Return("id") mCrypto.EXPECT().Encrypt(gomock.Any()).AnyTimes().DoAndReturn( func(refrehToken []byte) ([]byte, error) { return refrehToken, nil }, ) mCrypto.EXPECT().Decrypt(gomock.Any(), gomock.Any()).AnyTimes().DoAndReturn( func(refrehToken []byte, keyID string) ([]byte, error) { if keyID != "id" { return nil, caos_errs.ThrowInternal(nil, "id", "invalid key id") } return refrehToken, nil }, ) return mCrypto } func TestCommands_addRefreshToken(t *testing.T) { authTime := time.Now().Add(-1 * time.Hour) type fields struct { eventstore *eventstore.Eventstore keyAlgorithm crypto.EncryptionAlgorithm } type args struct { ctx context.Context accessToken *domain.Token authMethodsReferences []string authTime time.Time idleExpiration time.Duration expiration time.Duration } type res struct { event *user.HumanRefreshTokenAddedEvent refreshToken string err func(error) bool } tests := []struct { name string fields fields args args res res }{ { name: "add refresh Token", fields: fields{ eventstore: eventstoreExpect(t), keyAlgorithm: refreshTokenEncryptionAlgorithm(gomock.NewController(t)), }, args: args{ ctx: context.Background(), accessToken: &domain.Token{ ObjectRoot: models.ObjectRoot{ AggregateID: "userID", ResourceOwner: "org1", }, TokenID: "accessTokenID1", ApplicationID: "clientID", UserAgentID: "agentID", RefreshTokenID: "refreshTokenID", Audience: []string{"clientID1"}, Expiration: time.Now().Add(5 * time.Minute), Scopes: []string{oidc.ScopeOpenID, oidc.ScopeProfile, oidc.ScopeEmail, oidc.ScopeOfflineAccess}, PreferredLanguage: "de", }, authMethodsReferences: []string{"password"}, authTime: authTime, idleExpiration: 1 * time.Hour, expiration: 10 * time.Hour, }, res: res{ event: user.NewHumanRefreshTokenAddedEvent( context.Background(), &user.NewAggregate("userID", "org1").Aggregate, "refreshTokenID", "clientID", "agentID", "de", []string{"clientID1"}, []string{oidc.ScopeOpenID, oidc.ScopeProfile, oidc.ScopeEmail, oidc.ScopeOfflineAccess}, []string{"password"}, authTime, 1*time.Hour, 10*time.Hour, ), refreshToken: base64.RawURLEncoding.EncodeToString([]byte("userID:refreshTokenID:refreshTokenID")), }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { c := &Commands{ eventstore: tt.fields.eventstore, keyAlgorithm: tt.fields.keyAlgorithm, } gotEvent, gotRefreshToken, err := c.addRefreshToken(tt.args.ctx, tt.args.accessToken, tt.args.authMethodsReferences, tt.args.authTime, tt.args.idleExpiration, tt.args.expiration) if tt.res.err == nil { assert.NoError(t, err) } if tt.res.err != nil && !tt.res.err(err) { t.Errorf("got wrong err: %v ", err) } if tt.res.err == nil { assert.Equal(t, tt.res.event, gotEvent) assert.Equal(t, tt.res.refreshToken, gotRefreshToken) } }) } } func TestCommands_renewRefreshToken(t *testing.T) { type fields struct { eventstore *eventstore.Eventstore idGenerator id.Generator keyAlgorithm crypto.EncryptionAlgorithm } type args struct { ctx context.Context userID string orgID string refreshToken string idleExpiration time.Duration } type res struct { event *user.HumanRefreshTokenRenewedEvent refreshTokenID string newRefreshToken string err func(error) bool } tests := []struct { name string fields fields args args res res }{ { name: "empty token, error", fields: fields{ eventstore: eventstoreExpect(t), }, args: args{ ctx: context.Background(), }, res: res{ err: caos_errs.IsErrorInvalidArgument, }, }, { name: "invalid token, error", fields: fields{ eventstore: eventstoreExpect(t), keyAlgorithm: refreshTokenEncryptionAlgorithm(gomock.NewController(t)), }, args: args{ ctx: context.Background(), refreshToken: "invalid", }, res: res{ err: caos_errs.IsErrorInvalidArgument, }, }, { name: "invalid token (invalid userID), error", fields: fields{ eventstore: eventstoreExpect(t), keyAlgorithm: refreshTokenEncryptionAlgorithm(gomock.NewController(t)), }, args: args{ ctx: context.Background(), userID: "userID", orgID: "orgID", refreshToken: base64.RawURLEncoding.EncodeToString([]byte("userID2:tokenID:token")), }, res: res{ err: caos_errs.IsErrorInvalidArgument, }, }, { name: "token inactive, error", fields: fields{ eventstore: eventstoreExpect(t, expectFilter( eventFromEventPusher(user.NewHumanRefreshTokenAddedEvent( context.Background(), &user.NewAggregate("userID", "orgID").Aggregate, "tokenID", "applicationID", "userAgentID", "de", []string{"clientID1"}, []string{oidc.ScopeOpenID, oidc.ScopeProfile, oidc.ScopeEmail, oidc.ScopeOfflineAccess}, []string{"password"}, time.Now(), 1*time.Hour, 24*time.Hour, )), eventFromEventPusher(user.NewHumanRefreshTokenRemovedEvent( context.Background(), &user.NewAggregate("userID", "orgID").Aggregate, "tokenID", )), ), ), keyAlgorithm: refreshTokenEncryptionAlgorithm(gomock.NewController(t)), }, args: args{ ctx: context.Background(), userID: "userID", orgID: "orgID", refreshToken: base64.RawURLEncoding.EncodeToString([]byte("userID:tokenID:token")), }, res: res{ err: caos_errs.IsErrorInvalidArgument, }, }, { name: "token expired, error", fields: fields{ eventstore: eventstoreExpect(t, expectFilter( eventFromEventPusher(user.NewHumanRefreshTokenAddedEvent( context.Background(), &user.NewAggregate("userID", "orgID").Aggregate, "tokenID", "applicationID", "userAgentID", "de", []string{"clientID1"}, []string{oidc.ScopeOpenID, oidc.ScopeProfile, oidc.ScopeEmail, oidc.ScopeOfflineAccess}, []string{"password"}, time.Now(), 1*time.Hour, 24*time.Hour, )), ), ), keyAlgorithm: refreshTokenEncryptionAlgorithm(gomock.NewController(t)), }, args: args{ ctx: context.Background(), userID: "userID", orgID: "orgID", refreshToken: base64.RawURLEncoding.EncodeToString([]byte("userID:tokenID:tokenID")), }, res: res{ err: caos_errs.IsErrorInvalidArgument, }, }, { name: "user deactivated, error", fields: fields{ eventstore: eventstoreExpect(t, expectFilter( eventFromEventPusherWithCreationDateNow(user.NewHumanRefreshTokenAddedEvent( context.Background(), &user.NewAggregate("userID", "orgID").Aggregate, "tokenID", "applicationID", "userAgentID", "de", []string{"clientID1"}, []string{oidc.ScopeOpenID, oidc.ScopeProfile, oidc.ScopeEmail, oidc.ScopeOfflineAccess}, []string{"password"}, time.Now(), 1*time.Hour, 24*time.Hour, )), eventFromEventPusher( user.NewUserDeactivatedEvent( context.Background(), &user.NewAggregate("userID", "orgID").Aggregate, ), ), ), ), keyAlgorithm: refreshTokenEncryptionAlgorithm(gomock.NewController(t)), }, args: args{ ctx: context.Background(), userID: "userID", orgID: "orgID", refreshToken: base64.RawURLEncoding.EncodeToString([]byte("userID:tokenID:tokenID")), idleExpiration: 1 * time.Hour, }, res: res{ err: caos_errs.IsErrorInvalidArgument, }, }, { name: "user signedout, error", fields: fields{ eventstore: eventstoreExpect(t, expectFilter( eventFromEventPusherWithCreationDateNow(user.NewHumanRefreshTokenAddedEvent( context.Background(), &user.NewAggregate("userID", "orgID").Aggregate, "tokenID", "applicationID", "userAgentID", "de", []string{"clientID1"}, []string{oidc.ScopeOpenID, oidc.ScopeProfile, oidc.ScopeEmail, oidc.ScopeOfflineAccess}, []string{"password"}, time.Now(), 1*time.Hour, 24*time.Hour, )), eventFromEventPusher( user.NewHumanSignedOutEvent( context.Background(), &user.NewAggregate("userID", "orgID").Aggregate, "userAgentID", ), ), ), ), keyAlgorithm: refreshTokenEncryptionAlgorithm(gomock.NewController(t)), }, args: args{ ctx: context.Background(), userID: "userID", orgID: "orgID", refreshToken: base64.RawURLEncoding.EncodeToString([]byte("userID:tokenID:tokenID")), idleExpiration: 1 * time.Hour, }, res: res{ err: caos_errs.IsErrorInvalidArgument, }, }, { name: "token renewed, ok", fields: fields{ eventstore: eventstoreExpect(t, expectFilter( eventFromEventPusherWithCreationDateNow(user.NewHumanRefreshTokenAddedEvent( context.Background(), &user.NewAggregate("userID", "orgID").Aggregate, "tokenID", "applicationID", "userAgentID", "de", []string{"clientID1"}, []string{oidc.ScopeOpenID, oidc.ScopeProfile, oidc.ScopeEmail, oidc.ScopeOfflineAccess}, []string{"password"}, time.Now(), 1*time.Hour, 24*time.Hour, )), ), ), keyAlgorithm: refreshTokenEncryptionAlgorithm(gomock.NewController(t)), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "refreshToken1"), }, args: args{ ctx: context.Background(), userID: "userID", orgID: "orgID", refreshToken: base64.RawURLEncoding.EncodeToString([]byte("userID:tokenID:tokenID")), idleExpiration: 1 * time.Hour, }, res: res{ event: user.NewHumanRefreshTokenRenewedEvent( context.Background(), &user.NewAggregate("userID", "orgID").Aggregate, "tokenID", "refreshToken1", 1*time.Hour, ), refreshTokenID: "tokenID", newRefreshToken: base64.RawURLEncoding.EncodeToString([]byte("userID:tokenID:refreshToken1")), }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { c := &Commands{ eventstore: tt.fields.eventstore, idGenerator: tt.fields.idGenerator, keyAlgorithm: tt.fields.keyAlgorithm, } gotEvent, gotRefreshTokenID, gotNewRefreshToken, err := c.renewRefreshToken(tt.args.ctx, tt.args.userID, tt.args.orgID, tt.args.refreshToken, tt.args.idleExpiration) if tt.res.err == nil { assert.NoError(t, err) } if tt.res.err != nil && !tt.res.err(err) { t.Errorf("got wrong err: %v ", err) } if tt.res.err == nil { assert.Equal(t, tt.res.event, gotEvent) assert.Equal(t, tt.res.refreshTokenID, gotRefreshTokenID) assert.Equal(t, tt.res.newRefreshToken, gotNewRefreshToken) } }) } }