diff --git a/NuGet.config b/NuGet.config
index 653bce8caccf..80454d301e83 100644
--- a/NuGet.config
+++ b/NuGet.config
@@ -4,12 +4,10 @@
-
-
-
+
-
+
@@ -28,12 +26,10 @@
-
-
-
+
-
+
diff --git a/eng/Baseline.Designer.props b/eng/Baseline.Designer.props
index 979475fa9fef..543b6cca8d1f 100644
--- a/eng/Baseline.Designer.props
+++ b/eng/Baseline.Designer.props
@@ -2,28 +2,28 @@
$(MSBuildAllProjects);$(MSBuildThisFileFullPath)
- 7.0.5
+ 7.0.7
- 7.0.5
+ 7.0.7
- 7.0.5
+ 7.0.7
- 7.0.5
+ 7.0.7
- 7.0.5
+ 7.0.7
-
-
-
+
+
+
@@ -35,105 +35,105 @@
- 7.0.5
+ 7.0.7
- 7.0.5
+ 7.0.7
- 7.0.5
+ 7.0.7
- 7.0.5
+ 7.0.7
- 7.0.5
+ 7.0.7
- 7.0.5
+ 7.0.7
- 7.0.5
+ 7.0.7
- 7.0.5
+ 7.0.7
- 7.0.5
+ 7.0.7
- 7.0.5
+ 7.0.7
- 7.0.5
+ 7.0.7
- 7.0.5
+ 7.0.7
- 7.0.5
+ 7.0.7
- 7.0.5
+ 7.0.7
- 7.0.5
+ 7.0.7
- 7.0.5
+ 7.0.7
- 7.0.5
+ 7.0.7
- 7.0.5
+ 7.0.7
- 7.0.5
+ 7.0.7
-
+
-
+
- 7.0.5
+ 7.0.7
- 7.0.5
+ 7.0.7
- 7.0.5
+ 7.0.7
@@ -141,121 +141,121 @@
- 7.0.5
+ 7.0.7
-
-
+
+
-
-
+
+
-
-
+
+
- 7.0.5
+ 7.0.7
-
+
- 7.0.5
+ 7.0.7
- 7.0.5
+ 7.0.7
-
+
- 7.0.5
+ 7.0.7
-
-
+
+
- 7.0.5
+ 7.0.7
- 7.0.5
+ 7.0.7
-
-
+
+
- 7.0.5
+ 7.0.7
-
+
- 7.0.5
+ 7.0.7
-
+
- 7.0.5
+ 7.0.7
-
-
-
+
+
+
- 7.0.5
+ 7.0.7
-
-
+
+
- 7.0.5
+ 7.0.7
-
-
+
+
- 7.0.5
+ 7.0.7
- 7.0.5
+ 7.0.7
- 7.0.5
+ 7.0.7
-
-
+
+
@@ -263,82 +263,82 @@
- 7.0.5
+ 7.0.7
-
+
- 7.0.5
+ 7.0.7
-
+
-
+
-
+
-
+
- 7.0.5
+ 7.0.7
- 7.0.5
+ 7.0.7
-
+
-
+
-
+
- 7.0.5
+ 7.0.7
-
-
+
+
-
+
-
-
+
+
-
+
-
-
+
+
-
+
@@ -346,58 +346,58 @@
- 7.0.5
+ 7.0.7
- 7.0.5
+ 7.0.7
-
-
+
+
- 7.0.5
+ 7.0.7
-
+
-
+
-
+
- 7.0.5
+ 7.0.7
-
+
-
+
-
+
- 7.0.5
+ 7.0.7
-
+
- 7.0.5
+ 7.0.7
@@ -414,7 +414,7 @@
- 7.0.5
+ 7.0.7
@@ -422,71 +422,71 @@
- 7.0.5
+ 7.0.7
-
+
- 7.0.5
+ 7.0.7
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
- 7.0.5
+ 7.0.7
-
-
+
+
-
+
-
-
+
+
- 7.0.5
+ 7.0.7
-
-
+
+
- 7.0.5
+ 7.0.7
-
-
+
+
- 7.0.5
+ 7.0.7
@@ -502,27 +502,27 @@
- 7.0.5
+ 7.0.7
- 7.0.5
+ 7.0.7
- 7.0.5
+ 7.0.7
-
+
- 7.0.5
+ 7.0.7
@@ -531,151 +531,151 @@
- 7.0.5
+ 7.0.7
-
+
- 7.0.5
+ 7.0.7
- 7.0.5
+ 7.0.7
- 7.0.5
+ 7.0.7
-
-
+
+
-
-
+
+
-
-
+
+
- 7.0.5
+ 7.0.7
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
- 7.0.5
+ 7.0.7
-
+
-
+
-
+
-
+
-
+
- 7.0.5
+ 7.0.7
-
+
-
+
-
+
- 7.0.5
+ 7.0.7
-
+
-
+
-
+
- 7.0.5
+ 7.0.7
-
+
-
+
-
+
- 7.0.5
+ 7.0.7
-
-
-
-
+
+
+
+
- 7.0.5
+ 7.0.7
@@ -684,60 +684,60 @@
- 7.0.5
+ 7.0.7
- 7.0.5
+ 7.0.7
- 7.0.5
+ 7.0.7
- 7.0.5
+ 7.0.7
-
+
- 7.0.5
+ 7.0.7
- 7.0.5
+ 7.0.7
- 7.0.5
+ 7.0.7
- 7.0.5
+ 7.0.7
- 7.0.5
+ 7.0.7
- 7.0.5
+ 7.0.7
- 7.0.5
+ 7.0.7
- 7.0.5
+ 7.0.7
@@ -756,29 +756,29 @@
- 7.0.5
+ 7.0.7
-
+
-
+
-
+
- 7.0.5
+ 7.0.7
@@ -794,46 +794,46 @@
- 7.0.5
+ 7.0.7
-
+
-
+
-
+
-
+
-
+
-
+
- 7.0.5
+ 7.0.7
- 7.0.5
+ 7.0.7
-
-
-
+
+
+
- 7.0.5
+ 7.0.7
- 7.0.5
+ 7.0.7
@@ -843,7 +843,7 @@
- 7.0.5
+ 7.0.7
@@ -852,79 +852,79 @@
- 7.0.5
+ 7.0.7
-
+
-
+
-
+
- 7.0.5
+ 7.0.7
-
+
-
+
-
+
- 7.0.5
+ 7.0.7
-
+
-
+
-
+
-
+
-
+
-
+
- 7.0.5
+ 7.0.7
- 7.0.5
+ 7.0.7
-
+
@@ -933,7 +933,7 @@
-
+
@@ -941,17 +941,17 @@
-
+
- 7.0.5
+ 7.0.7
- 7.0.5
+ 7.0.7
@@ -969,13 +969,13 @@
- 7.0.5
+ 7.0.7
- 7.0.5
+ 7.0.7
-
+
\ No newline at end of file
diff --git a/eng/Baseline.xml b/eng/Baseline.xml
index d69619e2d461..b428ac509c8f 100644
--- a/eng/Baseline.xml
+++ b/eng/Baseline.xml
@@ -4,109 +4,109 @@ This file contains a list of all the packages and their versions which were rele
Update this list when preparing for a new patch.
-->
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml
index fe08af6e15bd..08c476cea384 100644
--- a/eng/Version.Details.xml
+++ b/eng/Version.Details.xml
@@ -9,37 +9,37 @@
-->
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-efcore
- 81a09d5f0ca45f744ec8a4b8938f38780bb15a4c
+ e00be013488a9a56a0e29230be0fe10026dbb209
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-efcore
- 81a09d5f0ca45f744ec8a4b8938f38780bb15a4c
+ e00be013488a9a56a0e29230be0fe10026dbb209
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-efcore
- 81a09d5f0ca45f744ec8a4b8938f38780bb15a4c
+ e00be013488a9a56a0e29230be0fe10026dbb209
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-efcore
- 81a09d5f0ca45f744ec8a4b8938f38780bb15a4c
+ e00be013488a9a56a0e29230be0fe10026dbb209
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-efcore
- 81a09d5f0ca45f744ec8a4b8938f38780bb15a4c
+ e00be013488a9a56a0e29230be0fe10026dbb209
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-efcore
- 81a09d5f0ca45f744ec8a4b8938f38780bb15a4c
+ e00be013488a9a56a0e29230be0fe10026dbb209
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-efcore
- 81a09d5f0ca45f744ec8a4b8938f38780bb15a4c
+ e00be013488a9a56a0e29230be0fe10026dbb209
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-efcore
- 81a09d5f0ca45f744ec8a4b8938f38780bb15a4c
+ e00be013488a9a56a0e29230be0fe10026dbb209
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
@@ -177,9 +177,9 @@
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
d099f075e45d2aa6007a22b71b45a08758559f80
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- 5b20af47d99620150c53eaf5db8636fdf730b126
+ 8e9a17b2216f51a5788f8b1c467a4cf3b769e7d7
https://github.com/dotnet/source-build-externals
@@ -262,33 +262,33 @@
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
d099f075e45d2aa6007a22b71b45a08758559f80
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- 5b20af47d99620150c53eaf5db8636fdf730b126
+ 8e9a17b2216f51a5788f8b1c467a4cf3b769e7d7
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- 5b20af47d99620150c53eaf5db8636fdf730b126
+ 8e9a17b2216f51a5788f8b1c467a4cf3b769e7d7
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- 5b20af47d99620150c53eaf5db8636fdf730b126
+ 8e9a17b2216f51a5788f8b1c467a4cf3b769e7d7
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- 5b20af47d99620150c53eaf5db8636fdf730b126
+ 8e9a17b2216f51a5788f8b1c467a4cf3b769e7d7
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- 5b20af47d99620150c53eaf5db8636fdf730b126
+ 8e9a17b2216f51a5788f8b1c467a4cf3b769e7d7
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- 5b20af47d99620150c53eaf5db8636fdf730b126
+ 8e9a17b2216f51a5788f8b1c467a4cf3b769e7d7
https://github.com/dotnet/xdt
@@ -298,26 +298,26 @@
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- 5b20af47d99620150c53eaf5db8636fdf730b126
+ 8e9a17b2216f51a5788f8b1c467a4cf3b769e7d7
-
+
https://github.com/dotnet/arcade
- 7c5e5a782c67460b123c8e41d484ebcca8002c93
+ 59ac824080b9807fd91dbc8a6d2b4447e74874ab
-
+
https://github.com/dotnet/arcade
- 7c5e5a782c67460b123c8e41d484ebcca8002c93
+ 59ac824080b9807fd91dbc8a6d2b4447e74874ab
-
+
https://github.com/dotnet/arcade
- 7c5e5a782c67460b123c8e41d484ebcca8002c93
+ 59ac824080b9807fd91dbc8a6d2b4447e74874ab
-
+
https://github.com/dotnet/arcade
- 7c5e5a782c67460b123c8e41d484ebcca8002c93
+ 59ac824080b9807fd91dbc8a6d2b4447e74874ab
diff --git a/eng/Versions.props b/eng/Versions.props
index f1c326621af3..2f5daccfadee 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -8,7 +8,7 @@
7
0
- 7
+ 9
false
7.0.0
- 7.0.7
- 7.0.7
- 7.0.7
- 7.0.7
- 7.0.7
- 7.0.7-servicing.23274.4
+ 7.0.9
+ 7.0.9
+ 7.0.9
+ 7.0.9
+ 7.0.9
+ 7.0.9-servicing.23320.18
7.0.0
7.0.0
7.0.0
@@ -103,7 +103,7 @@
7.0.0
7.0.1
7.0.0
- 7.0.7-servicing.23274.4
+ 7.0.9-servicing.23320.18
7.0.0
7.0.2
7.0.0
@@ -123,19 +123,19 @@
7.0.0
7.0.0
- 7.0.3
+ 7.0.4
- 7.0.7
- 7.0.7
- 7.0.7
- 7.0.7
- 7.0.7
- 7.0.7
- 7.0.7
- 7.0.7
+ 7.0.9
+ 7.0.9
+ 7.0.9
+ 7.0.9
+ 7.0.9
+ 7.0.9
+ 7.0.9
+ 7.0.9
- 7.0.0-beta.23211.2
- 7.0.0-beta.23211.2
+ 7.0.0-beta.23313.4
+ 7.0.0-beta.23313.4
7.0.0-alpha.1.22505.1
@@ -223,9 +223,9 @@
3.0.1
11.1.0
6.21.0
- 6.2.0
- 6.2.0
- 6.2.0
+ 6.2.4
+ 6.2.4
+ 6.2.4
5.0.0
5.0.0-alpha.20560.6
5.0.0
@@ -238,7 +238,7 @@
5.0.17-servicing-22215-7
$(MicrosoftAspNetCoreAzureAppServicesSiteExtension50Version)
$(MicrosoftAspNetCoreAzureAppServicesSiteExtension50Version)
- 6.0.16-servicing-23174-6
+ 6.0.18-servicing-23269-9
$(MicrosoftAspNetCoreAzureAppServicesSiteExtension60Version)
$(MicrosoftAspNetCoreAzureAppServicesSiteExtension60Version)
diff --git a/eng/common/templates/job/job.yml b/eng/common/templates/job/job.yml
index e3ba9398016b..ef337eac55ec 100644
--- a/eng/common/templates/job/job.yml
+++ b/eng/common/templates/job/job.yml
@@ -24,7 +24,7 @@ parameters:
enablePublishBuildAssets: false
enablePublishTestResults: false
enablePublishUsingPipelines: false
- disableComponentGovernance: false
+ disableComponentGovernance: ''
mergeTestResults: false
testRunTitle: ''
testResultsFormat: ''
@@ -73,6 +73,10 @@ jobs:
- ${{ if eq(parameters.enableRichCodeNavigation, 'true') }}:
- name: EnableRichCodeNavigation
value: 'true'
+ # Retry signature validation up to three times, waiting 2 seconds between attempts.
+ # See https://learn.microsoft.com/en-us/nuget/reference/errors-and-warnings/nu3028#retry-untrusted-root-failures
+ - name: NUGET_EXPERIMENTAL_CHAIN_BUILD_RETRY_POLICY
+ value: 3,2000
- ${{ each variable in parameters.variables }}:
# handle name-value variable syntax
# example:
@@ -81,7 +85,7 @@ jobs:
- ${{ if ne(variable.name, '') }}:
- name: ${{ variable.name }}
value: ${{ variable.value }}
-
+
# handle variable groups
- ${{ if ne(variable.group, '') }}:
- group: ${{ variable.group }}
@@ -142,14 +146,20 @@ jobs:
richNavLogOutputDirectory: $(Build.SourcesDirectory)/artifacts/bin
continueOnError: true
- - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest'), ne(parameters.disableComponentGovernance, 'true')) }}:
- - task: ComponentGovernanceComponentDetection@0
- continueOnError: true
+ - template: /eng/common/templates/steps/component-governance.yml
+ parameters:
+ ${{ if eq(parameters.disableComponentGovernance, '') }}:
+ ${{ if and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest'), eq(parameters.runAsPublic, 'false'), or(startsWith(variables['Build.SourceBranch'], 'refs/heads/release/'), startsWith(variables['Build.SourceBranch'], 'refs/heads/dotnet/'), startsWith(variables['Build.SourceBranch'], 'refs/heads/microsoft/'), eq(variables['Build.SourceBranch'], 'refs/heads/main'))) }}:
+ disableComponentGovernance: false
+ ${{ else }}:
+ disableComponentGovernance: true
+ ${{ else }}:
+ disableComponentGovernance: ${{ parameters.disableComponentGovernance }}
- ${{ if eq(parameters.enableMicrobuild, 'true') }}:
- ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}:
- task: MicroBuildCleanup@1
- displayName: Execute Microbuild cleanup tasks
+ displayName: Execute Microbuild cleanup tasks
condition: and(always(), in(variables['_SignType'], 'real', 'test'), eq(variables['Agent.Os'], 'Windows_NT'))
continueOnError: ${{ parameters.continueOnError }}
env:
@@ -217,7 +227,7 @@ jobs:
displayName: Publish XUnit Test Results
inputs:
testResultsFormat: 'xUnit'
- testResultsFiles: '*.xml'
+ testResultsFiles: '*.xml'
searchFolder: '$(Build.SourcesDirectory)/artifacts/TestResults/$(_BuildConfig)'
testRunTitle: ${{ coalesce(parameters.testRunTitle, parameters.name, '$(System.JobName)') }}-xunit
mergeTestResults: ${{ parameters.mergeTestResults }}
@@ -228,7 +238,7 @@ jobs:
displayName: Publish TRX Test Results
inputs:
testResultsFormat: 'VSTest'
- testResultsFiles: '*.trx'
+ testResultsFiles: '*.trx'
searchFolder: '$(Build.SourcesDirectory)/artifacts/TestResults/$(_BuildConfig)'
testRunTitle: ${{ coalesce(parameters.testRunTitle, parameters.name, '$(System.JobName)') }}-trx
mergeTestResults: ${{ parameters.mergeTestResults }}
diff --git a/eng/common/templates/steps/component-governance.yml b/eng/common/templates/steps/component-governance.yml
new file mode 100644
index 000000000000..babc2757d8d1
--- /dev/null
+++ b/eng/common/templates/steps/component-governance.yml
@@ -0,0 +1,10 @@
+parameters:
+ disableComponentGovernance: false
+
+steps:
+- ${{ if eq(parameters.disableComponentGovernance, 'true') }}:
+ - script: "echo ##vso[task.setvariable variable=skipComponentGovernanceDetection]true"
+ displayName: Set skipComponentGovernanceDetection variable
+- ${{ if ne(parameters.disableComponentGovernance, 'true') }}:
+ - task: ComponentGovernanceComponentDetection@0
+ continueOnError: true
\ No newline at end of file
diff --git a/global.json b/global.json
index 9962c52e84f1..afc8fc984bd9 100644
--- a/global.json
+++ b/global.json
@@ -1,9 +1,9 @@
{
"sdk": {
- "version": "7.0.105"
+ "version": "7.0.107"
},
"tools": {
- "dotnet": "7.0.105",
+ "dotnet": "7.0.107",
"runtimes": {
"dotnet/x86": [
"$(MicrosoftNETCoreBrowserDebugHostTransportVersion)"
@@ -27,7 +27,7 @@
},
"msbuild-sdks": {
"Yarn.MSBuild": "1.22.10",
- "Microsoft.DotNet.Arcade.Sdk": "7.0.0-beta.23211.2",
- "Microsoft.DotNet.Helix.Sdk": "7.0.0-beta.23211.2"
+ "Microsoft.DotNet.Arcade.Sdk": "7.0.0-beta.23313.4",
+ "Microsoft.DotNet.Helix.Sdk": "7.0.0-beta.23313.4"
}
}
diff --git a/src/Identity/Core/src/SignInManager.cs b/src/Identity/Core/src/SignInManager.cs
index 6a4fe1c09b9a..269230364cb8 100644
--- a/src/Identity/Core/src/SignInManager.cs
+++ b/src/Identity/Core/src/SignInManager.cs
@@ -4,6 +4,7 @@
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Security.Claims;
+using System.Text;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
@@ -388,7 +389,14 @@ public virtual async Task CheckPasswordSignInAsync(TUser user, str
// Only reset the lockout when not in quirks mode if either TFA is not enabled or the client is remembered for TFA.
if (alwaysLockout || !await IsTfaEnabled(user) || await IsTwoFactorClientRememberedAsync(user))
{
- await ResetLockout(user);
+ var resetLockoutResult = await ResetLockoutWithResult(user);
+ if (!resetLockoutResult.Succeeded)
+ {
+ // ResetLockout got an unsuccessful result that could be caused by concurrency failures indicating an
+ // attacker could be trying to bypass the MaxFailedAccessAttempts limit. Return the same failure we do
+ // when failing to increment the lockout to avoid giving an attacker extra guesses at the password.
+ return SignInResult.Failed;
+ }
}
return SignInResult.Success;
@@ -398,7 +406,13 @@ public virtual async Task CheckPasswordSignInAsync(TUser user, str
if (UserManager.SupportsUserLockout && lockoutOnFailure)
{
// If lockout is requested, increment access failed count which might lock out the user
- await UserManager.AccessFailedAsync(user);
+ var incrementLockoutResult = await UserManager.AccessFailedAsync(user) ?? IdentityResult.Success;
+ if (!incrementLockoutResult.Succeeded)
+ {
+ // Return the same failure we do when resetting the lockout fails after a correct password.
+ return SignInResult.Failed;
+ }
+
if (await UserManager.IsLockedOutAsync(user))
{
return await LockedOut(user);
@@ -467,18 +481,23 @@ public virtual async Task TwoFactorRecoveryCodeSignInAsync(string
var result = await UserManager.RedeemTwoFactorRecoveryCodeAsync(user, recoveryCode);
if (result.Succeeded)
{
- await DoTwoFactorSignInAsync(user, twoFactorInfo, isPersistent: false, rememberClient: false);
- return SignInResult.Success;
+ return await DoTwoFactorSignInAsync(user, twoFactorInfo, isPersistent: false, rememberClient: false);
}
// We don't protect against brute force attacks since codes are expected to be random.
return SignInResult.Failed;
}
- private async Task DoTwoFactorSignInAsync(TUser user, TwoFactorAuthenticationInfo twoFactorInfo, bool isPersistent, bool rememberClient)
+ private async Task DoTwoFactorSignInAsync(TUser user, TwoFactorAuthenticationInfo twoFactorInfo, bool isPersistent, bool rememberClient)
{
- // When token is verified correctly, clear the access failed count used for lockout
- await ResetLockout(user);
+ var resetLockoutResult = await ResetLockoutWithResult(user);
+ if (!resetLockoutResult.Succeeded)
+ {
+ // ResetLockout got an unsuccessful result that could be caused by concurrency failures indicating an
+ // attacker could be trying to bypass the MaxFailedAccessAttempts limit. Return the same failure we do
+ // when failing to increment the lockout to avoid giving an attacker extra guesses at the two factor code.
+ return SignInResult.Failed;
+ }
var claims = new List();
claims.Add(new Claim("amr", "mfa"));
@@ -496,6 +515,7 @@ private async Task DoTwoFactorSignInAsync(TUser user, TwoFactorAuthenticationInf
await RememberTwoFactorClientAsync(user);
}
await SignInWithClaimsAsync(user, isPersistent, claims);
+ return SignInResult.Success;
}
///
@@ -528,13 +548,18 @@ public virtual async Task TwoFactorAuthenticatorSignInAsync(string
if (await UserManager.VerifyTwoFactorTokenAsync(user, Options.Tokens.AuthenticatorTokenProvider, code))
{
- await DoTwoFactorSignInAsync(user, twoFactorInfo, isPersistent, rememberClient);
- return SignInResult.Success;
+ return await DoTwoFactorSignInAsync(user, twoFactorInfo, isPersistent, rememberClient);
}
// If the token is incorrect, record the failure which also may cause the user to be locked out
if (UserManager.SupportsUserLockout)
{
- await UserManager.AccessFailedAsync(user);
+ var incrementLockoutResult = await UserManager.AccessFailedAsync(user) ?? IdentityResult.Success;
+ if (!incrementLockoutResult.Succeeded)
+ {
+ // Return the same failure we do when resetting the lockout fails after a correct two factor code.
+ // This is currently redundant, but it's here in case the code gets copied elsewhere.
+ return SignInResult.Failed;
+ }
}
return SignInResult.Failed;
}
@@ -569,13 +594,18 @@ public virtual async Task TwoFactorSignInAsync(string provider, st
}
if (await UserManager.VerifyTwoFactorTokenAsync(user, provider, code))
{
- await DoTwoFactorSignInAsync(user, twoFactorInfo, isPersistent, rememberClient);
- return SignInResult.Success;
+ return await DoTwoFactorSignInAsync(user, twoFactorInfo, isPersistent, rememberClient);
}
// If the token is incorrect, record the failure which also may cause the user to be locked out
if (UserManager.SupportsUserLockout)
{
- await UserManager.AccessFailedAsync(user);
+ var incrementLockoutResult = await UserManager.AccessFailedAsync(user) ?? IdentityResult.Success;
+ if (!incrementLockoutResult.Succeeded)
+ {
+ // Return the same failure we do when resetting the lockout fails after a correct two factor code.
+ // This is currently redundant, but it's here in case the code gets copied elsewhere.
+ return SignInResult.Failed;
+ }
}
return SignInResult.Failed;
}
@@ -867,13 +897,77 @@ protected virtual Task LockedOut(TUser user)
///
/// The user
/// The that represents the asynchronous operation, containing the of the operation.
- protected virtual Task ResetLockout(TUser user)
+ protected virtual async Task ResetLockout(TUser user)
{
if (UserManager.SupportsUserLockout)
{
- return UserManager.ResetAccessFailedCountAsync(user);
+ // The IdentityResult should not be null according to the annotations, but our own tests return null and I'm trying to limit breakages.
+ var result = await UserManager.ResetAccessFailedCountAsync(user) ?? IdentityResult.Success;
+
+ if (!result.Succeeded)
+ {
+ throw new IdentityResultException(result);
+ }
+ }
+ }
+
+ private async Task ResetLockoutWithResult(TUser user)
+ {
+ // Avoid relying on throwing an exception if we're not in a derived class.
+ if (GetType() == typeof(SignInManager))
+ {
+ if (!UserManager.SupportsUserLockout)
+ {
+ return IdentityResult.Success;
+ }
+
+ return await UserManager.ResetAccessFailedCountAsync(user) ?? IdentityResult.Success;
+ }
+
+ try
+ {
+ var resetLockoutTask = ResetLockout(user);
+
+ if (resetLockoutTask is Task resultTask)
+ {
+ return await resultTask ?? IdentityResult.Success;
+ }
+
+ await resetLockoutTask;
+ return IdentityResult.Success;
+ }
+ catch (IdentityResultException ex)
+ {
+ return ex.IdentityResult;
+ }
+ }
+
+ private sealed class IdentityResultException : Exception
+ {
+ internal IdentityResultException(IdentityResult result) : base()
+ {
+ IdentityResult = result;
+ }
+
+ internal IdentityResult IdentityResult { get; set; }
+
+ public override string Message
+ {
+ get
+ {
+ var sb = new StringBuilder("ResetLockout failed.");
+
+ foreach (var error in IdentityResult.Errors)
+ {
+ sb.AppendLine();
+ sb.Append(error.Code);
+ sb.Append(": ");
+ sb.Append(error.Description);
+ }
+
+ return sb.ToString();
+ }
}
- return Task.CompletedTask;
}
internal sealed class TwoFactorAuthenticationInfo
diff --git a/src/Identity/test/Identity.Test/SignInManagerTest.cs b/src/Identity/test/Identity.Test/SignInManagerTest.cs
index 147c16e31de1..81cd53ce5b4d 100644
--- a/src/Identity/test/Identity.Test/SignInManagerTest.cs
+++ b/src/Identity/test/Identity.Test/SignInManagerTest.cs
@@ -975,4 +975,234 @@ public async Task ExternalLoginInfoAsyncReturnsAuthenticationPropertiesWithCusto
Assert.NotNull(externalProperties);
Assert.Equal("fizzbuzz", customValue);
}
+
+ public static object[][] SignInManagerTypeNames => new object[][]
+ {
+ new[] { nameof(SignInManager) },
+ new[] { nameof(NoOverridesSignInManager) },
+ new[] { nameof(OverrideAndAwaitBaseResetSignInManager) },
+ new[] { nameof(OverrideAndPassThroughUserManagerResetSignInManager) },
+ };
+
+ [Theory]
+ [MemberData(nameof(SignInManagerTypeNames))]
+ public async Task CheckPasswordSignInFailsWhenResetLockoutFails(string signInManagerTypeName)
+ {
+ // Setup
+ var user = new PocoUser { UserName = "Foo" };
+ var manager = SetupUserManager(user);
+ manager.Setup(m => m.SupportsUserLockout).Returns(true).Verifiable();
+ manager.Setup(m => m.IsLockedOutAsync(user)).ReturnsAsync(false).Verifiable();
+ manager.Setup(m => m.CheckPasswordAsync(user, "[PLACEHOLDER]-1a")).ReturnsAsync(true).Verifiable();
+ manager.Setup(m => m.ResetAccessFailedCountAsync(user)).ReturnsAsync(IdentityResult.Failed()).Verifiable();
+
+ var context = new DefaultHttpContext();
+ var helper = SetupSignInManagerType(manager.Object, context, signInManagerTypeName);
+
+ // Act
+ var result = await helper.CheckPasswordSignInAsync(user, "[PLACEHOLDER]-1a", false);
+
+ // Assert
+ Assert.Same(SignInResult.Failed, result);
+ manager.Verify();
+ }
+
+ [Theory]
+ [MemberData(nameof(SignInManagerTypeNames))]
+ public async Task PasswordSignInWorksWhenResetLockoutReturnsNullIdentityResult(string signInManagerTypeName)
+ {
+ // Setup
+ var user = new PocoUser { UserName = "Foo" };
+ var manager = SetupUserManager(user);
+ manager.Setup(m => m.SupportsUserLockout).Returns(true).Verifiable();
+ manager.Setup(m => m.IsLockedOutAsync(user)).ReturnsAsync(false).Verifiable();
+ manager.Setup(m => m.CheckPasswordAsync(user, "[PLACEHOLDER]-1a")).ReturnsAsync(true).Verifiable();
+ manager.Setup(m => m.ResetAccessFailedCountAsync(user)).ReturnsAsync((IdentityResult)null).Verifiable();
+
+ var context = new DefaultHttpContext();
+ var auth = MockAuth(context);
+ SetupSignIn(context, auth);
+ var helper = SetupSignInManagerType(manager.Object, context, signInManagerTypeName);
+
+ // Act
+ var result = await helper.PasswordSignInAsync(user.UserName, "[PLACEHOLDER]-1a", false, false);
+
+ // Assert
+ Assert.True(result.Succeeded);
+ manager.Verify();
+ auth.Verify();
+ }
+
+ [Fact]
+ public async Task TwoFactorSignFailsWhenResetLockoutFails()
+ {
+ // Setup
+ var user = new PocoUser { UserName = "Foo" };
+ var manager = SetupUserManager(user);
+ var provider = "twofactorprovider";
+ var code = "123456";
+ manager.Setup(m => m.SupportsUserLockout).Returns(true).Verifiable();
+ manager.Setup(m => m.IsLockedOutAsync(user)).ReturnsAsync(false).Verifiable();
+ manager.Setup(m => m.VerifyTwoFactorTokenAsync(user, provider, code)).ReturnsAsync(true).Verifiable();
+
+ manager.Setup(m => m.ResetAccessFailedCountAsync(user)).ReturnsAsync(IdentityResult.Failed()).Verifiable();
+
+ var context = new DefaultHttpContext();
+ var auth = MockAuth(context);
+ var helper = SetupSignInManager(manager.Object, context);
+ var id = SignInManager.StoreTwoFactorInfo(user.Id, null);
+ auth.Setup(a => a.AuthenticateAsync(context, IdentityConstants.TwoFactorUserIdScheme))
+ .ReturnsAsync(AuthenticateResult.Success(new AuthenticationTicket(id, null, IdentityConstants.TwoFactorUserIdScheme))).Verifiable();
+
+ // Act
+ var result = await helper.TwoFactorSignInAsync(provider, code, false, false);
+
+ // Assert
+ Assert.Same(SignInResult.Failed, result);
+ manager.Verify();
+ auth.Verify();
+ }
+
+ public static object[][] ExpectedLockedOutSignInResultsGivenAccessFailedResults => new object[][]
+ {
+ new object[] { IdentityResult.Success, SignInResult.LockedOut },
+ new object[] { null, SignInResult.LockedOut },
+ new object[] { IdentityResult.Failed(), SignInResult.Failed },
+ };
+
+ [Theory]
+ [MemberData(nameof(ExpectedLockedOutSignInResultsGivenAccessFailedResults))]
+ public async Task CheckPasswordSignInLockedOutResultIsDependentOnTheAccessFailedAsyncResult(IdentityResult accessFailedResult, SignInResult expectedSignInResult)
+ {
+ // Setup
+ var isLockedOutCallCount = 0;
+ var user = new PocoUser { UserName = "Foo" };
+ var manager = SetupUserManager(user);
+ manager.Setup(m => m.SupportsUserLockout).Returns(true).Verifiable();
+ // Return false initially to allow the password to be checked Only return true the second time after the bogus password is checked.
+ manager.Setup(m => m.IsLockedOutAsync(user)).ReturnsAsync(() => isLockedOutCallCount++ > 0).Verifiable();
+ manager.Setup(m => m.CheckPasswordAsync(user, "[PLACEHOLDER]-bogus1")).ReturnsAsync(false).Verifiable();
+ manager.Setup(m => m.AccessFailedAsync(user)).ReturnsAsync(accessFailedResult).Verifiable();
+
+ var context = new DefaultHttpContext();
+ // Since the PasswordSignInAsync calls the UserManager directly rather than a virtual SignInManager method like ResetLockout, we don't need to test derived SignInManagers.
+ var helper = SetupSignInManager(manager.Object, context);
+
+ // Act
+ var result = await helper.CheckPasswordSignInAsync(user, "[PLACEHOLDER]-bogus1", lockoutOnFailure: true);
+
+ // Assert
+ Assert.Same(expectedSignInResult, result);
+ manager.Verify();
+ }
+
+ public static object[][] AccessFailedResults => new object[][]
+ {
+ new object[] { IdentityResult.Success },
+ new object[] { null },
+ new object[] { IdentityResult.Failed() },
+ };
+
+ [Theory]
+ [MemberData(nameof(AccessFailedResults))]
+ public async Task TwoFactorSignInLockedOutResultIsAlwaysGenericFailureRegardlessOfTheAccessFailedAsyncResult(IdentityResult accessFailedResult)
+ {
+ // Setup
+ var isLockedOutCallCount = 0;
+ var user = new PocoUser { UserName = "Foo" };
+ var manager = SetupUserManager(user);
+ var provider = "twofactorprovider";
+ var code = "123456";
+ manager.Setup(m => m.SupportsUserLockout).Returns(true).Verifiable();
+ // Return false initially to allow the 2fa code to be checked. Only return true if ever in the future it is called again after failure.
+ manager.Setup(m => m.IsLockedOutAsync(user)).ReturnsAsync(() => isLockedOutCallCount++ > 0).Verifiable();
+ manager.Setup(m => m.VerifyTwoFactorTokenAsync(user, provider, code)).ReturnsAsync(false).Verifiable();
+
+ manager.Setup(m => m.AccessFailedAsync(user)).ReturnsAsync(accessFailedResult).Verifiable();
+
+ var context = new DefaultHttpContext();
+ var auth = MockAuth(context);
+ var helper = SetupSignInManager(manager.Object, context);
+ var id = SignInManager.StoreTwoFactorInfo(user.Id, null);
+ auth.Setup(a => a.AuthenticateAsync(context, IdentityConstants.TwoFactorUserIdScheme))
+ .ReturnsAsync(AuthenticateResult.Success(new AuthenticationTicket(id, null, IdentityConstants.TwoFactorUserIdScheme))).Verifiable();
+
+ // Act
+ var result = await helper.TwoFactorSignInAsync(provider, code, false, false);
+
+ // Assert
+ // Unlike password sign in, 2fa always returns SignInResult.Failed rather than LockedOut.
+ Assert.Same(SignInResult.Failed, result);
+ manager.Verify();
+ auth.Verify();
+ }
+
+ private static SignInManager SetupSignInManagerType(UserManager manager, HttpContext context, string typeName)
+ {
+ var contextAccessor = new Mock();
+ contextAccessor.Setup(a => a.HttpContext).Returns(context);
+ var roleManager = MockHelpers.MockRoleManager();
+ var options = Options.Create(new IdentityOptions());
+ var claimsFactory = new UserClaimsPrincipalFactory(manager, roleManager.Object, options);
+
+ return typeName switch
+ {
+ nameof(SignInManager) => new SignInManager(manager, contextAccessor.Object, claimsFactory, options, NullLogger>.Instance, Mock.Of(), new DefaultUserConfirmation()),
+ nameof(NoOverridesSignInManager) => new NoOverridesSignInManager(manager, contextAccessor.Object, claimsFactory, options),
+ nameof(OverrideAndAwaitBaseResetSignInManager) => new OverrideAndAwaitBaseResetSignInManager(manager, contextAccessor.Object, claimsFactory, options),
+ nameof(OverrideAndPassThroughUserManagerResetSignInManager) => new OverrideAndPassThroughUserManagerResetSignInManager(manager, contextAccessor.Object, claimsFactory, options),
+ _ => throw new NotImplementedException(),
+ };
+ }
+
+ private class NoOverridesSignInManager : SignInManager where TUser : class
+ {
+ public NoOverridesSignInManager(
+ UserManager userManager,
+ IHttpContextAccessor contextAccessor,
+ IUserClaimsPrincipalFactory claimsFactory,
+ IOptions optionsAccessor)
+ : base(userManager, contextAccessor, claimsFactory, optionsAccessor, NullLogger>.Instance, Mock.Of(), new DefaultUserConfirmation())
+ {
+ }
+ }
+
+ private class OverrideAndAwaitBaseResetSignInManager : SignInManager where TUser : class
+ {
+ public OverrideAndAwaitBaseResetSignInManager(
+ UserManager userManager,
+ IHttpContextAccessor contextAccessor,
+ IUserClaimsPrincipalFactory claimsFactory,
+ IOptions optionsAccessor)
+ : base(userManager, contextAccessor, claimsFactory, optionsAccessor, NullLogger>.Instance, Mock.Of(), new DefaultUserConfirmation())
+ {
+ }
+
+ protected override async Task ResetLockout(TUser user)
+ {
+ await base.ResetLockout(user);
+ }
+ }
+
+ private class OverrideAndPassThroughUserManagerResetSignInManager : SignInManager where TUser : class
+ {
+ public OverrideAndPassThroughUserManagerResetSignInManager(
+ UserManager userManager,
+ IHttpContextAccessor contextAccessor,
+ IUserClaimsPrincipalFactory claimsFactory,
+ IOptions optionsAccessor)
+ : base(userManager, contextAccessor, claimsFactory, optionsAccessor, NullLogger>.Instance, Mock.Of(), new DefaultUserConfirmation())
+ {
+ }
+
+ protected override Task ResetLockout(TUser user)
+ {
+ if (UserManager.SupportsUserLockout)
+ {
+ return UserManager.ResetAccessFailedCountAsync(user);
+ }
+
+ return Task.CompletedTask;
+ }
+ }
}
diff --git a/src/submodules/googletest b/src/submodules/googletest
index 797b0ad2a3a4..04cf2989168a 160000
--- a/src/submodules/googletest
+++ b/src/submodules/googletest
@@ -1 +1 @@
-Subproject commit 797b0ad2a3a45608ecf5c67e6e289d377a3521ca
+Subproject commit 04cf2989168a3f9218d463bea6f15f8ade2032fd
diff --git a/src/submodules/spa-templates b/src/submodules/spa-templates
index d5cae287824e..68cd408c62c6 160000
--- a/src/submodules/spa-templates
+++ b/src/submodules/spa-templates
@@ -1 +1 @@
-Subproject commit d5cae287824ec8bb94f1cf5514af3e89364864c9
+Subproject commit 68cd408c62c6a881a58f41a6f6f5f8b73b6ae515