diff --git a/pkg/coredata/session.go b/pkg/coredata/session.go index 2c80cf475..9f44a051f 100644 --- a/pkg/coredata/session.go +++ b/pkg/coredata/session.go @@ -343,6 +343,30 @@ WHERE return result.RowsAffected(), nil } +func (s *Sessions) ExpireAllForIdentity(ctx context.Context, conn pg.Querier, identityID gid.GID) (int64, error) { + q := ` +UPDATE iam_sessions +SET + expired_at = NOW(), + updated_at = NOW(), + expire_reason = 'revoked' +WHERE + identity_id = @identity_id + AND expire_reason IS NULL +` + + args := pgx.StrictNamedArgs{ + "identity_id": identityID, + } + + result, err := conn.Exec(ctx, q, args) + if err != nil { + return 0, fmt.Errorf("cannot query sessions: %w", err) + } + + return result.RowsAffected(), nil +} + func (s *Session) LoadByRootSessionIDAndMembershipID( ctx context.Context, conn pg.Querier, diff --git a/pkg/iam/account_service.go b/pkg/iam/account_service.go index 96387c38c..5aefe8868 100644 --- a/pkg/iam/account_service.go +++ b/pkg/iam/account_service.go @@ -235,7 +235,7 @@ func (s *AccountService) ListPendingInvitations( return page.NewPage(invitations, cursor), nil } -func (s AccountService) ChangePassword(ctx context.Context, identityID gid.GID, req *ChangePasswordRequest) error { +func (s AccountService) ChangePassword(ctx context.Context, identityID gid.GID, currentSessionID gid.GID, req *ChangePasswordRequest) error { if err := req.Validate(); err != nil { return fmt.Errorf("invalid request: %w", err) } @@ -275,6 +275,11 @@ func (s AccountService) ChangePassword(ctx context.Context, identityID gid.GID, return fmt.Errorf("cannot update identity: %w", err) } + sessions := coredata.Sessions{} + if _, err := sessions.ExpireAllForIdentityExceptOneSession(ctx, tx, identity.ID, currentSessionID); err != nil { + return fmt.Errorf("cannot expire other sessions: %w", err) + } + // TODO: email to notify identity that their password has been changed return nil diff --git a/pkg/iam/auth_service.go b/pkg/iam/auth_service.go index d59d74c2f..91b3f8bb9 100644 --- a/pkg/iam/auth_service.go +++ b/pkg/iam/auth_service.go @@ -279,6 +279,11 @@ func (s AuthService) ResetPassword( return fmt.Errorf("cannot update identity: %w", err) } + sessions := coredata.Sessions{} + if _, err := sessions.ExpireAllForIdentity(ctx, tx, identity.ID); err != nil { + return fmt.Errorf("cannot expire sessions: %w", err) + } + return nil }, ) diff --git a/pkg/server/api/connect/v1/session_resolvers.go b/pkg/server/api/connect/v1/session_resolvers.go index 7f5b05a79..ac475037e 100644 --- a/pkg/server/api/connect/v1/session_resolvers.go +++ b/pkg/server/api/connect/v1/session_resolvers.go @@ -318,10 +318,12 @@ func (r *mutationResolver) VerifyEmail(ctx context.Context, input types.VerifyEm // ChangePassword is the resolver for the changePassword field. func (r *mutationResolver) ChangePassword(ctx context.Context, input types.ChangePasswordInput) (*types.ChangePasswordPayload, error) { identity := authn.IdentityFromContext(ctx) + session := authn.SessionFromContext(ctx) err := r.iam.AccountService.ChangePassword( ctx, identity.ID, + session.ID, &iam.ChangePasswordRequest{ CurrentPassword: input.CurrentPassword, NewPassword: input.NewPassword,