This is scenario where we just want to use FIDO as the MFA. The user register and logins with their username and password. For demo purposes, we trigger the MFA registering on sign up.
├── .github
└── workflows
│ └── dotnet.yml
├── .gitignore
├── BlazorServerOidc.sln
├── BlazorWebApp
├── BlazorWebApp.csproj
├── Components
│ ├── App.razor
│ ├── Layout
│ │ ├── MainLayout.razor
│ │ ├── MainLayout.razor.css
│ │ ├── NavMenu.razor
│ │ ├── NavMenu.razor.css
│ │ └── RedirectToLogin.razor
│ ├── Pages
│ │ ├── Counter.razor
│ │ ├── Error.razor
│ │ ├── Home.razor
│ │ └── Weather.razor
│ ├── Routes.razor
│ └── _Imports.razor
├── MapLoginLogoutEndpoints.cs
├── Program.cs
├── Properties
│ └── launchSettings.json
├── SecurityHeadersDefinitions.cs
├── appsettings.Development.json
├── appsettings.json
└── wwwroot
│ ├── app.css
│ ├── favicon.png
│ └── lib
│ └── bootstrap
│ └── dist
│ ├── css
│ ├── bootstrap-grid.css
│ ├── bootstrap-grid.css.map
│ ├── bootstrap-grid.min.css
│ ├── bootstrap-grid.min.css.map
│ ├── bootstrap-grid.rtl.css
│ ├── bootstrap-grid.rtl.css.map
│ ├── bootstrap-grid.rtl.min.css
│ ├── bootstrap-grid.rtl.min.css.map
│ ├── bootstrap-reboot.css
│ ├── bootstrap-reboot.css.map
│ ├── bootstrap-reboot.min.css
│ ├── bootstrap-reboot.min.css.map
│ ├── bootstrap-reboot.rtl.css
│ ├── bootstrap-reboot.rtl.css.map
│ ├── bootstrap-reboot.rtl.min.css
│ ├── bootstrap-reboot.rtl.min.css.map
│ ├── bootstrap-utilities.css
│ ├── bootstrap-utilities.css.map
│ ├── bootstrap-utilities.min.css
│ ├── bootstrap-utilities.min.css.map
│ ├── bootstrap-utilities.rtl.css
│ ├── bootstrap-utilities.rtl.css.map
│ ├── bootstrap-utilities.rtl.min.css
│ ├── bootstrap-utilities.rtl.min.css.map
│ ├── bootstrap.css
│ ├── bootstrap.css.map
│ ├── bootstrap.min.css
│ ├── bootstrap.min.css.map
│ ├── bootstrap.rtl.css
│ ├── bootstrap.rtl.css.map
│ ├── bootstrap.rtl.min.css
│ └── bootstrap.rtl.min.css.map
│ └── js
│ ├── bootstrap.bundle.js
│ ├── bootstrap.bundle.js.map
│ ├── bootstrap.bundle.min.js
│ ├── bootstrap.bundle.min.js.map
│ ├── bootstrap.esm.js
│ ├── bootstrap.esm.js.map
│ ├── bootstrap.esm.min.js
│ ├── bootstrap.esm.min.js.map
│ ├── bootstrap.js
│ ├── bootstrap.js.map
│ ├── bootstrap.min.js
│ └── bootstrap.min.js.map
├── IdentityProvider
├── Areas
│ └── Identity
│ │ ├── IdentityHostingStartup.cs
│ │ └── Pages
│ │ ├── Account
│ │ ├── Login.cshtml
│ │ ├── Login.cshtml.cs
│ │ ├── LoginFido2Mfa.cshtml
│ │ ├── LoginFido2Mfa.cshtml.cs
│ │ ├── Manage
│ │ │ ├── Disable2fa.cshtml
│ │ │ ├── Disable2fa.cshtml.cs
│ │ │ ├── Fido2Mfa.cshtml
│ │ │ ├── Fido2Mfa.cshtml.cs
│ │ │ ├── ManageNavPages.cs
│ │ │ ├── TwoFactorAuthentication.cshtml
│ │ │ ├── TwoFactorAuthentication.cshtml.cs
│ │ │ ├── _ManageNav.cshtml
│ │ │ └── _ViewImports.cshtml
│ │ └── _ViewImports.cshtml
│ │ ├── _ValidationScriptsPartial.cshtml
│ │ ├── _ViewImports.cshtml
│ │ └── _ViewStart.cshtml
├── Controllers
│ ├── AuthorizationController.cs
│ ├── ErrorController.cs
│ ├── HomeController.cs
│ ├── ResourceController.cs
│ └── UserinfoController.cs
├── Data
│ ├── ApplicationDbContext.cs
│ └── ApplicationUser.cs
├── Fido2
│ ├── Fido2Store.cs
│ ├── Fido2UserTwoFactorTokenProvider.cs
│ ├── FidoStoredCredential.cs
│ ├── MfaFido2RegisterController.cs
│ ├── MfaFido2SignInFidoController.cs
│ ├── PwFido2RegisterController.cs
│ └── PwFido2SignInController.cs
├── Helpers
│ ├── AsyncEnumerableExtensions.cs
│ └── FormValueRequiredAttribute.cs
├── HostingExtensions.cs
├── IdentityProvider.csproj
├── Migrations
│ ├── 20231222075731_init_sts.Designer.cs
│ ├── 20231222075731_init_sts.cs
│ ├── 20231222144924_5-0.0.Designer.cs
│ ├── 20231222144924_5-0.0.cs
│ └── ApplicationDbContextModelSnapshot.cs
├── Program.cs
├── Properties
│ └── launchSettings.json
├── ViewModels
│ ├── Authorization
│ │ └── AuthorizeViewModel.cs
│ └── Shared
│ │ └── ErrorViewModel.cs
├── Views
│ ├── Authorization
│ │ ├── Authorize.cshtml
│ │ └── Logout.cshtml
│ ├── Home
│ │ ├── Index.cshtml
│ │ └── Privacy.cshtml
│ ├── Shared
│ │ ├── Error.cshtml
│ │ ├── _Layout.cshtml
│ │ └── _LoginPartial.cshtml
│ ├── _ViewImports.cshtml
│ └── _ViewStart.cshtml
├── Worker.cs
├── appsettings.json
└── wwwroot
│ ├── css
│ └── site.css
│ ├── favicon.ico
│ ├── images
│ ├── securitykey.min.svg
│ └── securitykey.svg
│ ├── js
│ ├── helpers.js
│ ├── instant.js
│ ├── mfa.login.js
│ ├── mfa.register.js
│ └── site.js
│ └── lib
│ ├── bootstrap
│ ├── LICENSE
│ └── dist
│ │ ├── css
│ │ ├── bootstrap-grid.css
│ │ ├── bootstrap-grid.css.map
│ │ ├── bootstrap-grid.min.css
│ │ ├── bootstrap-grid.min.css.map
│ │ ├── bootstrap-reboot.css
│ │ ├── bootstrap-reboot.css.map
│ │ ├── bootstrap-reboot.min.css
│ │ ├── bootstrap-reboot.min.css.map
│ │ ├── bootstrap.css
│ │ ├── bootstrap.css.map
│ │ ├── bootstrap.min.css
│ │ └── bootstrap.min.css.map
│ │ └── js
│ │ ├── bootstrap.bundle.js
│ │ ├── bootstrap.bundle.js.map
│ │ ├── bootstrap.bundle.min.js
│ │ ├── bootstrap.bundle.min.js.map
│ │ ├── bootstrap.js
│ │ ├── bootstrap.js.map
│ │ ├── bootstrap.min.js
│ │ └── bootstrap.min.js.map
│ ├── jquery-validation-unobtrusive
│ ├── LICENSE.txt
│ ├── jquery.validate.unobtrusive.js
│ └── jquery.validate.unobtrusive.min.js
│ ├── jquery-validation
│ ├── LICENSE.md
│ └── dist
│ │ ├── additional-methods.js
│ │ ├── additional-methods.min.js
│ │ ├── jquery.validate.js
│ │ └── jquery.validate.min.js
│ └── jquery
│ ├── LICENSE.txt
│ └── dist
│ ├── jquery.js
│ ├── jquery.min.js
│ └── jquery.min.map
├── LICENSE
├── Old
├── BlazorServerOidc
│ ├── App.razor
│ ├── BlazorServerOidc.csproj
│ ├── Controllers
│ │ └── AccountController.cs
│ ├── Data
│ │ ├── WeatherForecast.cs
│ │ └── WeatherForecastService.cs
│ ├── Pages
│ │ ├── Counter.razor
│ │ ├── Error.cshtml
│ │ ├── Error.cshtml.cs
│ │ ├── FetchData.razor
│ │ ├── Index.razor
│ │ ├── SignedOut.cshtml
│ │ ├── SignedOut.cshtml.cs
│ │ ├── _Host.cshtml
│ │ └── _Layout.cshtml
│ ├── Program.cs
│ ├── Properties
│ │ └── launchSettings.json
│ ├── SecurityHeadersDefinitions.cs
│ ├── Shared
│ │ ├── LoginLogoutMenu.razor
│ │ ├── MainLayout.razor
│ │ ├── MainLayout.razor.css
│ │ ├── NavMenu.razor
│ │ ├── NavMenu.razor.css
│ │ └── SurveyPrompt.razor
│ ├── _Imports.razor
│ ├── appsettings.Development.json
│ ├── appsettings.json
│ └── wwwroot
│ │ ├── .well-known
│ │ └── security.txt
│ │ ├── css
│ │ ├── bootstrap
│ │ │ ├── bootstrap.min.css
│ │ │ └── bootstrap.min.css.map
│ │ ├── open-iconic
│ │ │ ├── FONT-LICENSE
│ │ │ ├── ICON-LICENSE
│ │ │ ├── README.md
│ │ │ └── font
│ │ │ │ ├── css
│ │ │ │ └── open-iconic-bootstrap.min.css
│ │ │ │ └── fonts
│ │ │ │ ├── open-iconic.eot
│ │ │ │ ├── open-iconic.otf
│ │ │ │ ├── open-iconic.svg
│ │ │ │ ├── open-iconic.ttf
│ │ │ │ └── open-iconic.woff
│ │ └── site.css
│ │ ├── favicon.ico
│ │ └── js
│ │ ├── blazor.bootstrap.js
│ │ ├── bootstrap.bundle.js
│ │ ├── bootstrap.bundle.js.map
│ │ ├── bootstrap.bundle.min.js
│ │ ├── bootstrap.bundle.min.js.map
│ │ ├── bootstrap.esm.js
│ │ ├── bootstrap.esm.js.map
│ │ ├── bootstrap.esm.min.js
│ │ ├── bootstrap.esm.min.js.map
│ │ ├── bootstrap.js
│ │ ├── bootstrap.js.map
│ │ ├── bootstrap.min.js
│ │ ├── bootstrap.min.js.map
│ │ └── popper.min.js
└── BlazorWebFromBlazorServerOidc
│ ├── App.razor
│ ├── BlazorNonceService.cs
│ ├── BlazorWebFromBlazorServerOidc.csproj
│ ├── Controllers
│ └── AccountController.cs
│ ├── Data
│ ├── WeatherForecast.cs
│ └── WeatherForecastService.cs
│ ├── Layout
│ ├── LogInOrOut.razor
│ ├── MainLayout.razor
│ ├── MainLayout.razor.css
│ ├── NavMenu.razor
│ └── NavMenu.razor.css
│ ├── NonceMiddleware.cs
│ ├── Pages
│ ├── Counter.razor
│ ├── Error.cshtml
│ ├── Error.cshtml.cs
│ ├── FetchData.razor
│ ├── Index.razor
│ ├── SignedOut.cshtml
│ └── SignedOut.cshtml.cs
│ ├── Program.cs
│ ├── Properties
│ └── launchSettings.json
│ ├── Routes.razor
│ ├── SecurityHeadersDefinitions.cs
│ ├── _Imports.razor
│ ├── appsettings.Development.json
│ ├── appsettings.json
│ └── wwwroot
│ ├── .well-known
│ └── security.txt
│ ├── css
│ ├── bootstrap
│ │ ├── bootstrap.min.css
│ │ └── bootstrap.min.css.map
│ ├── open-iconic
│ │ ├── FONT-LICENSE
│ │ ├── ICON-LICENSE
│ │ ├── README.md
│ │ └── font
│ │ │ ├── css
│ │ │ └── open-iconic-bootstrap.min.css
│ │ │ └── fonts
│ │ │ ├── open-iconic.eot
│ │ │ ├── open-iconic.otf
│ │ │ ├── open-iconic.svg
│ │ │ ├── open-iconic.ttf
│ │ │ └── open-iconic.woff
│ └── site.css
│ ├── favicon.ico
│ └── js
│ ├── blazor.bootstrap.js
│ ├── bootstrap.bundle.js
│ ├── bootstrap.bundle.js.map
│ ├── bootstrap.bundle.min.js
│ ├── bootstrap.bundle.min.js.map
│ ├── bootstrap.esm.js
│ ├── bootstrap.esm.js.map
│ ├── bootstrap.esm.min.js
│ ├── bootstrap.esm.min.js.map
│ ├── bootstrap.js
│ ├── bootstrap.js.map
│ ├── bootstrap.min.js
│ ├── bootstrap.min.js.map
│ └── popper.min.js
└── README.md
/.github/workflows/dotnet.yml:
--------------------------------------------------------------------------------
1 | # This workflow will build a .NET project
2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net
3 |
4 | name: .NET
5 |
6 | on:
7 | push:
8 | branches: [ "main" ]
9 | pull_request:
10 | branches: [ "main" ]
11 |
12 | jobs:
13 | build:
14 |
15 | runs-on: ubuntu-latest
16 |
17 | steps:
18 | - uses: actions/checkout@v4
19 | - name: Setup .NET
20 | uses: actions/setup-dotnet@v4
21 | with:
22 | dotnet-version: 9.0.x
23 | - name: Restore dependencies
24 | run: dotnet restore
25 | - name: Build
26 | run: dotnet build --no-restore
27 | - name: Test
28 | run: dotnet test --no-build --verbosity normal
29 |
--------------------------------------------------------------------------------
/BlazorServerOidc.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.8.34330.188
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IdentityProvider", "IdentityProvider\IdentityProvider.csproj", "{763C2B8D-87E1-4289-9F19-FE2834FA62F7}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{2826EED1-B486-46EF-9D22-771A0FABC003}"
9 | ProjectSection(SolutionItems) = preProject
10 | .github\workflows\dotnet.yml = .github\workflows\dotnet.yml
11 | README.md = README.md
12 | EndProjectSection
13 | EndProject
14 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Old", "Old", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}"
15 | EndProject
16 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlazorServerOidc", "Old\BlazorServerOidc\BlazorServerOidc.csproj", "{EE564E05-E433-7D2C-B719-4000E6B67CA7}"
17 | EndProject
18 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlazorWebFromBlazorServerOidc", "Old\BlazorWebFromBlazorServerOidc\BlazorWebFromBlazorServerOidc.csproj", "{FD991A49-4041-920D-D635-E5FEA13A87CE}"
19 | EndProject
20 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlazorWebApp", "BlazorWebApp\BlazorWebApp.csproj", "{2BA1FED1-8B11-44D8-A902-FF3EBF51DDEB}"
21 | EndProject
22 | Global
23 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
24 | Debug|Any CPU = Debug|Any CPU
25 | Release|Any CPU = Release|Any CPU
26 | EndGlobalSection
27 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
28 | {763C2B8D-87E1-4289-9F19-FE2834FA62F7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
29 | {763C2B8D-87E1-4289-9F19-FE2834FA62F7}.Debug|Any CPU.Build.0 = Debug|Any CPU
30 | {763C2B8D-87E1-4289-9F19-FE2834FA62F7}.Release|Any CPU.ActiveCfg = Release|Any CPU
31 | {763C2B8D-87E1-4289-9F19-FE2834FA62F7}.Release|Any CPU.Build.0 = Release|Any CPU
32 | {EE564E05-E433-7D2C-B719-4000E6B67CA7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33 | {EE564E05-E433-7D2C-B719-4000E6B67CA7}.Debug|Any CPU.Build.0 = Debug|Any CPU
34 | {EE564E05-E433-7D2C-B719-4000E6B67CA7}.Release|Any CPU.ActiveCfg = Release|Any CPU
35 | {EE564E05-E433-7D2C-B719-4000E6B67CA7}.Release|Any CPU.Build.0 = Release|Any CPU
36 | {FD991A49-4041-920D-D635-E5FEA13A87CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
37 | {FD991A49-4041-920D-D635-E5FEA13A87CE}.Debug|Any CPU.Build.0 = Debug|Any CPU
38 | {FD991A49-4041-920D-D635-E5FEA13A87CE}.Release|Any CPU.ActiveCfg = Release|Any CPU
39 | {FD991A49-4041-920D-D635-E5FEA13A87CE}.Release|Any CPU.Build.0 = Release|Any CPU
40 | {2BA1FED1-8B11-44D8-A902-FF3EBF51DDEB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
41 | {2BA1FED1-8B11-44D8-A902-FF3EBF51DDEB}.Debug|Any CPU.Build.0 = Debug|Any CPU
42 | {2BA1FED1-8B11-44D8-A902-FF3EBF51DDEB}.Release|Any CPU.ActiveCfg = Release|Any CPU
43 | {2BA1FED1-8B11-44D8-A902-FF3EBF51DDEB}.Release|Any CPU.Build.0 = Release|Any CPU
44 | EndGlobalSection
45 | GlobalSection(SolutionProperties) = preSolution
46 | HideSolutionNode = FALSE
47 | EndGlobalSection
48 | GlobalSection(NestedProjects) = preSolution
49 | {EE564E05-E433-7D2C-B719-4000E6B67CA7} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
50 | {FD991A49-4041-920D-D635-E5FEA13A87CE} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
51 | EndGlobalSection
52 | GlobalSection(ExtensibilityGlobals) = postSolution
53 | SolutionGuid = {F612A4DE-7883-4FA5-AF96-8E18D8DFCE59}
54 | EndGlobalSection
55 | EndGlobal
56 |
--------------------------------------------------------------------------------
/BlazorWebApp/BlazorWebApp.csproj:
--------------------------------------------------------------------------------
1 |
Current count: @currentCount
9 | 10 | 11 | 12 | @code { 13 | private int currentCount = 0; 14 | 15 | private void IncrementCount() 16 | { 17 | currentCount++; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /BlazorWebApp/Components/Pages/Error.razor: -------------------------------------------------------------------------------- 1 | @page "/Error" 2 | @using System.Diagnostics 3 | 4 |
12 | Request ID: @RequestId
13 |
18 | Swapping to Development environment will display more detailed information about the error that occurred. 19 |
20 |21 | The Development environment shouldn't be enabled for deployed applications. 22 | It can result in displaying sensitive information from exceptions to end users. 23 | For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development 24 | and restarting the app. 25 |
26 | 27 | @code{ 28 | [CascadingParameter] 29 | private HttpContext? HttpContext { get; set; } 30 | 31 | private string? RequestId { get; set; } 32 | private bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 33 | 34 | protected override void OnInitialized() => 35 | RequestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier; 36 | } 37 | -------------------------------------------------------------------------------- /BlazorWebApp/Components/Pages/Home.razor: -------------------------------------------------------------------------------- 1 | @page "/" 2 | 3 |This component demonstrates showing data.
9 | 10 | @if (forecasts == null) 11 | { 12 |Loading...
13 | } 14 | else 15 | { 16 |Date | 20 |Temp. (C) | 21 |Temp. (F) | 22 |Summary | 23 |
---|---|---|---|
@forecast.Date.ToShortDateString() | 30 |@forecast.TemperatureC | 31 |@forecast.TemperatureF | 32 |@forecast.Summary | 33 |
You are not authorized to access this resource.
16 | } 17 |60 | There are no external authentication services configured. See this article 61 | about setting up this ASP.NET application to support logging in via external services. 62 |
63 |This is scenario where we just want to use FIDO as the MFA. The user register and logins with their username and password. For demo purposes, we trigger the MFA registering on sign up.
13 | This action only disables 2FA. 14 |
15 |16 | Disabling 2FA does not change the keys used in authenticator apps. If you wish to change the key 17 | used in an authenticator app you should reset your authenticator keys. 18 |
19 |This is scenario where we just want to use FIDO as the MFA. The user register and logins with their username and password. For demo purposes, we trigger the MFA registering on sign up.
You must generate a new set of recovery codes before you can log in with a recovery code.
22 |You can generate a new set of recovery codes.
29 |You should generate a new set of recovery codes.
36 |You must accept the policy before you can enable two factor authentication.
65 |Do you want to grant @Model.ApplicationName access to your data? (scopes requested: @Model.Scope)
8 | 9 | 20 |Are you sure you want to sign out?
6 | 7 | 17 |Learn about building Web apps with ASP.NET Core.
8 |Use this page to detail your site's privacy policy.
7 | -------------------------------------------------------------------------------- /IdentityProvider/Views/Shared/Error.cshtml: -------------------------------------------------------------------------------- 1 | @model ErrorViewModel 2 | 3 |6 | @if (!string.IsNullOrEmpty(Model.Error)) { 7 | @Model.Error 8 | } 9 | 10 | @if (!string.IsNullOrEmpty(Model.ErrorDescription)) { 11 | @Model.ErrorDescription 12 | } 13 |
14 |Sorry, there's nothing at this address.
21 |Current count: @currentCount
8 | 9 | 10 | 11 | @code { 12 | private int currentCount = 0; 13 | 14 | private void IncrementCount() 15 | { 16 | currentCount++; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Old/BlazorServerOidc/Pages/Error.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model BlazorServerOidc.Pages.ErrorModel 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
24 | Request ID: @Model.RequestId
25 |
30 | Swapping to the Development environment displays detailed information about the error that occurred. 31 |
32 |33 | The Development environment shouldn't be enabled for deployed applications. 34 | It can result in displaying sensitive information from exceptions to end users. 35 | For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development 36 | and restarting the app. 37 |
38 |This component demonstrates fetching data from a service.
11 | 12 | @if (forecasts == null) 13 | { 14 |Loading...
15 | } 16 | else 17 | { 18 |Date | 22 |Temp. (C) | 23 |Temp. (F) | 24 |Summary | 25 |
---|---|---|---|
@forecast.Date.ToShortDateString() | 32 |@forecast.TemperatureC | 33 |@forecast.TemperatureF | 34 |@forecast.Summary | 35 |
You have successfully signed out
39 |