(t *testing.T, ctx context.Context, s *TwoFactorsStore)
| 131 | } |
| 132 | |
| 133 | func twoFactorsUseRecoveryCode(t *testing.T, ctx context.Context, s *TwoFactorsStore) { |
| 134 | // Create 2FA tokens for two users |
| 135 | err := s.Create(ctx, 1, "secure-key", "secure-secret") |
| 136 | require.NoError(t, err) |
| 137 | err = s.Create(ctx, 2, "secure-key", "secure-secret") |
| 138 | require.NoError(t, err) |
| 139 | |
| 140 | // Get recovery codes for both users |
| 141 | var user1Codes []TwoFactorRecoveryCode |
| 142 | err = s.db.Where("user_id = ?", 1).Find(&user1Codes).Error |
| 143 | require.NoError(t, err) |
| 144 | require.NotEmpty(t, user1Codes) |
| 145 | |
| 146 | var user2Codes []TwoFactorRecoveryCode |
| 147 | err = s.db.Where("user_id = ?", 2).Find(&user2Codes).Error |
| 148 | require.NoError(t, err) |
| 149 | require.NotEmpty(t, user2Codes) |
| 150 | |
| 151 | // User 1 should be able to use their own recovery code |
| 152 | err = s.UseRecoveryCode(ctx, 1, user1Codes[0].Code) |
| 153 | require.NoError(t, err) |
| 154 | |
| 155 | // Verify the code is now marked as used |
| 156 | var usedCode TwoFactorRecoveryCode |
| 157 | err = s.db.Where("id = ?", user1Codes[0].ID).First(&usedCode).Error |
| 158 | require.NoError(t, err) |
| 159 | assert.True(t, usedCode.IsUsed) |
| 160 | |
| 161 | // User 1 should NOT be able to use user 2's recovery code |
| 162 | // This is the key security test - recovery codes must be scoped by user |
| 163 | err = s.UseRecoveryCode(ctx, 1, user2Codes[0].Code) |
| 164 | assert.True(t, IsTwoFactorRecoveryCodeNotFound(err), "expected recovery code not found error when using another user's code") |
| 165 | |
| 166 | // User 2's code should still be unused |
| 167 | var user2Code TwoFactorRecoveryCode |
| 168 | err = s.db.Where("id = ?", user2Codes[0].ID).First(&user2Code).Error |
| 169 | require.NoError(t, err) |
| 170 | assert.False(t, user2Code.IsUsed, "user 2's recovery code should not be marked as used") |
| 171 | |
| 172 | // User 2 should be able to use their own code |
| 173 | err = s.UseRecoveryCode(ctx, 2, user2Codes[0].Code) |
| 174 | require.NoError(t, err) |
| 175 | |
| 176 | // Using an already-used code should fail |
| 177 | err = s.UseRecoveryCode(ctx, 1, user1Codes[0].Code) |
| 178 | assert.True(t, IsTwoFactorRecoveryCodeNotFound(err), "expected error when reusing a recovery code") |
| 179 | |
| 180 | // Using a non-existent code should fail |
| 181 | err = s.UseRecoveryCode(ctx, 1, "invalid-code") |
| 182 | assert.True(t, IsTwoFactorRecoveryCodeNotFound(err), "expected error for invalid recovery code") |
| 183 | } |
nothing calls this directly
no test coverage detected