├── IdentityServerAspNetIdentity ├── Views │ ├── _ViewStart.cshtml │ ├── _ViewImports.cshtml │ ├── Device │ │ ├── Success.cshtml │ │ ├── UserCodeCapture.cshtml │ │ └── UserCodeConfirmation.cshtml │ ├── Account │ │ ├── AccessDenied.cshtml │ │ ├── Logout.cshtml │ │ ├── LoggedOut.cshtml │ │ └── Login.cshtml │ ├── Shared │ │ ├── _ValidationSummary.cshtml │ │ ├── Redirect.cshtml │ │ ├── _ScopeListItem.cshtml │ │ ├── Error.cshtml │ │ └── _Layout.cshtml │ ├── Diagnostics │ │ └── Index.cshtml │ ├── Home │ │ └── Index.cshtml │ ├── Grants │ │ └── Index.cshtml │ └── Consent │ │ └── Index.cshtml ├── AspIdUsers.db ├── appsettings.json ├── wwwroot │ ├── icon.jpg │ ├── icon.png │ ├── favicon.ico │ ├── js │ │ ├── signin-redirect.js │ │ └── signout-redirect.js │ ├── lib │ │ ├── bootstrap │ │ │ └── fonts │ │ │ │ ├── glyphicons-halflings-regular.eot │ │ │ │ ├── glyphicons-halflings-regular.ttf │ │ │ │ ├── glyphicons-halflings-regular.woff │ │ │ │ └── glyphicons-halflings-regular.woff2 │ │ └── jquery-validation │ │ │ └── build │ │ │ └── release.js │ └── css │ │ ├── site.min.css │ │ ├── site.css │ │ └── site.less ├── Models │ └── ApplicationUser.cs ├── Quickstart │ ├── Account │ │ ├── LogoutInputModel.cs │ │ ├── RedirectViewModel.cs │ │ ├── LogoutViewModel.cs │ │ ├── ExternalProvider.cs │ │ ├── LoginInputModel.cs │ │ ├── LoggedOutViewModel.cs │ │ ├── LoginViewModel.cs │ │ ├── AccountOptions.cs │ │ └── ExternalController.cs │ ├── Device │ │ ├── DeviceAuthorizationInputModel.cs │ │ ├── DeviceAuthorizationViewModel.cs │ │ └── DeviceController.cs │ ├── Consent │ │ ├── ConsentInputModel.cs │ │ ├── ScopeViewModel.cs │ │ ├── ProcessConsentResult.cs │ │ ├── ConsentViewModel.cs │ │ ├── ConsentOptions.cs │ │ └── ConsentController.cs │ ├── Home │ │ ├── ErrorViewModel.cs │ │ └── HomeController.cs │ ├── Extensions.cs │ ├── Grants │ │ ├── GrantsViewModel.cs │ │ └── GrantsController.cs │ ├── Diagnostics │ │ ├── DiagnosticsController.cs │ │ └── DiagnosticsViewModel.cs │ └── SecurityHeadersAttribute.cs ├── Properties │ └── launchSettings.json ├── Data │ ├── ApplicationDbContext.cs │ └── Migrations │ │ ├── ApplicationDbContextModelSnapshot.cs │ │ ├── 20180109192453_CreateIdentitySchema.Designer.cs │ │ └── 20180109192453_CreateIdentitySchema.cs ├── IdentityServerAspNetIdentity.csproj ├── tempkey.rsa ├── Config.cs ├── Program.cs ├── Startup.cs ├── SeedData.cs └── updateUI.ps1 ├── BlazorId_App ├── wwwroot │ ├── favicon.ico │ └── css │ │ ├── open-iconic │ │ ├── font │ │ │ ├── fonts │ │ │ │ ├── open-iconic.eot │ │ │ │ ├── open-iconic.otf │ │ │ │ ├── open-iconic.ttf │ │ │ │ └── open-iconic.woff │ │ │ └── css │ │ │ │ └── open-iconic-bootstrap.min.css │ │ ├── ICON-LICENSE │ │ ├── README.md │ │ └── FONT-LICENSE │ │ └── site.css ├── Pages │ ├── Index.razor │ ├── Counter.razor │ ├── Identity-Api.razor │ ├── Identity-App.razor │ ├── Error.razor │ └── _Host.cshtml ├── appsettings.json ├── appsettings.Development.json ├── Shared │ ├── MainLayout.razor │ ├── SurveyPrompt.razor │ └── NavMenu.razor ├── _Imports.razor ├── BlazorId_App.csproj ├── Properties │ └── launchSettings.json ├── App.razor ├── Program.cs ├── Data │ └── IdentityDataService.cs └── Startup.cs ├── Api ├── appsettings.json ├── Api.csproj ├── Program.cs ├── Properties │ └── launchSettings.json ├── Controllers │ └── IdentityController.cs └── Startup.cs ├── BlazorId_Shared ├── BlazorId_Shared.csproj └── Policies │ └── Policies.cs ├── BlazorId_App.sln ├── .gitattributes └── .gitignore /IdentityServerAspNetIdentity/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "_Layout"; 3 | } 4 | -------------------------------------------------------------------------------- /BlazorId_App/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tricklebyte/BlazorId_App/HEAD/BlazorId_App/wwwroot/favicon.ico -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/AspIdUsers.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tricklebyte/BlazorId_App/HEAD/IdentityServerAspNetIdentity/AspIdUsers.db -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "ConnectionStrings": { 3 | "DefaultConnection": "Data Source=AspIdUsers.db;" 4 | } 5 | } -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/Views/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using IdentityServer4.Quickstart.UI 2 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 3 | -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/wwwroot/icon.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tricklebyte/BlazorId_App/HEAD/IdentityServerAspNetIdentity/wwwroot/icon.jpg -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/wwwroot/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tricklebyte/BlazorId_App/HEAD/IdentityServerAspNetIdentity/wwwroot/icon.png -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tricklebyte/BlazorId_App/HEAD/IdentityServerAspNetIdentity/wwwroot/favicon.ico -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/wwwroot/js/signin-redirect.js: -------------------------------------------------------------------------------- 1 | window.location.href = document.querySelector("meta[http-equiv=refresh]").getAttribute("data-url"); 2 | -------------------------------------------------------------------------------- /BlazorId_App/Pages/Index.razor: -------------------------------------------------------------------------------- 1 | @page "/" 2 | 3 |

Hello, world!

4 | 5 | Welcome to your new app. 6 | 7 | 8 | -------------------------------------------------------------------------------- /BlazorId_App/wwwroot/css/open-iconic/font/fonts/open-iconic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tricklebyte/BlazorId_App/HEAD/BlazorId_App/wwwroot/css/open-iconic/font/fonts/open-iconic.eot -------------------------------------------------------------------------------- /BlazorId_App/wwwroot/css/open-iconic/font/fonts/open-iconic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tricklebyte/BlazorId_App/HEAD/BlazorId_App/wwwroot/css/open-iconic/font/fonts/open-iconic.otf -------------------------------------------------------------------------------- /BlazorId_App/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tricklebyte/BlazorId_App/HEAD/BlazorId_App/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf -------------------------------------------------------------------------------- /BlazorId_App/wwwroot/css/open-iconic/font/fonts/open-iconic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tricklebyte/BlazorId_App/HEAD/BlazorId_App/wwwroot/css/open-iconic/font/fonts/open-iconic.woff -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/Views/Device/Success.cshtml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Api/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "IncludeScopes": false, 4 | "LogLevel": { 5 | "Default": "Debug", 6 | "System": "Information", 7 | "Microsoft": "Information" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/wwwroot/lib/bootstrap/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tricklebyte/BlazorId_App/HEAD/IdentityServerAspNetIdentity/wwwroot/lib/bootstrap/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/wwwroot/lib/bootstrap/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tricklebyte/BlazorId_App/HEAD/IdentityServerAspNetIdentity/wwwroot/lib/bootstrap/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/wwwroot/lib/bootstrap/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tricklebyte/BlazorId_App/HEAD/IdentityServerAspNetIdentity/wwwroot/lib/bootstrap/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/wwwroot/lib/bootstrap/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tricklebyte/BlazorId_App/HEAD/IdentityServerAspNetIdentity/wwwroot/lib/bootstrap/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/Views/Account/AccessDenied.cshtml: -------------------------------------------------------------------------------- 1 | 2 |
3 | 6 | 7 |

You do not have access to that resource.

8 |
-------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/wwwroot/js/signout-redirect.js: -------------------------------------------------------------------------------- 1 | window.addEventListener("load", function () { 2 | var a = document.querySelector("a.PostLogoutRedirectUri"); 3 | if (a) { 4 | window.location = a.href; 5 | } 6 | }); 7 | -------------------------------------------------------------------------------- /BlazorId_App/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*" 10 | } 11 | -------------------------------------------------------------------------------- /BlazorId_App/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "DetailedErrors": true, 3 | "Logging": { 4 | "LogLevel": { 5 | "Default": "Information", 6 | "Microsoft": "Warning", 7 | "Microsoft.Hosting.Lifetime": "Information" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/Views/Shared/_ValidationSummary.cshtml: -------------------------------------------------------------------------------- 1 | @if (ViewContext.ModelState.IsValid == false) 2 | { 3 |
4 | Error 5 |
6 |
7 | } -------------------------------------------------------------------------------- /BlazorId_Shared/BlazorId_Shared.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/Models/ApplicationUser.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Identity; 2 | 3 | namespace IdentityServerAspNetIdentity.Models 4 | { 5 | // Add profile data for application users by adding properties to the ApplicationUser class 6 | public class ApplicationUser : IdentityUser 7 | { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/Views/Shared/Redirect.cshtml: -------------------------------------------------------------------------------- 1 | @model RedirectViewModel 2 | 3 |

You are now being returned to the application.

4 |

Once complete, you may close this tab

5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /BlazorId_App/Pages/Counter.razor: -------------------------------------------------------------------------------- 1 | @page "/counter" 2 | 3 |

Counter

4 | 5 |

Current count: @currentCount

6 | 7 | 8 | 9 | @code { 10 | private int currentCount = 0; 11 | 12 | private void IncrementCount() 13 | { 14 | currentCount++; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /BlazorId_App/Shared/MainLayout.razor: -------------------------------------------------------------------------------- 1 | @inherits LayoutComponentBase 2 | 3 | 6 | 7 |
8 |
9 | About 10 |
11 | 12 |
13 | @Body 14 |
15 |
16 | -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/Quickstart/Account/LogoutInputModel.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Brock Allen & Dominick Baier. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | 5 | namespace IdentityServer4.Quickstart.UI 6 | { 7 | public class LogoutInputModel 8 | { 9 | public string LogoutId { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/Quickstart/Account/RedirectViewModel.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Brock Allen & Dominick Baier. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | 5 | 6 | namespace IdentityServer4.Quickstart.UI 7 | { 8 | public class RedirectViewModel 9 | { 10 | public string RedirectUrl { get; set; } 11 | } 12 | } -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/Views/Device/UserCodeCapture.cshtml: -------------------------------------------------------------------------------- 1 | @model string 2 | 3 | -------------------------------------------------------------------------------- /BlazorId_App/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using System.Net.Http 2 | @using Microsoft.AspNetCore.Authorization 3 | @using Microsoft.AspNetCore.Components.Authorization 4 | @using Microsoft.AspNetCore.Components.Forms 5 | @using Microsoft.AspNetCore.Components.Routing 6 | @using Microsoft.AspNetCore.Components.Web 7 | @using Microsoft.JSInterop 8 | @using BlazorId_App 9 | @using BlazorId_App.Shared 10 | @using BlazorId_Shared 11 | @using System.Security.Claims 12 | -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/Quickstart/Account/LogoutViewModel.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Brock Allen & Dominick Baier. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | 5 | namespace IdentityServer4.Quickstart.UI 6 | { 7 | public class LogoutViewModel : LogoutInputModel 8 | { 9 | public bool ShowLogoutPrompt { get; set; } = true; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/Quickstart/Device/DeviceAuthorizationInputModel.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Brock Allen & Dominick Baier. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | 5 | namespace IdentityServer4.Quickstart.UI.Device 6 | { 7 | public class DeviceAuthorizationInputModel : ConsentInputModel 8 | { 9 | public string UserCode { get; set; } 10 | } 11 | } -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/Quickstart/Account/ExternalProvider.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Brock Allen & Dominick Baier. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | 5 | namespace IdentityServer4.Quickstart.UI 6 | { 7 | public class ExternalProvider 8 | { 9 | public string DisplayName { get; set; } 10 | public string AuthenticationScheme { get; set; } 11 | } 12 | } -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/Quickstart/Device/DeviceAuthorizationViewModel.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Brock Allen & Dominick Baier. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | 5 | namespace IdentityServer4.Quickstart.UI.Device 6 | { 7 | public class DeviceAuthorizationViewModel : ConsentViewModel 8 | { 9 | public string UserCode { get; set; } 10 | public bool ConfirmUserCode { get; set; } 11 | } 12 | } -------------------------------------------------------------------------------- /Api/Api.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /BlazorId_Shared/Policies/Policies.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Authorization; 2 | using System; 3 | 4 | namespace BlazorId_Shared 5 | { 6 | public class Policies 7 | { 8 | public const string CanViewIdentity = "CanViewIdentity"; 9 | 10 | public static AuthorizationPolicy CanViewIdentityPolicy() 11 | { 12 | return new AuthorizationPolicyBuilder() 13 | .RequireAuthenticatedUser() 14 | .RequireClaim("appuser_claim", "identity") 15 | .Build(); 16 | } 17 | 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /BlazorId_App/Shared/SurveyPrompt.razor: -------------------------------------------------------------------------------- 1 | 11 | 12 | @code { 13 | // Demonstrates how a parent component can supply parameters 14 | [Parameter] 15 | public string Title { get; set; } 16 | } 17 | -------------------------------------------------------------------------------- /BlazorId_App/BlazorId_App.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /BlazorId_App/Pages/Identity-Api.razor: -------------------------------------------------------------------------------- 1 | @page "/identityapi" 2 | 3 | @using BlazorId_App.Data 4 | @inject IdentityDataService IdentityService 5 | 6 |

User Claims from API

7 | 8 |

These are the user claims that were presented to the API

9 | 10 | 11 | @if (userClaimsJson == null) 12 | { 13 |

Loading...

14 | } 15 | else 16 | { 17 |
 @userClaimsJson 
18 | } 19 | 20 | @code { 21 | private string userClaimsJson; 22 | 23 | protected override async Task OnInitializedAsync() 24 | { 25 | userClaimsJson = await IdentityService.GetAPIUserClaimsJson(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/Quickstart/Consent/ConsentInputModel.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Brock Allen & Dominick Baier. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | 5 | using System.Collections.Generic; 6 | 7 | namespace IdentityServer4.Quickstart.UI 8 | { 9 | public class ConsentInputModel 10 | { 11 | public string Button { get; set; } 12 | public IEnumerable ScopesConsented { get; set; } 13 | public bool RememberConsent { get; set; } 14 | public string ReturnUrl { get; set; } 15 | } 16 | } -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/Quickstart/Consent/ScopeViewModel.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Brock Allen & Dominick Baier. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | 5 | namespace IdentityServer4.Quickstart.UI 6 | { 7 | public class ScopeViewModel 8 | { 9 | public string Name { get; set; } 10 | public string DisplayName { get; set; } 11 | public string Description { get; set; } 12 | public bool Emphasize { get; set; } 13 | public bool Required { get; set; } 14 | public bool Checked { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/Quickstart/Account/LoginInputModel.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Brock Allen & Dominick Baier. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | 5 | using System.ComponentModel.DataAnnotations; 6 | 7 | namespace IdentityServer4.Quickstart.UI 8 | { 9 | public class LoginInputModel 10 | { 11 | [Required] 12 | public string Username { get; set; } 13 | [Required] 14 | public string Password { get; set; } 15 | public bool RememberLogin { get; set; } 16 | public string ReturnUrl { get; set; } 17 | } 18 | } -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/Quickstart/Home/ErrorViewModel.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Brock Allen & Dominick Baier. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | 5 | using IdentityServer4.Models; 6 | 7 | namespace IdentityServer4.Quickstart.UI 8 | { 9 | public class ErrorViewModel 10 | { 11 | public ErrorViewModel() 12 | { 13 | } 14 | 15 | public ErrorViewModel(string error) 16 | { 17 | Error = new ErrorMessage { Error = error }; 18 | } 19 | 20 | public ErrorMessage Error { get; set; } 21 | } 22 | } -------------------------------------------------------------------------------- /Api/Program.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Brock Allen & Dominick Baier. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | using System; 5 | using Microsoft.AspNetCore; 6 | using Microsoft.AspNetCore.Hosting; 7 | 8 | namespace Api 9 | { 10 | public class Program 11 | { 12 | public static void Main(string[] args) 13 | { 14 | Console.Title = "API"; 15 | 16 | BuildWebHost(args).Run(); 17 | } 18 | 19 | public static IWebHost BuildWebHost(string[] args) => 20 | WebHost.CreateDefaultBuilder(args) 21 | .UseStartup() 22 | .Build(); 23 | } 24 | } -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/Views/Account/Logout.cshtml: -------------------------------------------------------------------------------- 1 | @model LogoutViewModel 2 | 3 |
4 | 7 | 8 |
9 |
10 |

Would you like to logout of IdentityServer?

11 |
12 | 13 |
14 |
15 | 16 |
17 |
18 |
19 |
20 |
21 |
-------------------------------------------------------------------------------- /Api/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "https://localhost:44328/", 7 | "sslPort": 44328 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "environmentVariables": { 14 | "ASPNETCORE_ENVIRONMENT": "Development" 15 | }, 16 | "applicationUrl": "http://localhost:5001" 17 | }, 18 | "Api": { 19 | "commandName": "Project", 20 | "environmentVariables": { 21 | "ASPNETCORE_ENVIRONMENT": "Development" 22 | }, 23 | "applicationUrl": "http://localhost:5001" 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /BlazorId_App/Pages/Identity-App.razor: -------------------------------------------------------------------------------- 1 | @page "/identityapp" 2 | 3 | @using BlazorId_App.Data 4 | @inject IdentityDataService IdentityService 5 | @attribute [Authorize(Policy=BlazorId_Shared.Policies.CanViewIdentity)] 6 | 7 |

User Claims from APP

8 | 9 |

These are the user claims that were presented to the Application

10 | 11 | 12 | @if (userClaimsJson == null) 13 | { 14 |

Loading...

15 | } 16 | else 17 | { 18 |
 @userClaimsJson 
19 | } 20 | 21 | @code { 22 | private string userClaimsJson; 23 | 24 | protected override async Task OnInitializedAsync() 25 | { 26 | string claimsJson = 27 | userClaimsJson = await IdentityService.GetAPPUserClaimsJson(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/Views/Diagnostics/Index.cshtml: -------------------------------------------------------------------------------- 1 | @model DiagnosticsViewModel 2 | 3 |

Authentication cookie

4 | 5 |

Claims

6 |
7 | @foreach (var claim in Model.AuthenticateResult.Principal.Claims) 8 | { 9 |
@claim.Type
10 |
@claim.Value
11 | } 12 |
13 | 14 |

Properties

15 |
16 | @foreach (var prop in Model.AuthenticateResult.Properties.Items) 17 | { 18 |
@prop.Key
19 |
@prop.Value
20 | } 21 |
22 | 23 | @if (Model.Clients.Any()) 24 | { 25 |

Clients

26 |
    27 | @foreach (var client in Model.Clients) 28 | { 29 |
  • @client
  • 30 | } 31 |
32 | } -------------------------------------------------------------------------------- /Api/Controllers/IdentityController.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Brock Allen & Dominick Baier. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | using System.Linq; 5 | using Microsoft.AspNetCore.Mvc; 6 | using Microsoft.AspNetCore.Authorization; 7 | using BlazorId_Shared; 8 | 9 | namespace Api.Controllers 10 | { 11 | [Route("identity")] 12 | [Authorize] 13 | public class IdentityController : ControllerBase 14 | { 15 | [HttpGet] 16 | [Authorize(Policy = Policies.CanViewIdentity)] 17 | public IActionResult Get() 18 | { 19 | return new JsonResult(from c in User.Claims select new { c.Type, c.Value }); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /BlazorId_App/Pages/Error.razor: -------------------------------------------------------------------------------- 1 | @page "/error" 2 | 3 | 4 |

Error.

5 |

An error occurred while processing your request.

6 | 7 |

Development Mode

8 |

9 | Swapping to Development environment will display more detailed information about the error that occurred. 10 |

11 |

12 | The Development environment shouldn't be enabled for deployed applications. 13 | It can result in displaying sensitive information from exceptions to end users. 14 | For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development 15 | and restarting the app. 16 |

-------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/Quickstart/Consent/ProcessConsentResult.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Brock Allen & Dominick Baier. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | 5 | namespace IdentityServer4.Quickstart.UI 6 | { 7 | public class ProcessConsentResult 8 | { 9 | public bool IsRedirect => RedirectUri != null; 10 | public string RedirectUri { get; set; } 11 | public string ClientId { get; set; } 12 | 13 | public bool ShowView => ViewModel != null; 14 | public ConsentViewModel ViewModel { get; set; } 15 | 16 | public bool HasValidationError => ValidationError != null; 17 | public string ValidationError { get; set; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /BlazorId_App/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:53108", 7 | "sslPort": 44321 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "BlazorId_App": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "applicationUrl": "https://localhost:5001;http://localhost:5000", 22 | "environmentVariables": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/Quickstart/Consent/ConsentViewModel.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Brock Allen & Dominick Baier. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | 5 | using System.Collections.Generic; 6 | 7 | namespace IdentityServer4.Quickstart.UI 8 | { 9 | public class ConsentViewModel : ConsentInputModel 10 | { 11 | public string ClientName { get; set; } 12 | public string ClientUrl { get; set; } 13 | public string ClientLogoUrl { get; set; } 14 | public bool AllowRememberConsent { get; set; } 15 | 16 | public IEnumerable IdentityScopes { get; set; } 17 | public IEnumerable ResourceScopes { get; set; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /BlazorId_App/App.razor: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |

Sorry, you're not authorized to view this page.

7 |

You may want to try logging in (as someone with the necessary authorization).

8 |
9 |
10 |
11 | 12 | 13 |

Sorry, there's nothing at this address.

14 |
15 |
16 |
17 |
-------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/Quickstart/Account/LoggedOutViewModel.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Brock Allen & Dominick Baier. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | 5 | namespace IdentityServer4.Quickstart.UI 6 | { 7 | public class LoggedOutViewModel 8 | { 9 | public string PostLogoutRedirectUri { get; set; } 10 | public string ClientName { get; set; } 11 | public string SignOutIframeUrl { get; set; } 12 | 13 | public bool AutomaticRedirectAfterSignOut { get; set; } 14 | 15 | public string LogoutId { get; set; } 16 | public bool TriggerExternalSignout => ExternalAuthenticationScheme != null; 17 | public string ExternalAuthenticationScheme { get; set; } 18 | } 19 | } -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "https://localhost:44387/", 7 | "sslPort": 44387 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "launchUrl": "https://localhost:44387/", 15 | "environmentVariables": { 16 | "ASPNETCORE_ENVIRONMENT": "Development" 17 | } 18 | }, 19 | "SelfHost": { 20 | "commandName": "Project", 21 | "launchBrowser": true, 22 | "environmentVariables": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | }, 25 | "applicationUrl": "http://localhost:5000" 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/Quickstart/Consent/ConsentOptions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Brock Allen & Dominick Baier. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | 5 | namespace IdentityServer4.Quickstart.UI 6 | { 7 | public class ConsentOptions 8 | { 9 | public static bool EnableOfflineAccess = true; 10 | public static string OfflineAccessDisplayName = "Offline Access"; 11 | public static string OfflineAccessDescription = "Access to your applications and resources, even when you are offline"; 12 | 13 | public static readonly string MustChooseOneErrorMessage = "You must pick at least one permission"; 14 | public static readonly string InvalidSelectionErrorMessage = "Invalid selection"; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /BlazorId_App/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore; 7 | using Microsoft.AspNetCore.Hosting; 8 | using Microsoft.Extensions.Configuration; 9 | using Microsoft.Extensions.Hosting; 10 | using Microsoft.Extensions.Logging; 11 | 12 | namespace BlazorId_App 13 | { 14 | public class Program 15 | { 16 | public static void Main(string[] args) 17 | { 18 | CreateHostBuilder(args).Build().Run(); 19 | } 20 | 21 | public static IHostBuilder CreateHostBuilder(string[] args) => 22 | Host.CreateDefaultBuilder(args) 23 | .ConfigureWebHostDefaults(webBuilder => 24 | { 25 | webBuilder.UseStartup(); 26 | }); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/Quickstart/Extensions.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using IdentityServer4.Stores; 3 | 4 | namespace IdentityServer4.Quickstart.UI 5 | { 6 | public static class Extensions 7 | { 8 | /// 9 | /// Determines whether the client is configured to use PKCE. 10 | /// 11 | /// The store. 12 | /// The client identifier. 13 | /// 14 | public static async Task IsPkceClientAsync(this IClientStore store, string client_id) 15 | { 16 | if (!string.IsNullOrWhiteSpace(client_id)) 17 | { 18 | var client = await store.FindEnabledClientByIdAsync(client_id); 19 | return client?.RequirePkce == true; 20 | } 21 | 22 | return false; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/Data/ApplicationDbContext.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Identity.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore; 3 | using IdentityServerAspNetIdentity.Models; 4 | 5 | namespace IdentityServerAspNetIdentity.Data 6 | { 7 | public class ApplicationDbContext : IdentityDbContext 8 | { 9 | public ApplicationDbContext(DbContextOptions options) 10 | : base(options) 11 | { 12 | } 13 | 14 | protected override void OnModelCreating(ModelBuilder builder) 15 | { 16 | base.OnModelCreating(builder); 17 | // Customize the ASP.NET Identity model and override the defaults if needed. 18 | // For example, you can rename the ASP.NET Identity table names and more. 19 | // Add your customizations after calling base.OnModelCreating(builder); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/Quickstart/Grants/GrantsViewModel.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Brock Allen & Dominick Baier. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | 5 | using System; 6 | using System.Collections.Generic; 7 | 8 | namespace IdentityServer4.Quickstart.UI 9 | { 10 | public class GrantsViewModel 11 | { 12 | public IEnumerable Grants { get; set; } 13 | } 14 | 15 | public class GrantViewModel 16 | { 17 | public string ClientId { get; set; } 18 | public string ClientName { get; set; } 19 | public string ClientUrl { get; set; } 20 | public string ClientLogoUrl { get; set; } 21 | public DateTime Created { get; set; } 22 | public DateTime? Expires { get; set; } 23 | public IEnumerable IdentityGrantNames { get; set; } 24 | public IEnumerable ApiGrantNames { get; set; } 25 | } 26 | } -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/Views/Account/LoggedOut.cshtml: -------------------------------------------------------------------------------- 1 | @model LoggedOutViewModel 2 | 3 | @{ 4 | // set this so the layout rendering sees an anonymous user 5 | ViewData["signed-out"] = true; 6 | } 7 | 8 | 27 | 28 | @section scripts 29 | { 30 | @if (Model.AutomaticRedirectAfterSignOut) 31 | { 32 | 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/Quickstart/Account/LoginViewModel.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Brock Allen & Dominick Baier. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | 9 | namespace IdentityServer4.Quickstart.UI 10 | { 11 | public class LoginViewModel : LoginInputModel 12 | { 13 | public bool AllowRememberLogin { get; set; } = true; 14 | public bool EnableLocalLogin { get; set; } = true; 15 | 16 | public IEnumerable ExternalProviders { get; set; } = Enumerable.Empty(); 17 | public IEnumerable VisibleExternalProviders => ExternalProviders.Where(x => !String.IsNullOrWhiteSpace(x.DisplayName)); 18 | 19 | public bool IsExternalLoginOnly => EnableLocalLogin == false && ExternalProviders?.Count() == 1; 20 | public string ExternalLoginScheme => IsExternalLoginOnly ? ExternalProviders?.SingleOrDefault()?.AuthenticationScheme : null; 21 | } 22 | } -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/Views/Shared/_ScopeListItem.cshtml: -------------------------------------------------------------------------------- 1 | @model ScopeViewModel 2 | 3 |
  • 4 | 24 | @if (Model.Required) 25 | { 26 | (required) 27 | } 28 | @if (Model.Description != null) 29 | { 30 | 33 | } 34 |
  • -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/Quickstart/Diagnostics/DiagnosticsController.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Brock Allen & Dominick Baier. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | using Microsoft.AspNetCore.Authentication; 8 | using Microsoft.AspNetCore.Authorization; 9 | using Microsoft.AspNetCore.Mvc; 10 | 11 | namespace IdentityServer4.Quickstart.UI 12 | { 13 | [SecurityHeaders] 14 | [Authorize] 15 | public class DiagnosticsController : Controller 16 | { 17 | public async Task Index() 18 | { 19 | var localAddresses = new string[] { "127.0.0.1", "::1", HttpContext.Connection.LocalIpAddress.ToString() }; 20 | if (!localAddresses.Contains(HttpContext.Connection.RemoteIpAddress.ToString())) 21 | { 22 | return NotFound(); 23 | } 24 | 25 | var model = new DiagnosticsViewModel(await HttpContext.AuthenticateAsync()); 26 | return View(model); 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /BlazorId_App/wwwroot/css/open-iconic/ICON-LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Waybury 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/Quickstart/Account/AccountOptions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Brock Allen & Dominick Baier. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | 5 | using System; 6 | 7 | namespace IdentityServer4.Quickstart.UI 8 | { 9 | public class AccountOptions 10 | { 11 | public static bool AllowLocalLogin = true; 12 | public static bool AllowRememberLogin = true; 13 | public static TimeSpan RememberMeLoginDuration = TimeSpan.FromDays(30); 14 | 15 | public static bool ShowLogoutPrompt = true; 16 | public static bool AutomaticRedirectAfterSignOut = false; 17 | 18 | // specify the Windows authentication scheme being used 19 | public static readonly string WindowsAuthenticationSchemeName = Microsoft.AspNetCore.Server.IISIntegration.IISDefaults.AuthenticationScheme; 20 | // if user uses windows auth, should we load the groups from windows 21 | public static bool IncludeWindowsGroups = false; 22 | 23 | public static string InvalidCredentialsErrorMessage = "Invalid username or password"; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/Views/Shared/Error.cshtml: -------------------------------------------------------------------------------- 1 | @model ErrorViewModel 2 | 3 | @{ 4 | var error = Model?.Error?.Error; 5 | var errorDescription = Model?.Error?.ErrorDescription; 6 | var request_id = Model?.Error?.RequestId; 7 | } 8 | 9 |
    10 | 13 | 14 |
    15 |
    16 |
    17 | Sorry, there was an error 18 | 19 | @if (error != null) 20 | { 21 | 22 | 23 | : @error 24 | 25 | 26 | 27 | if (errorDescription != null) 28 | { 29 |
    @errorDescription
    30 | } 31 | } 32 |
    33 | 34 | @if (request_id != null) 35 | { 36 |
    Request Id: @request_id
    37 | } 38 |
    39 |
    40 |
    41 | -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/wwwroot/css/site.min.css: -------------------------------------------------------------------------------- 1 | body{margin-top:65px;}.navbar-header{position:relative;top:-4px;}.navbar-brand>.icon-banner{position:relative;top:-2px;display:inline;}.icon{position:relative;top:-10px;}.logged-out iframe{display:none;width:0;height:0;}.page-consent .client-logo{float:left;}.page-consent .client-logo img{width:80px;height:80px;}.page-consent .consent-buttons{margin-top:25px;}.page-consent .consent-form .consent-scopecheck{display:inline-block;margin-right:5px;}.page-consent .consent-form .consent-description{margin-left:25px;}.page-consent .consent-form .consent-description label{font-weight:normal;}.page-consent .consent-form .consent-remember{padding-left:16px;}.grants .page-header{margin-bottom:10px;}.grants .grant{margin-top:20px;padding-bottom:20px;border-bottom:1px solid #d3d3d3;}.grants .grant img{width:100px;height:100px;}.grants .grant .clientname{font-size:140%;font-weight:bold;}.grants .grant .granttype{font-size:120%;font-weight:bold;}.grants .grant .created{font-size:120%;font-weight:bold;}.grants .grant .expires{font-size:120%;font-weight:bold;}.grants .grant li{list-style-type:none;display:inline;}.grants .grant li:after{content:', ';}.grants .grant li:last-child:after{content:'';} -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/IdentityServerAspNetIdentity.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/Quickstart/Diagnostics/DiagnosticsViewModel.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Brock Allen & Dominick Baier. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | 5 | using IdentityModel; 6 | using Microsoft.AspNetCore.Authentication; 7 | using Newtonsoft.Json; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | 11 | namespace IdentityServer4.Quickstart.UI 12 | { 13 | public class DiagnosticsViewModel 14 | { 15 | public DiagnosticsViewModel(AuthenticateResult result) 16 | { 17 | AuthenticateResult = result; 18 | 19 | if (result.Properties.Items.ContainsKey("client_list")) 20 | { 21 | var encoded = result.Properties.Items["client_list"]; 22 | var bytes = Base64Url.Decode(encoded); 23 | var value = Encoding.UTF8.GetString(bytes); 24 | 25 | Clients = JsonConvert.DeserializeObject(value); 26 | } 27 | } 28 | 29 | public AuthenticateResult AuthenticateResult { get; } 30 | public IEnumerable Clients { get; } = new List(); 31 | } 32 | } -------------------------------------------------------------------------------- /BlazorId_App/Pages/_Host.cshtml: -------------------------------------------------------------------------------- 1 | @page "/" 2 | @namespace BlazorId_App.Pages 3 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 4 | @{ 5 | Layout = null; 6 | } 7 | 8 | 9 | 10 | 11 | 12 | 13 | BlazorId_App 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
    24 | 25 | An error has occurred. This application may no longer respond until reloaded. 26 | 27 | 28 | An unhandled exception has occurred. See browser dev tools for details. 29 | 30 | Reload 31 | 🗙 32 |
    33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/wwwroot/lib/jquery-validation/build/release.js: -------------------------------------------------------------------------------- 1 | /* Release checklist 2 | - Run `git changelog` and edit to match previous output (this should make use of jquey-release instead) 3 | - pull latest https://github.com/jquery/jquery-release 4 | - run 5 | node release.js --remote=jzaefferer/jquery-validation 6 | - Wait a while, verify and confirm each step 7 | - 8 | */ 9 | 10 | /*jshint node:true */ 11 | module.exports = function( Release ) { 12 | 13 | function today() { 14 | return new Date().toISOString().replace(/T.+/, ""); 15 | } 16 | 17 | // also add version property to this 18 | Release._jsonFiles.push( "validation.jquery.json" ); 19 | 20 | Release.define({ 21 | issueTracker: "github", 22 | changelogShell: function() { 23 | return Release.newVersion + " / " + today() + "\n==================\n\n"; 24 | }, 25 | 26 | generateArtifacts: function( done ) { 27 | Release.exec( "grunt release", "Grunt command failed" ); 28 | done([ 29 | "dist/additional-methods.js", 30 | "dist/additional-methods.min.js", 31 | "dist/jquery.validate.js", 32 | "dist/jquery.validate.min.js" 33 | ]); 34 | }, 35 | 36 | // disable CDN publishing 37 | cdnPublish: false, 38 | 39 | // disable authors check 40 | _checkAuthorsTxt: function() {} 41 | }); 42 | 43 | }; 44 | -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/Views/Home/Index.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | var version = typeof(IdentityServer4.Hosting.IdentityServerMiddleware).Assembly.GetName().Version.ToString(); 3 | } 4 | 5 |
    6 | 15 | 16 |
    17 |
    18 |

    19 | IdentityServer publishes a 20 | discovery document 21 | where you can find metadata and links to all the endpoints, key material, etc. 22 |

    23 |
    24 |
    25 |

    26 | Click here to manage your stored grants. 27 |

    28 |
    29 |
    30 |
    31 |
    32 |

    33 | Here are links to the 34 | source code repository, 35 | and ready to use samples. 36 |

    37 |
    38 |
    39 |
    40 | -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/tempkey.rsa: -------------------------------------------------------------------------------- 1 | {"KeyId":"MdtEZy89b24Hoj8cq76gNQ","Parameters":{"D":"Vc89LuLiAgnDs+YNMpEKDRHnPEKf7IjW4SwNnDrr5HRN0GbFBdsHHemkjaSE/O02w6r9YA0zExF5B0d++7yh9VA40ZrFBiSDLNFrx2WC6Dtbg+h0VdM2JKV1itGxJu62pfvbrkKUWnZeitNRwuteoemnflWYGo7jG2i9KNLofec5XN7FuGb42krM6KZvU5MmJtLddCAHdYwmx6srwEB+lwIdjkfdra3AUgK+nC51xGR9DS0K6JNcfcAuee4CgQdwy2VEYKqH7NcJJu3LzEy09TP21Pa5iUSnvcJk2JvJ9IbIb+4NXVAZ+nrXqGMEx3zUaLtK4mP0UZnSwSr20STsQQ==","DP":"wEnMMzPKsaBDNaLgOfbKgLm/bOV4A3RqnvznUp5HYRrVRL0ByHIMwCCFxy3UeWNUnXcfsJnjkvBAxq24QZqDUkXvqLnJ17/1bbx7ft1kit6mT+sbL+GcC5kuHAc4gi/UCFON++qHjiDNQZ4J8BPIGQH8olEtlu18uJCo2oygZEU=","DQ":"EzkTWJRn6dK9w9o3VH22RUgj35DluVbRV10Mmn+JFk7F+GsgtRd24Y4ExQ09eNmQ1xk6NqNk+twSSN1FmWXSsv8VswXWzTuIeuST9sWsa4tk6XxwS3Km1/MMKumhEF8B37PHOMrLrloCMdzFLeT1McsHXvBNSGTb1JE823Iherc=","Exponent":"AQAB","InverseQ":"KpdJO8ennMi6bthI/QdTJH+HqnzO08JIgl2LoWichuDQwmRs9cjo9nzUaP0N08CQXE8PFMmwTlWlkNtQZA8MhR+w4ESK2iJSWUKBIxIyNtSba5Xc6LJnQbtZ+bqKa+Qj6jzFaLcfPBcIp9DkJEnghuWKo5eD8SmIm27eVxOIw+A=","Modulus":"taTexvq3JL6PIQX2adNBNUiJLf1SjE9od5+LHZLJnqEHDQDWsc+vgN3BN8XxMcNOEGtvnrb/E3xUWOjmOnCzSEDxmCyjy2xuN3i6e7X/UeESpe/LmFbjOT/aS//3eMnekSdOiZPM6GgxwSxKzQp26j0zRBw7qx8SNc/IDNVpDX+xwAF2BQ5pGzVd09gHhwVLirqVN0+IgK86tGmYaigd4dljUuCGHPokmG6OeICdRIzGw0enoF1td4zXhEB6x2oUlm2dkNmNIacN563H4UkYeuozSgvC55bvecy+dEmUqyYfOM/FMGdsb5g7IMZJYeOb6wdXInL5BHxZTkDjUMNGLQ==","P":"2xDr87evNYxxIa3gSHG6/BgIE1FTiJzsBCCsqgVnCUr6XTtrvrBNjJ8zHPySY5f/7q3Gaecvyh3eQ1VJfKlOWmElj/ONDN51vbN7Aiqu7Zl0qv/TsI8rlxsyteSW4nGK2skxdO0DZNnlHS5XjZE6M+Xt6ngi2qi2hOfgpkhNMAc=","Q":"1ETHsgDMNLzosK0EpVWRRl6DX5MvgCyhPhYrX4w16SILcFM1+FRea8mUNhckc7ri07RQOS2/p7T/DFw9ibOoNvF+biTqQ3nXTMfrcLGBJwc5GqwCPPIqpGwDMLCAzEkUWMExleQn577OFqTRDkiDEsXczSGGTlZdJQMJpfqa4ys="}} -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/wwwroot/css/site.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin-top: 65px; 3 | } 4 | .navbar-header { 5 | position: relative; 6 | top: -4px; 7 | } 8 | .navbar-brand > .icon-banner { 9 | position: relative; 10 | top: -2px; 11 | display: inline; 12 | } 13 | .icon { 14 | position: relative; 15 | top: -10px; 16 | } 17 | .logged-out iframe { 18 | display: none; 19 | width: 0; 20 | height: 0; 21 | } 22 | .page-consent .client-logo { 23 | float: left; 24 | } 25 | .page-consent .client-logo img { 26 | width: 80px; 27 | height: 80px; 28 | } 29 | .page-consent .consent-buttons { 30 | margin-top: 25px; 31 | } 32 | .page-consent .consent-form .consent-scopecheck { 33 | display: inline-block; 34 | margin-right: 5px; 35 | } 36 | .page-consent .consent-form .consent-description { 37 | margin-left: 25px; 38 | } 39 | .page-consent .consent-form .consent-description label { 40 | font-weight: normal; 41 | } 42 | .page-consent .consent-form .consent-remember { 43 | padding-left: 16px; 44 | } 45 | .grants .page-header { 46 | margin-bottom: 10px; 47 | } 48 | .grants .grant { 49 | margin-top: 20px; 50 | padding-bottom: 20px; 51 | border-bottom: 1px solid lightgray; 52 | } 53 | .grants .grant img { 54 | width: 100px; 55 | height: 100px; 56 | } 57 | .grants .grant .clientname { 58 | font-size: 140%; 59 | font-weight: bold; 60 | } 61 | .grants .grant .granttype { 62 | font-size: 120%; 63 | font-weight: bold; 64 | } 65 | .grants .grant .created { 66 | font-size: 120%; 67 | font-weight: bold; 68 | } 69 | .grants .grant .expires { 70 | font-size: 120%; 71 | font-weight: bold; 72 | } 73 | .grants .grant li { 74 | list-style-type: none; 75 | display: inline; 76 | } 77 | .grants .grant li:after { 78 | content: ', '; 79 | } 80 | .grants .grant li:last-child:after { 81 | content: ''; 82 | } -------------------------------------------------------------------------------- /Api/Startup.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Brock Allen & Dominick Baier. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | using Microsoft.AspNetCore.Builder; 5 | using Microsoft.Extensions.DependencyInjection; 6 | 7 | namespace Api 8 | { 9 | public class Startup 10 | { 11 | public void ConfigureServices(IServiceCollection services) 12 | { 13 | services.AddControllers() 14 | .AddNewtonsoftJson(); 15 | 16 | services.AddAuthentication("Bearer") 17 | .AddJwtBearer("Bearer", options => 18 | { 19 | options.Authority = "https://localhost:44387"; 20 | options.RequireHttpsMetadata = false; 21 | 22 | options.Audience = "identityApi"; 23 | }); 24 | 25 | services.AddCors(options => 26 | { 27 | // this defines a CORS policy called "default" 28 | options.AddPolicy("default", policy => 29 | { 30 | policy.WithOrigins("https://localhost:44321") 31 | .AllowAnyHeader() 32 | .AllowAnyMethod(); 33 | }); 34 | }); 35 | 36 | services.AddAuthorization(authorizationOptions => 37 | { 38 | authorizationOptions.AddPolicy( 39 | BlazorId_Shared.Policies.CanViewIdentity, 40 | BlazorId_Shared.Policies.CanViewIdentityPolicy()); 41 | }); 42 | } 43 | 44 | public void Configure(IApplicationBuilder app) 45 | { 46 | app.UseRouting(); 47 | 48 | app.UseCors("default"); 49 | 50 | app.UseAuthentication(); 51 | app.UseAuthorization(); 52 | 53 | app.UseEndpoints(endpoints => 54 | { 55 | endpoints.MapControllers(); 56 | }); 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/Quickstart/Home/HomeController.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Brock Allen & Dominick Baier. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | 5 | using IdentityServer4.Services; 6 | using Microsoft.AspNetCore.Authorization; 7 | using Microsoft.AspNetCore.Hosting; 8 | using Microsoft.AspNetCore.Mvc; 9 | using Microsoft.Extensions.Hosting; 10 | using Microsoft.Extensions.Logging; 11 | using System.Threading.Tasks; 12 | 13 | namespace IdentityServer4.Quickstart.UI 14 | { 15 | [SecurityHeaders] 16 | [AllowAnonymous] 17 | public class HomeController : Controller 18 | { 19 | private readonly IIdentityServerInteractionService _interaction; 20 | private readonly IWebHostEnvironment _environment; 21 | private readonly ILogger _logger; 22 | 23 | public HomeController(IIdentityServerInteractionService interaction, IWebHostEnvironment environment, ILogger logger) 24 | { 25 | _interaction = interaction; 26 | _environment = environment; 27 | _logger = logger; 28 | } 29 | 30 | public IActionResult Index() 31 | { 32 | if (_environment.IsDevelopment()) 33 | { 34 | // only show in development 35 | return View(); 36 | } 37 | 38 | _logger.LogInformation("Homepage is disabled in production. Returning 404."); 39 | return NotFound(); 40 | } 41 | 42 | /// 43 | /// Shows the error page 44 | /// 45 | public async Task Error(string errorId) 46 | { 47 | var vm = new ErrorViewModel(); 48 | 49 | // retrieve error details from identityserver 50 | var message = await _interaction.GetErrorContextAsync(errorId); 51 | if (message != null) 52 | { 53 | vm.Error = message; 54 | 55 | if (!_environment.IsDevelopment()) 56 | { 57 | // only show in development 58 | message.ErrorDescription = null; 59 | } 60 | } 61 | 62 | return View("Error", vm); 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /BlazorId_App/Shared/NavMenu.razor: -------------------------------------------------------------------------------- 1 | 7 | 8 |
    9 | 47 |
    48 | 49 | @code { 50 | private bool collapseNavMenu = true; 51 | 52 | private string NavMenuCssClass => collapseNavMenu ? "collapse" : null; 53 | 54 | private void ToggleNavMenu() 55 | { 56 | collapseNavMenu = !collapseNavMenu; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/wwwroot/css/site.less: -------------------------------------------------------------------------------- 1 | body { 2 | margin-top: 65px; 3 | } 4 | 5 | .navbar-header { 6 | position: relative; 7 | top: -4px; 8 | } 9 | 10 | .navbar-brand > .icon-banner { 11 | position: relative; 12 | top: -2px; 13 | display: inline; 14 | } 15 | 16 | .icon { 17 | position: relative; 18 | top: -10px; 19 | } 20 | 21 | .logged-out iframe { 22 | display: none; 23 | width: 0; 24 | height: 0; 25 | } 26 | 27 | .page-consent { 28 | .client-logo { 29 | float: left; 30 | 31 | img { 32 | width: 80px; 33 | height: 80px; 34 | } 35 | } 36 | 37 | .consent-buttons { 38 | margin-top: 25px; 39 | } 40 | 41 | .consent-form { 42 | .consent-scopecheck { 43 | display: inline-block; 44 | margin-right: 5px; 45 | } 46 | 47 | .consent-scopecheck[disabled] { 48 | //visibility:hidden; 49 | } 50 | 51 | .consent-description { 52 | margin-left: 25px; 53 | 54 | label { 55 | font-weight: normal; 56 | } 57 | } 58 | 59 | .consent-remember { 60 | padding-left: 16px; 61 | } 62 | } 63 | } 64 | 65 | .grants { 66 | .page-header { 67 | margin-bottom: 10px; 68 | } 69 | 70 | .grant { 71 | margin-top: 20px; 72 | padding-bottom: 20px; 73 | border-bottom: 1px solid lightgray; 74 | 75 | img { 76 | width: 100px; 77 | height: 100px; 78 | } 79 | 80 | .clientname { 81 | font-size: 140%; 82 | font-weight: bold; 83 | } 84 | 85 | .granttype { 86 | font-size: 120%; 87 | font-weight: bold; 88 | } 89 | 90 | .created { 91 | font-size: 120%; 92 | font-weight: bold; 93 | } 94 | 95 | .expires { 96 | font-size: 120%; 97 | font-weight: bold; 98 | } 99 | 100 | li { 101 | list-style-type: none; 102 | display: inline; 103 | 104 | &:after { 105 | content: ', '; 106 | } 107 | 108 | &:last-child:after { 109 | content: ''; 110 | } 111 | 112 | .displayname { 113 | } 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/Views/Shared/_Layout.cshtml: -------------------------------------------------------------------------------- 1 | @using IdentityServer4.Extensions 2 | @{ 3 | string name = null; 4 | if (!true.Equals(ViewData["signed-out"])) 5 | { 6 | name = Context.User?.GetDisplayName(); 7 | } 8 | } 9 | 10 | 11 | 12 | 13 | 14 | 15 | IdentityServer4 16 | 17 | 18 | 19 | 20 | 21 | 22 | 52 | 53 |
    54 | @RenderBody() 55 |
    56 | 57 | 58 | 59 | @RenderSection("scripts", required: false) 60 | 61 | 62 | -------------------------------------------------------------------------------- /BlazorId_App.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29911.98 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlazorId_App", "BlazorId_App\BlazorId_App.csproj", "{AD755486-2C45-44CD-AED0-FC3F774457B3}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlazorId_Shared", "BlazorId_Shared\BlazorId_Shared.csproj", "{32F03A32-E197-4835-A63F-332393CE89A9}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Api", "Api\Api.csproj", "{B1F6639B-12E7-467B-9F53-7B9539AEADD8}" 11 | EndProject 12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IdentityServerAspNetIdentity", "IdentityServerAspNetIdentity\IdentityServerAspNetIdentity.csproj", "{1DE104B0-D982-4F47-9D4D-3B16D59D8CFD}" 13 | EndProject 14 | Global 15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 16 | Debug|Any CPU = Debug|Any CPU 17 | Release|Any CPU = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {AD755486-2C45-44CD-AED0-FC3F774457B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {AD755486-2C45-44CD-AED0-FC3F774457B3}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {AD755486-2C45-44CD-AED0-FC3F774457B3}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {AD755486-2C45-44CD-AED0-FC3F774457B3}.Release|Any CPU.Build.0 = Release|Any CPU 24 | {32F03A32-E197-4835-A63F-332393CE89A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 25 | {32F03A32-E197-4835-A63F-332393CE89A9}.Debug|Any CPU.Build.0 = Debug|Any CPU 26 | {32F03A32-E197-4835-A63F-332393CE89A9}.Release|Any CPU.ActiveCfg = Release|Any CPU 27 | {32F03A32-E197-4835-A63F-332393CE89A9}.Release|Any CPU.Build.0 = Release|Any CPU 28 | {B1F6639B-12E7-467B-9F53-7B9539AEADD8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 29 | {B1F6639B-12E7-467B-9F53-7B9539AEADD8}.Debug|Any CPU.Build.0 = Debug|Any CPU 30 | {B1F6639B-12E7-467B-9F53-7B9539AEADD8}.Release|Any CPU.ActiveCfg = Release|Any CPU 31 | {B1F6639B-12E7-467B-9F53-7B9539AEADD8}.Release|Any CPU.Build.0 = Release|Any CPU 32 | {1DE104B0-D982-4F47-9D4D-3B16D59D8CFD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {1DE104B0-D982-4F47-9D4D-3B16D59D8CFD}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | {1DE104B0-D982-4F47-9D4D-3B16D59D8CFD}.Release|Any CPU.ActiveCfg = Release|Any CPU 35 | {1DE104B0-D982-4F47-9D4D-3B16D59D8CFD}.Release|Any CPU.Build.0 = Release|Any CPU 36 | EndGlobalSection 37 | GlobalSection(SolutionProperties) = preSolution 38 | HideSolutionNode = FALSE 39 | EndGlobalSection 40 | GlobalSection(ExtensibilityGlobals) = postSolution 41 | SolutionGuid = {CFCBBA75-DA3B-44BF-B8AC-3A47FE73602E} 42 | EndGlobalSection 43 | EndGlobal 44 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /BlazorId_App/Data/IdentityDataService.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Authentication; 2 | using Microsoft.AspNetCore.Http; 3 | using Newtonsoft.Json; 4 | using Newtonsoft.Json.Linq; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Net.Http; 9 | using System.Runtime.InteropServices.ComTypes; 10 | using System.Security.Claims; 11 | using System.Text.Json; 12 | using System.Threading.Tasks; 13 | 14 | namespace BlazorId_App.Data 15 | { 16 | public class IdentityDataService 17 | { 18 | private readonly HttpClient _httpClient; 19 | private readonly IHttpContextAccessor _httpContextAccessor; 20 | private string _userClaims; 21 | public IdentityDataService(HttpClient httpClient, IHttpContextAccessor httpContextAccessor) 22 | { 23 | _httpClient = httpClient ?? 24 | throw new System.ArgumentNullException(nameof(httpClient)); 25 | _httpContextAccessor = httpContextAccessor ?? 26 | throw new System.ArgumentNullException(nameof(httpContextAccessor)); 27 | } 28 | 29 | // Get user claims json from the API get method 30 | public async TaskGetAPIUserClaimsJson() 31 | { 32 | var accessToken = await _httpContextAccessor.HttpContext.GetTokenAsync("access_token"); 33 | if (accessToken != null) 34 | { 35 | _httpClient.DefaultRequestHeaders.Add("Authorization", "Bearer " + accessToken); 36 | } 37 | 38 | HttpResponseMessage response = null ; 39 | try 40 | { 41 | response = await _httpClient.GetAsync($"identity"); 42 | response.EnsureSuccessStatusCode(); 43 | } 44 | catch (HttpRequestException e) 45 | { 46 | return e.Message; 47 | } 48 | string rawJson = await response.Content.ReadAsStringAsync(); 49 | JToken parsedJson = JToken.Parse(rawJson); 50 | return parsedJson.ToString(Formatting.Indented); 51 | 52 | } 53 | 54 | /// 55 | /// Gets user claims from the current httpcontext 56 | /// 57 | /// 58 | public async Task GetAPPUserClaimsJson() 59 | { 60 | JObject jo; 61 | try 62 | { 63 | var claimsList = _httpContextAccessor.HttpContext.User.Claims; 64 | 65 | var rawList = (from c in claimsList select new { c.Type, c.Value }); 66 | string json = JsonConvert.SerializeObject(rawList, Formatting.Indented); 67 | 68 | return json; 69 | } 70 | catch(Exception e) 71 | { 72 | throw (e); 73 | } 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/Quickstart/SecurityHeadersAttribute.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Brock Allen & Dominick Baier. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | 5 | using Microsoft.AspNetCore.Mvc; 6 | using Microsoft.AspNetCore.Mvc.Filters; 7 | 8 | namespace IdentityServer4.Quickstart.UI 9 | { 10 | public class SecurityHeadersAttribute : ActionFilterAttribute 11 | { 12 | public override void OnResultExecuting(ResultExecutingContext context) 13 | { 14 | var result = context.Result; 15 | if (result is ViewResult) 16 | { 17 | // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options 18 | if (!context.HttpContext.Response.Headers.ContainsKey("X-Content-Type-Options")) 19 | { 20 | context.HttpContext.Response.Headers.Add("X-Content-Type-Options", "nosniff"); 21 | } 22 | 23 | // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options 24 | if (!context.HttpContext.Response.Headers.ContainsKey("X-Frame-Options")) 25 | { 26 | context.HttpContext.Response.Headers.Add("X-Frame-Options", "SAMEORIGIN"); 27 | } 28 | 29 | // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy 30 | var csp = "default-src 'self'; object-src 'none'; frame-ancestors 'none'; sandbox allow-forms allow-same-origin allow-scripts; base-uri 'self';"; 31 | // also consider adding upgrade-insecure-requests once you have HTTPS in place for production 32 | //csp += "upgrade-insecure-requests;"; 33 | // also an example if you need client images to be displayed from twitter 34 | // csp += "img-src 'self' https://pbs.twimg.com;"; 35 | 36 | // once for standards compliant browsers 37 | if (!context.HttpContext.Response.Headers.ContainsKey("Content-Security-Policy")) 38 | { 39 | context.HttpContext.Response.Headers.Add("Content-Security-Policy", csp); 40 | } 41 | // and once again for IE 42 | if (!context.HttpContext.Response.Headers.ContainsKey("X-Content-Security-Policy")) 43 | { 44 | context.HttpContext.Response.Headers.Add("X-Content-Security-Policy", csp); 45 | } 46 | 47 | // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy 48 | var referrer_policy = "no-referrer"; 49 | if (!context.HttpContext.Response.Headers.ContainsKey("Referrer-Policy")) 50 | { 51 | context.HttpContext.Response.Headers.Add("Referrer-Policy", referrer_policy); 52 | } 53 | } 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/Views/Grants/Index.cshtml: -------------------------------------------------------------------------------- 1 | @model GrantsViewModel 2 | 3 |
    4 | 12 | 13 | @if (Model.Grants.Any() == false) 14 | { 15 |
    16 |
    17 |
    18 | You have not given access to any applications 19 |
    20 |
    21 |
    22 | } 23 | else 24 | { 25 | foreach (var grant in Model.Grants) 26 | { 27 |
    28 |
    29 | @if (grant.ClientLogoUrl != null) 30 | { 31 | 32 | } 33 |
    34 |
    35 |
    @grant.ClientName
    36 |
    37 | Created: @grant.Created.ToString("yyyy-MM-dd") 38 |
    39 | @if (grant.Expires.HasValue) 40 | { 41 |
    42 | Expires: @grant.Expires.Value.ToString("yyyy-MM-dd") 43 |
    44 | } 45 | @if (grant.IdentityGrantNames.Any()) 46 | { 47 |
    48 |
    Identity Grants
    49 |
      50 | @foreach (var name in grant.IdentityGrantNames) 51 | { 52 |
    • @name
    • 53 | } 54 |
    55 |
    56 | } 57 | @if (grant.ApiGrantNames.Any()) 58 | { 59 |
    60 |
    API Grants
    61 |
      62 | @foreach (var name in grant.ApiGrantNames) 63 | { 64 |
    • @name
    • 65 | } 66 |
    67 |
    68 | } 69 |
    70 |
    71 |
    72 | 73 | 74 |
    75 |
    76 |
    77 | } 78 | } 79 |
    -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/Config.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Brock Allen & Dominick Baier. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | 5 | using IdentityServer4; 6 | using IdentityServer4.Models; 7 | using System.Collections.Generic; 8 | 9 | namespace IdentityServerAspNetIdentity 10 | { 11 | public static class Config 12 | { 13 | public static IEnumerable Ids => 14 | new List 15 | { 16 | new IdentityResources.OpenId(), 17 | new IdentityResources.Profile(), 18 | new IdentityResources.Email(), 19 | // Identity Resource for custom user claim type 20 | new IdentityResource("appUser_claim", new []{"appUser_claim"}) 21 | }; 22 | 23 | 24 | public static IEnumerable Apis => 25 | new List 26 | { // Identity API, consumes user claim type 'appUser_claim' 27 | // By assigning the user claim to the api resource, we are instructing Identity Server to include that claim in Access tokens for this resource. 28 | new ApiResource("identityApi", 29 | "Identity Claims Api", 30 | new []{"appUser_claim"}) 31 | }; 32 | 33 | public static IEnumerable Clients => 34 | new Client[] 35 | { 36 | //// machine to machine client 37 | //new Client 38 | //{ 39 | // ClientId = "client", 40 | // ClientSecrets = { new Secret("secret".Sha256()) }, 41 | // AllowedGrantTypes = GrantTypes.ClientCredentials, 42 | // // scopes that client has access to 43 | // AllowedScopes = { "identityApi" }, 44 | //}, 45 | 46 | // interactive ASP.NET Core Blazor Server Client 47 | new Client 48 | { 49 | ClientId = "BlazorID_App", 50 | ClientName="Blazor Server App - Identity Claims", 51 | ClientSecrets = { new Secret("secret".Sha256()) }, 52 | 53 | AllowedGrantTypes = GrantTypes.Code, 54 | RequireConsent = false, 55 | RequirePkce = true, 56 | 57 | // where to redirect to after login 58 | RedirectUris = { "https://localhost:44321/signin-oidc" }, 59 | 60 | // where to redirect to after logout 61 | PostLogoutRedirectUris = { "https://localhost:44321/signout-callback-oidc" }, 62 | 63 | // allowed scopes - include Api Resources and Identity Resources that may be accessed by this client 64 | AllowedScopes = { "openid", "profile", "email", "identityApi","appUser_claim" }, 65 | 66 | // include the refresh token 67 | AllowOfflineAccess = true 68 | } 69 | }; 70 | 71 | 72 | } 73 | 74 | 75 | 76 | } -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/Program.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Brock Allen & Dominick Baier. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | 5 | using Microsoft.AspNetCore.Hosting; 6 | using Microsoft.Extensions.Configuration; 7 | using Microsoft.Extensions.DependencyInjection; 8 | using Microsoft.Extensions.Hosting; 9 | using Serilog; 10 | using Serilog.Events; 11 | using Serilog.Sinks.SystemConsole.Themes; 12 | using System; 13 | using System.Linq; 14 | 15 | namespace IdentityServerAspNetIdentity 16 | { 17 | public class Program 18 | { 19 | public static int Main(string[] args) 20 | { 21 | Log.Logger = new LoggerConfiguration() 22 | .MinimumLevel.Debug() 23 | .MinimumLevel.Override("Microsoft", LogEventLevel.Warning) 24 | .MinimumLevel.Override("System", LogEventLevel.Warning) 25 | .MinimumLevel.Override("Microsoft.AspNetCore.Authentication", LogEventLevel.Information) 26 | .Enrich.FromLogContext() 27 | // uncomment to write to Azure diagnostics stream 28 | //.WriteTo.File( 29 | // @"D:\home\LogFiles\Application\identityserver.txt", 30 | // fileSizeLimitBytes: 1_000_000, 31 | // rollOnFileSizeLimit: true, 32 | // shared: true, 33 | // flushToDiskInterval: TimeSpan.FromSeconds(1)) 34 | .WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level}] {SourceContext}{NewLine}{Message:lj}{NewLine}{Exception}{NewLine}", theme: AnsiConsoleTheme.Literate) 35 | .CreateLogger(); 36 | 37 | try 38 | { 39 | var seed = args.Contains("/seed"); 40 | if (seed) 41 | { 42 | args = args.Except(new[] { "/seed" }).ToArray(); 43 | } 44 | 45 | var host = CreateHostBuilder(args).Build(); 46 | 47 | if (seed) 48 | { 49 | Log.Information("Seeding database..."); 50 | var config = host.Services.GetRequiredService(); 51 | var connectionString = config.GetConnectionString("DefaultConnection"); 52 | SeedData.EnsureSeedData(connectionString); 53 | Log.Information("Done seeding database."); 54 | return 0; 55 | } 56 | 57 | Log.Information("Starting host..."); 58 | host.Run(); 59 | return 0; 60 | } 61 | catch (Exception ex) 62 | { 63 | Log.Fatal(ex, "Host terminated unexpectedly."); 64 | return 1; 65 | } 66 | finally 67 | { 68 | Log.CloseAndFlush(); 69 | } 70 | } 71 | 72 | public static IHostBuilder CreateHostBuilder(string[] args) => 73 | Host.CreateDefaultBuilder(args) 74 | .ConfigureWebHostDefaults(webBuilder => 75 | { 76 | webBuilder.UseStartup(); 77 | webBuilder.UseSerilog(); 78 | }); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/Views/Consent/Index.cshtml: -------------------------------------------------------------------------------- 1 | @model ConsentViewModel 2 | 3 | -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/Quickstart/Grants/GrantsController.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Brock Allen & Dominick Baier. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | 5 | using IdentityServer4.Services; 6 | using IdentityServer4.Stores; 7 | using Microsoft.AspNetCore.Mvc; 8 | using System.Collections.Generic; 9 | using System.Linq; 10 | using System.Threading.Tasks; 11 | using Microsoft.AspNetCore.Authorization; 12 | using IdentityServer4.Events; 13 | using IdentityServer4.Extensions; 14 | 15 | namespace IdentityServer4.Quickstart.UI 16 | { 17 | /// 18 | /// This sample controller allows a user to revoke grants given to clients 19 | /// 20 | [SecurityHeaders] 21 | [Authorize] 22 | public class GrantsController : Controller 23 | { 24 | private readonly IIdentityServerInteractionService _interaction; 25 | private readonly IClientStore _clients; 26 | private readonly IResourceStore _resources; 27 | private readonly IEventService _events; 28 | 29 | public GrantsController(IIdentityServerInteractionService interaction, 30 | IClientStore clients, 31 | IResourceStore resources, 32 | IEventService events) 33 | { 34 | _interaction = interaction; 35 | _clients = clients; 36 | _resources = resources; 37 | _events = events; 38 | } 39 | 40 | /// 41 | /// Show list of grants 42 | /// 43 | [HttpGet] 44 | public async Task Index() 45 | { 46 | return View("Index", await BuildViewModelAsync()); 47 | } 48 | 49 | /// 50 | /// Handle postback to revoke a client 51 | /// 52 | [HttpPost] 53 | [ValidateAntiForgeryToken] 54 | public async Task Revoke(string clientId) 55 | { 56 | await _interaction.RevokeUserConsentAsync(clientId); 57 | await _events.RaiseAsync(new GrantsRevokedEvent(User.GetSubjectId(), clientId)); 58 | 59 | return RedirectToAction("Index"); 60 | } 61 | 62 | private async Task BuildViewModelAsync() 63 | { 64 | var grants = await _interaction.GetAllUserConsentsAsync(); 65 | 66 | var list = new List(); 67 | foreach(var grant in grants) 68 | { 69 | var client = await _clients.FindClientByIdAsync(grant.ClientId); 70 | if (client != null) 71 | { 72 | var resources = await _resources.FindResourcesByScopeAsync(grant.Scopes); 73 | 74 | var item = new GrantViewModel() 75 | { 76 | ClientId = client.ClientId, 77 | ClientName = client.ClientName ?? client.ClientId, 78 | ClientLogoUrl = client.LogoUri, 79 | ClientUrl = client.ClientUri, 80 | Created = grant.CreationTime, 81 | Expires = grant.Expiration, 82 | IdentityGrantNames = resources.IdentityResources.Select(x => x.DisplayName ?? x.Name).ToArray(), 83 | ApiGrantNames = resources.ApiResources.Select(x => x.DisplayName ?? x.Name).ToArray() 84 | }; 85 | 86 | list.Add(item); 87 | } 88 | } 89 | 90 | return new GrantsViewModel 91 | { 92 | Grants = list 93 | }; 94 | } 95 | } 96 | } -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/Views/Device/UserCodeConfirmation.cshtml: -------------------------------------------------------------------------------- 1 | @model IdentityServer4.Quickstart.UI.Device.DeviceAuthorizationViewModel 2 | 3 | -------------------------------------------------------------------------------- /BlazorId_App/wwwroot/css/open-iconic/README.md: -------------------------------------------------------------------------------- 1 | [Open Iconic v1.1.1](http://useiconic.com/open) 2 | =========== 3 | 4 | ### Open Iconic is the open source sibling of [Iconic](http://useiconic.com). It is a hyper-legible collection of 223 icons with a tiny footprint—ready to use with Bootstrap and Foundation. [View the collection](http://useiconic.com/open#icons) 5 | 6 | 7 | 8 | ## What's in Open Iconic? 9 | 10 | * 223 icons designed to be legible down to 8 pixels 11 | * Super-light SVG files - 61.8 for the entire set 12 | * SVG sprite—the modern replacement for icon fonts 13 | * Webfont (EOT, OTF, SVG, TTF, WOFF), PNG and WebP formats 14 | * Webfont stylesheets (including versions for Bootstrap and Foundation) in CSS, LESS, SCSS and Stylus formats 15 | * PNG and WebP raster images in 8px, 16px, 24px, 32px, 48px and 64px. 16 | 17 | 18 | ## Getting Started 19 | 20 | #### For code samples and everything else you need to get started with Open Iconic, check out our [Icons](http://useiconic.com/open#icons) and [Reference](http://useiconic.com/open#reference) sections. 21 | 22 | ### General Usage 23 | 24 | #### Using Open Iconic's SVGs 25 | 26 | We like SVGs and we think they're the way to display icons on the web. Since Open Iconic are just basic SVGs, we suggest you display them like you would any other image (don't forget the `alt` attribute). 27 | 28 | ``` 29 | icon name 30 | ``` 31 | 32 | #### Using Open Iconic's SVG Sprite 33 | 34 | Open Iconic also comes in a SVG sprite which allows you to display all the icons in the set with a single request. It's like an icon font, without being a hack. 35 | 36 | Adding an icon from an SVG sprite is a little different than what you're used to, but it's still a piece of cake. *Tip: To make your icons easily style able, we suggest adding a general class to the* `` *tag and a unique class name for each different icon in the* `` *tag.* 37 | 38 | ``` 39 | 40 | 41 | 42 | ``` 43 | 44 | Sizing icons only needs basic CSS. All the icons are in a square format, so just set the `` tag with equal width and height dimensions. 45 | 46 | ``` 47 | .icon { 48 | width: 16px; 49 | height: 16px; 50 | } 51 | ``` 52 | 53 | Coloring icons is even easier. All you need to do is set the `fill` rule on the `` tag. 54 | 55 | ``` 56 | .icon-account-login { 57 | fill: #f00; 58 | } 59 | ``` 60 | 61 | To learn more about SVG Sprites, read [Chris Coyier's guide](http://css-tricks.com/svg-sprites-use-better-icon-fonts/). 62 | 63 | #### Using Open Iconic's Icon Font... 64 | 65 | 66 | ##### …with Bootstrap 67 | 68 | You can find our Bootstrap stylesheets in `font/css/open-iconic-bootstrap.{css, less, scss, styl}` 69 | 70 | 71 | ``` 72 | 73 | ``` 74 | 75 | 76 | ``` 77 | 78 | ``` 79 | 80 | ##### …with Foundation 81 | 82 | You can find our Foundation stylesheets in `font/css/open-iconic-foundation.{css, less, scss, styl}` 83 | 84 | ``` 85 | 86 | ``` 87 | 88 | 89 | ``` 90 | 91 | ``` 92 | 93 | ##### …on its own 94 | 95 | You can find our default stylesheets in `font/css/open-iconic.{css, less, scss, styl}` 96 | 97 | ``` 98 | 99 | ``` 100 | 101 | ``` 102 | 103 | ``` 104 | 105 | 106 | ## License 107 | 108 | ### Icons 109 | 110 | All code (including SVG markup) is under the [MIT License](http://opensource.org/licenses/MIT). 111 | 112 | ### Fonts 113 | 114 | All fonts are under the [SIL Licensed](http://scripts.sil.org/cms/scripts/page.php?item_id=OFL_web). 115 | -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/Startup.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Brock Allen & Dominick Baier. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | 5 | using IdentityServerAspNetIdentity.Data; 6 | using IdentityServerAspNetIdentity.Models; 7 | using Microsoft.AspNetCore.Builder; 8 | using Microsoft.AspNetCore.Hosting; 9 | using Microsoft.AspNetCore.Identity; 10 | using Microsoft.EntityFrameworkCore; 11 | using Microsoft.Extensions.Configuration; 12 | using Microsoft.Extensions.DependencyInjection; 13 | using Microsoft.Extensions.Hosting; 14 | 15 | namespace IdentityServerAspNetIdentity 16 | { 17 | public class Startup 18 | { 19 | public IWebHostEnvironment Environment { get; } 20 | public IConfiguration Configuration { get; } 21 | 22 | public Startup(IWebHostEnvironment environment, IConfiguration configuration) 23 | { 24 | Environment = environment; 25 | Configuration = configuration; 26 | } 27 | 28 | public void ConfigureServices(IServiceCollection services) 29 | { 30 | services.AddControllersWithViews(); 31 | 32 | // configures IIS out-of-proc settings (see https://github.com/aspnet/AspNetCore/issues/14882) 33 | services.Configure(iis => 34 | { 35 | iis.AuthenticationDisplayName = "Windows"; 36 | iis.AutomaticAuthentication = false; 37 | }); 38 | 39 | // configures IIS in-proc settings 40 | services.Configure(iis => 41 | { 42 | iis.AuthenticationDisplayName = "Windows"; 43 | iis.AutomaticAuthentication = false; 44 | }); 45 | 46 | services.AddDbContext(options => 47 | options.UseSqlite(Configuration.GetConnectionString("DefaultConnection"))); 48 | 49 | services.AddIdentity() 50 | .AddEntityFrameworkStores() 51 | .AddDefaultTokenProviders(); 52 | 53 | var builder = services.AddIdentityServer(options => 54 | { 55 | options.Events.RaiseErrorEvents = true; 56 | options.Events.RaiseInformationEvents = true; 57 | options.Events.RaiseFailureEvents = true; 58 | options.Events.RaiseSuccessEvents = true; 59 | }) 60 | .AddInMemoryIdentityResources(Config.Ids) 61 | .AddInMemoryApiResources(Config.Apis) 62 | .AddInMemoryClients(Config.Clients) 63 | .AddAspNetIdentity(); 64 | 65 | // not recommended for production - you need to store your key material somewhere secure 66 | builder.AddDeveloperSigningCredential(); 67 | 68 | //services.AddAuthentication() 69 | // .AddGoogle(options => 70 | // { 71 | // // register your IdentityServer with Google at https://console.developers.google.com 72 | // // enable the Google+ API 73 | // // set the redirect URI to http://localhost:5000/signin-google 74 | // options.ClientId = "copy client ID from Google here"; 75 | // options.ClientSecret = "copy client secret from Google here"; 76 | // }); 77 | } 78 | 79 | public void Configure(IApplicationBuilder app) 80 | { 81 | if (Environment.IsDevelopment()) 82 | { 83 | app.UseDeveloperExceptionPage(); 84 | app.UseDatabaseErrorPage(); 85 | } 86 | 87 | app.UseStaticFiles(); 88 | 89 | app.UseRouting(); 90 | app.UseIdentityServer(); 91 | app.UseAuthorization(); 92 | app.UseEndpoints(endpoints => 93 | { 94 | endpoints.MapDefaultControllerRoute(); 95 | }); 96 | } 97 | } 98 | } -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/Views/Account/Login.cshtml: -------------------------------------------------------------------------------- 1 | @model LoginViewModel 2 | 3 | -------------------------------------------------------------------------------- /BlazorId_App/wwwroot/css/site.css: -------------------------------------------------------------------------------- 1 | @import url('open-iconic/font/css/open-iconic-bootstrap.min.css'); 2 | 3 | html, body { 4 | font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; 5 | } 6 | 7 | a, .btn-link { 8 | color: #0366d6; 9 | } 10 | 11 | .btn-primary { 12 | color: #fff; 13 | background-color: #1b6ec2; 14 | border-color: #1861ac; 15 | } 16 | 17 | app { 18 | position: relative; 19 | display: flex; 20 | flex-direction: column; 21 | } 22 | 23 | .top-row { 24 | height: 3.5rem; 25 | display: flex; 26 | align-items: center; 27 | } 28 | 29 | .main { 30 | flex: 1; 31 | } 32 | 33 | .main .top-row { 34 | background-color: #f7f7f7; 35 | border-bottom: 1px solid #d6d5d5; 36 | justify-content: flex-end; 37 | } 38 | 39 | .main .top-row > a, .main .top-row .btn-link { 40 | white-space: nowrap; 41 | margin-left: 1.5rem; 42 | } 43 | 44 | .main .top-row a:first-child { 45 | overflow: hidden; 46 | text-overflow: ellipsis; 47 | } 48 | 49 | .sidebar { 50 | background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%); 51 | } 52 | 53 | .sidebar .top-row { 54 | background-color: rgba(0,0,0,0.4); 55 | } 56 | 57 | .sidebar .navbar-brand { 58 | font-size: 1.1rem; 59 | } 60 | 61 | .sidebar .oi { 62 | width: 2rem; 63 | font-size: 1.1rem; 64 | vertical-align: text-top; 65 | top: -2px; 66 | } 67 | 68 | .sidebar .nav-item { 69 | font-size: 0.9rem; 70 | padding-bottom: 0.5rem; 71 | } 72 | 73 | .sidebar .nav-item:first-of-type { 74 | padding-top: 1rem; 75 | } 76 | 77 | .sidebar .nav-item:last-of-type { 78 | padding-bottom: 1rem; 79 | } 80 | 81 | .sidebar .nav-item a { 82 | color: #d7d7d7; 83 | border-radius: 4px; 84 | height: 3rem; 85 | display: flex; 86 | align-items: center; 87 | line-height: 3rem; 88 | } 89 | 90 | .sidebar .nav-item a.active { 91 | background-color: rgba(255,255,255,0.25); 92 | color: white; 93 | } 94 | 95 | .sidebar .nav-item a:hover { 96 | background-color: rgba(255,255,255,0.1); 97 | color: white; 98 | } 99 | 100 | .content { 101 | padding-top: 1.1rem; 102 | } 103 | 104 | .navbar-toggler { 105 | background-color: rgba(255, 255, 255, 0.1); 106 | } 107 | 108 | .valid.modified:not([type=checkbox]) { 109 | outline: 1px solid #26b050; 110 | } 111 | 112 | .invalid { 113 | outline: 1px solid red; 114 | } 115 | 116 | .validation-message { 117 | color: red; 118 | } 119 | 120 | #blazor-error-ui { 121 | background: lightyellow; 122 | bottom: 0; 123 | box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); 124 | display: none; 125 | left: 0; 126 | padding: 0.6rem 1.25rem 0.7rem 1.25rem; 127 | position: fixed; 128 | width: 100%; 129 | z-index: 1000; 130 | } 131 | 132 | #blazor-error-ui .dismiss { 133 | cursor: pointer; 134 | position: absolute; 135 | right: 0.75rem; 136 | top: 0.5rem; 137 | } 138 | 139 | @media (max-width: 767.98px) { 140 | .main .top-row:not(.auth) { 141 | display: none; 142 | } 143 | 144 | .main .top-row.auth { 145 | justify-content: space-between; 146 | } 147 | 148 | .main .top-row a, .main .top-row .btn-link { 149 | margin-left: 0; 150 | } 151 | } 152 | 153 | @media (min-width: 768px) { 154 | app { 155 | flex-direction: row; 156 | } 157 | 158 | .sidebar { 159 | width: 250px; 160 | height: 100vh; 161 | position: sticky; 162 | top: 0; 163 | } 164 | 165 | .main .top-row { 166 | position: sticky; 167 | top: 0; 168 | } 169 | 170 | .main > div { 171 | padding-left: 2rem !important; 172 | padding-right: 1.5rem !important; 173 | } 174 | 175 | .navbar-toggler { 176 | display: none; 177 | } 178 | 179 | .sidebar .collapse { 180 | /* Never collapse the sidebar for wide screens */ 181 | display: block; 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /BlazorId_App/wwwroot/css/open-iconic/FONT-LICENSE: -------------------------------------------------------------------------------- 1 | SIL OPEN FONT LICENSE Version 1.1 2 | 3 | Copyright (c) 2014 Waybury 4 | 5 | PREAMBLE 6 | The goals of the Open Font License (OFL) are to stimulate worldwide 7 | development of collaborative font projects, to support the font creation 8 | efforts of academic and linguistic communities, and to provide a free and 9 | open framework in which fonts may be shared and improved in partnership 10 | with others. 11 | 12 | The OFL allows the licensed fonts to be used, studied, modified and 13 | redistributed freely as long as they are not sold by themselves. The 14 | fonts, including any derivative works, can be bundled, embedded, 15 | redistributed and/or sold with any software provided that any reserved 16 | names are not used by derivative works. The fonts and derivatives, 17 | however, cannot be released under any other type of license. The 18 | requirement for fonts to remain under this license does not apply 19 | to any document created using the fonts or their derivatives. 20 | 21 | DEFINITIONS 22 | "Font Software" refers to the set of files released by the Copyright 23 | Holder(s) under this license and clearly marked as such. This may 24 | include source files, build scripts and documentation. 25 | 26 | "Reserved Font Name" refers to any names specified as such after the 27 | copyright statement(s). 28 | 29 | "Original Version" refers to the collection of Font Software components as 30 | distributed by the Copyright Holder(s). 31 | 32 | "Modified Version" refers to any derivative made by adding to, deleting, 33 | or substituting -- in part or in whole -- any of the components of the 34 | Original Version, by changing formats or by porting the Font Software to a 35 | new environment. 36 | 37 | "Author" refers to any designer, engineer, programmer, technical 38 | writer or other person who contributed to the Font Software. 39 | 40 | PERMISSION & CONDITIONS 41 | Permission is hereby granted, free of charge, to any person obtaining 42 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 43 | redistribute, and sell modified and unmodified copies of the Font 44 | Software, subject to the following conditions: 45 | 46 | 1) Neither the Font Software nor any of its individual components, 47 | in Original or Modified Versions, may be sold by itself. 48 | 49 | 2) Original or Modified Versions of the Font Software may be bundled, 50 | redistributed and/or sold with any software, provided that each copy 51 | contains the above copyright notice and this license. These can be 52 | included either as stand-alone text files, human-readable headers or 53 | in the appropriate machine-readable metadata fields within text or 54 | binary files as long as those fields can be easily viewed by the user. 55 | 56 | 3) No Modified Version of the Font Software may use the Reserved Font 57 | Name(s) unless explicit written permission is granted by the corresponding 58 | Copyright Holder. This restriction only applies to the primary font name as 59 | presented to the users. 60 | 61 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 62 | Software shall not be used to promote, endorse or advertise any 63 | Modified Version, except to acknowledge the contribution(s) of the 64 | Copyright Holder(s) and the Author(s) or with their explicit written 65 | permission. 66 | 67 | 5) The Font Software, modified or unmodified, in part or in whole, 68 | must be distributed entirely under this license, and must not be 69 | distributed under any other license. The requirement for fonts to 70 | remain under this license does not apply to any document created 71 | using the Font Software. 72 | 73 | TERMINATION 74 | This license becomes null and void if any of the above conditions are 75 | not met. 76 | 77 | DISCLAIMER 78 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 79 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 80 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 81 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 82 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 83 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 84 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 85 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 86 | OTHER DEALINGS IN THE FONT SOFTWARE. 87 | -------------------------------------------------------------------------------- /BlazorId_App/Startup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.AspNetCore.Builder; 3 | using Microsoft.AspNetCore.Hosting; 4 | using Microsoft.Extensions.Configuration; 5 | using Microsoft.Extensions.DependencyInjection; 6 | using Microsoft.Extensions.Hosting; 7 | using Microsoft.AspNetCore.Http; 8 | using Microsoft.Net.Http.Headers; 9 | using Microsoft.AspNetCore.Authentication.Cookies; 10 | using Microsoft.AspNetCore.Authentication.OpenIdConnect; 11 | using Microsoft.AspNetCore.Authentication; 12 | using BlazorId_App.Data; 13 | 14 | namespace BlazorId_App 15 | { 16 | public class Startup 17 | { 18 | public Startup(IConfiguration configuration) 19 | { 20 | Configuration = configuration; 21 | } 22 | 23 | public IConfiguration Configuration { get; } 24 | 25 | // This method gets called by the runtime. Use this method to add services to the container. 26 | // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 27 | public void ConfigureServices(IServiceCollection services) 28 | { 29 | services.AddRazorPages(); 30 | services.AddServerSideBlazor(); 31 | services.AddSingleton(); 32 | 33 | //create an HTTP client for accessing the API 34 | services.AddHttpClient("ApiClient", client => 35 | { 36 | client.BaseAddress = new Uri("http://localhost:5001/"); 37 | client.DefaultRequestHeaders.Clear(); 38 | client.DefaultRequestHeaders.Add(HeaderNames.Accept, "application/json"); 39 | }); 40 | 41 | services.AddAuthentication(options => 42 | { 43 | options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; 44 | options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; 45 | }) 46 | .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme) 47 | .AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, 48 | options => 49 | { 50 | options.Authority = "https://localhost:44387/"; 51 | options.ClientId = "BlazorID_App"; 52 | options.ClientSecret = "secret"; 53 | options.UsePkce = true; 54 | options.ResponseType = "code"; 55 | options.Scope.Add("openid"); 56 | options.Scope.Add("profile"); 57 | options.Scope.Add("email"); 58 | options.Scope.Add("offline_access"); 59 | 60 | //Scope for accessing API 61 | options.Scope.Add("identityApi"); //invalid scope for client 62 | 63 | // Scope for custom user claim 64 | options.Scope.Add("appUser_claim"); //invalid scope for client 65 | 66 | // map custom user claim 67 | options.ClaimActions.MapUniqueJsonKey("appUser_claim", "appUser_claim"); 68 | 69 | //options.CallbackPath = ... 70 | options.SaveTokens = true; 71 | options.GetClaimsFromUserInfoEndpoint = true; 72 | 73 | }); 74 | 75 | services.AddAuthorization(authorizationOptions => 76 | { 77 | // add authorization poliy from shared project. This is the same policy used by the API 78 | authorizationOptions.AddPolicy( 79 | BlazorId_Shared.Policies.CanViewIdentity, 80 | BlazorId_Shared.Policies.CanViewIdentityPolicy()); 81 | }); 82 | 83 | services.AddHttpClient(client => 84 | { 85 | client.BaseAddress = new Uri("http://localhost:5001/"); 86 | }); 87 | } 88 | 89 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 90 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 91 | { 92 | if (env.IsDevelopment()) 93 | { 94 | app.UseDeveloperExceptionPage(); 95 | } 96 | else 97 | { 98 | app.UseExceptionHandler("/Error"); 99 | // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. 100 | app.UseHsts(); 101 | } 102 | 103 | app.UseHttpsRedirection(); 104 | app.UseStaticFiles(); 105 | 106 | app.UseRouting(); 107 | app.UseAuthentication(); 108 | app.UseAuthorization(); 109 | app.UseEndpoints(endpoints => 110 | { 111 | endpoints.MapBlazorHub(); 112 | endpoints.MapFallbackToPage("/_Host"); 113 | }); 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/SeedData.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Brock Allen & Dominick Baier. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | 5 | using System; 6 | using System.Linq; 7 | using System.Security.Claims; 8 | using IdentityModel; 9 | using IdentityServerAspNetIdentity.Data; 10 | using IdentityServerAspNetIdentity.Models; 11 | using Microsoft.AspNetCore.Identity; 12 | using Microsoft.EntityFrameworkCore; 13 | using Microsoft.Extensions.DependencyInjection; 14 | using Serilog; 15 | 16 | namespace IdentityServerAspNetIdentity 17 | { 18 | public class SeedData 19 | { 20 | public static void EnsureSeedData(string connectionString) 21 | { 22 | var services = new ServiceCollection(); 23 | services.AddLogging(); 24 | services.AddDbContext(options => 25 | options.UseSqlite(connectionString)); 26 | 27 | services.AddIdentity() 28 | .AddEntityFrameworkStores() 29 | .AddDefaultTokenProviders(); 30 | 31 | using (var serviceProvider = services.BuildServiceProvider()) 32 | { 33 | using (var scope = serviceProvider.GetRequiredService().CreateScope()) 34 | { 35 | var context = scope.ServiceProvider.GetService(); 36 | context.Database.Migrate(); 37 | 38 | var userMgr = scope.ServiceProvider.GetRequiredService>(); 39 | var alice = userMgr.FindByNameAsync("alice").Result; 40 | if (alice == null) 41 | { 42 | alice = new ApplicationUser 43 | { 44 | UserName = "alice" 45 | }; 46 | var result = userMgr.CreateAsync(alice, "Pass123$").Result; 47 | if (!result.Succeeded) 48 | { 49 | throw new Exception(result.Errors.First().Description); 50 | } 51 | 52 | result = userMgr.AddClaimsAsync(alice, new Claim[]{ 53 | new Claim(JwtClaimTypes.Name, "Alice Smith"), 54 | new Claim(JwtClaimTypes.GivenName, "Alice"), 55 | new Claim(JwtClaimTypes.FamilyName, "Smith"), 56 | new Claim(JwtClaimTypes.Email, "AliceSmith@email.com"), 57 | new Claim(JwtClaimTypes.EmailVerified, "true", ClaimValueTypes.Boolean), 58 | new Claim(JwtClaimTypes.WebSite, "http://alice.com"), 59 | new Claim(JwtClaimTypes.Address, @"{ 'street_address': 'One Hacker Way', 'locality': 'Heidelberg', 'postal_code': 69118, 'country': 'Germany' }", IdentityServer4.IdentityServerConstants.ClaimValueTypes.Json), 60 | // Add user_scope claim for Identity to authorize UI and API actions. Wendy has this permission, Bob does not. 61 | new Claim("appUser_claim","identity") 62 | }).Result; 63 | if (!result.Succeeded) 64 | { 65 | throw new Exception(result.Errors.First().Description); 66 | } 67 | Log.Debug("alice created"); 68 | } 69 | else 70 | { 71 | Log.Debug("alice already exists"); 72 | } 73 | 74 | var bob = userMgr.FindByNameAsync("bob").Result; 75 | if (bob == null) 76 | { 77 | bob = new ApplicationUser 78 | { 79 | UserName = "bob" 80 | }; 81 | var result = userMgr.CreateAsync(bob, "Pass123$").Result; 82 | if (!result.Succeeded) 83 | { 84 | throw new Exception(result.Errors.First().Description); 85 | } 86 | 87 | result = userMgr.AddClaimsAsync(bob, new Claim[]{ 88 | new Claim(JwtClaimTypes.Name, "Bob Smith"), 89 | new Claim(JwtClaimTypes.GivenName, "Bob"), 90 | new Claim(JwtClaimTypes.FamilyName, "Smith"), 91 | new Claim(JwtClaimTypes.Email, "BobSmith@email.com"), 92 | new Claim(JwtClaimTypes.EmailVerified, "true", ClaimValueTypes.Boolean), 93 | new Claim(JwtClaimTypes.WebSite, "http://bob.com"), 94 | new Claim(JwtClaimTypes.Address, @"{ 'street_address': 'One Hacker Way', 'locality': 'Heidelberg', 'postal_code': 69118, 'country': 'Germany' }", IdentityServer4.IdentityServerConstants.ClaimValueTypes.Json), 95 | new Claim("location", "somewhere") 96 | }).Result; 97 | if (!result.Succeeded) 98 | { 99 | throw new Exception(result.Errors.First().Description); 100 | } 101 | Log.Debug("bob created"); 102 | } 103 | else 104 | { 105 | Log.Debug("bob already exists"); 106 | } 107 | } 108 | } 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Dd]ebugPublic/ 19 | [Rr]elease/ 20 | [Rr]eleases/ 21 | x64/ 22 | x86/ 23 | [Aa][Rr][Mm]/ 24 | [Aa][Rr][Mm]64/ 25 | bld/ 26 | [Bb]in/ 27 | [Oo]bj/ 28 | [Ll]og/ 29 | 30 | # Visual Studio 2015/2017 cache/options directory 31 | .vs/ 32 | # Uncomment if you have tasks that create the project's static files in wwwroot 33 | #wwwroot/ 34 | 35 | # Visual Studio 2017 auto generated files 36 | Generated\ Files/ 37 | 38 | # MSTest test Results 39 | [Tt]est[Rr]esult*/ 40 | [Bb]uild[Ll]og.* 41 | 42 | # NUNIT 43 | *.VisualState.xml 44 | TestResult.xml 45 | 46 | # Build Results of an ATL Project 47 | [Dd]ebugPS/ 48 | [Rr]eleasePS/ 49 | dlldata.c 50 | 51 | # Benchmark Results 52 | BenchmarkDotNet.Artifacts/ 53 | 54 | # .NET Core 55 | project.lock.json 56 | project.fragment.lock.json 57 | artifacts/ 58 | 59 | # StyleCop 60 | StyleCopReport.xml 61 | 62 | # Files built by Visual Studio 63 | *_i.c 64 | *_p.c 65 | *_h.h 66 | *.ilk 67 | *.meta 68 | *.obj 69 | *.iobj 70 | *.pch 71 | *.pdb 72 | *.ipdb 73 | *.pgc 74 | *.pgd 75 | *.rsp 76 | *.sbr 77 | *.tlb 78 | *.tli 79 | *.tlh 80 | *.tmp 81 | *.tmp_proj 82 | *_wpftmp.csproj 83 | *.log 84 | *.vspscc 85 | *.vssscc 86 | .builds 87 | *.pidb 88 | *.svclog 89 | *.scc 90 | 91 | # Chutzpah Test files 92 | _Chutzpah* 93 | 94 | # Visual C++ cache files 95 | ipch/ 96 | *.aps 97 | *.ncb 98 | *.opendb 99 | *.opensdf 100 | *.sdf 101 | *.cachefile 102 | *.VC.db 103 | *.VC.VC.opendb 104 | 105 | # Visual Studio profiler 106 | *.psess 107 | *.vsp 108 | *.vspx 109 | *.sap 110 | 111 | # Visual Studio Trace Files 112 | *.e2e 113 | 114 | # TFS 2012 Local Workspace 115 | $tf/ 116 | 117 | # Guidance Automation Toolkit 118 | *.gpState 119 | 120 | # ReSharper is a .NET coding add-in 121 | _ReSharper*/ 122 | *.[Rr]e[Ss]harper 123 | *.DotSettings.user 124 | 125 | # JustCode is a .NET coding add-in 126 | .JustCode 127 | 128 | # TeamCity is a build add-in 129 | _TeamCity* 130 | 131 | # DotCover is a Code Coverage Tool 132 | *.dotCover 133 | 134 | # AxoCover is a Code Coverage Tool 135 | .axoCover/* 136 | !.axoCover/settings.json 137 | 138 | # Visual Studio code coverage results 139 | *.coverage 140 | *.coveragexml 141 | 142 | # NCrunch 143 | _NCrunch_* 144 | .*crunch*.local.xml 145 | nCrunchTemp_* 146 | 147 | # MightyMoose 148 | *.mm.* 149 | AutoTest.Net/ 150 | 151 | # Web workbench (sass) 152 | .sass-cache/ 153 | 154 | # Installshield output folder 155 | [Ee]xpress/ 156 | 157 | # DocProject is a documentation generator add-in 158 | DocProject/buildhelp/ 159 | DocProject/Help/*.HxT 160 | DocProject/Help/*.HxC 161 | DocProject/Help/*.hhc 162 | DocProject/Help/*.hhk 163 | DocProject/Help/*.hhp 164 | DocProject/Help/Html2 165 | DocProject/Help/html 166 | 167 | # Click-Once directory 168 | publish/ 169 | 170 | # Publish Web Output 171 | *.[Pp]ublish.xml 172 | *.azurePubxml 173 | # Note: Comment the next line if you want to checkin your web deploy settings, 174 | # but database connection strings (with potential passwords) will be unencrypted 175 | *.pubxml 176 | *.publishproj 177 | 178 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 179 | # checkin your Azure Web App publish settings, but sensitive information contained 180 | # in these scripts will be unencrypted 181 | PublishScripts/ 182 | 183 | # NuGet Packages 184 | *.nupkg 185 | # The packages folder can be ignored because of Package Restore 186 | **/[Pp]ackages/* 187 | # except build/, which is used as an MSBuild target. 188 | !**/[Pp]ackages/build/ 189 | # Uncomment if necessary however generally it will be regenerated when needed 190 | #!**/[Pp]ackages/repositories.config 191 | # NuGet v3's project.json files produces more ignorable files 192 | *.nuget.props 193 | *.nuget.targets 194 | 195 | # Microsoft Azure Build Output 196 | csx/ 197 | *.build.csdef 198 | 199 | # Microsoft Azure Emulator 200 | ecf/ 201 | rcf/ 202 | 203 | # Windows Store app package directories and files 204 | AppPackages/ 205 | BundleArtifacts/ 206 | Package.StoreAssociation.xml 207 | _pkginfo.txt 208 | *.appx 209 | 210 | # Visual Studio cache files 211 | # files ending in .cache can be ignored 212 | *.[Cc]ache 213 | # but keep track of directories ending in .cache 214 | !?*.[Cc]ache/ 215 | 216 | # Others 217 | ClientBin/ 218 | ~$* 219 | *~ 220 | *.dbmdl 221 | *.dbproj.schemaview 222 | *.jfm 223 | *.pfx 224 | *.publishsettings 225 | orleans.codegen.cs 226 | 227 | # Including strong name files can present a security risk 228 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 229 | #*.snk 230 | 231 | # Since there are multiple workflows, uncomment next line to ignore bower_components 232 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 233 | #bower_components/ 234 | 235 | # RIA/Silverlight projects 236 | Generated_Code/ 237 | 238 | # Backup & report files from converting an old project file 239 | # to a newer Visual Studio version. Backup files are not needed, 240 | # because we have git ;-) 241 | _UpgradeReport_Files/ 242 | Backup*/ 243 | UpgradeLog*.XML 244 | UpgradeLog*.htm 245 | ServiceFabricBackup/ 246 | *.rptproj.bak 247 | 248 | # SQL Server files 249 | *.mdf 250 | *.ldf 251 | *.ndf 252 | 253 | # Business Intelligence projects 254 | *.rdl.data 255 | *.bim.layout 256 | *.bim_*.settings 257 | *.rptproj.rsuser 258 | *- Backup*.rdl 259 | 260 | # Microsoft Fakes 261 | FakesAssemblies/ 262 | 263 | # GhostDoc plugin setting file 264 | *.GhostDoc.xml 265 | 266 | # Node.js Tools for Visual Studio 267 | .ntvs_analysis.dat 268 | node_modules/ 269 | 270 | # Visual Studio 6 build log 271 | *.plg 272 | 273 | # Visual Studio 6 workspace options file 274 | *.opt 275 | 276 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 277 | *.vbw 278 | 279 | # Visual Studio LightSwitch build output 280 | **/*.HTMLClient/GeneratedArtifacts 281 | **/*.DesktopClient/GeneratedArtifacts 282 | **/*.DesktopClient/ModelManifest.xml 283 | **/*.Server/GeneratedArtifacts 284 | **/*.Server/ModelManifest.xml 285 | _Pvt_Extensions 286 | 287 | # Paket dependency manager 288 | .paket/paket.exe 289 | paket-files/ 290 | 291 | # FAKE - F# Make 292 | .fake/ 293 | 294 | # JetBrains Rider 295 | .idea/ 296 | *.sln.iml 297 | 298 | # CodeRush personal settings 299 | .cr/personal 300 | 301 | # Python Tools for Visual Studio (PTVS) 302 | __pycache__/ 303 | *.pyc 304 | 305 | # Cake - Uncomment if you are using it 306 | # tools/** 307 | # !tools/packages.config 308 | 309 | # Tabs Studio 310 | *.tss 311 | 312 | # Telerik's JustMock configuration file 313 | *.jmconfig 314 | 315 | # BizTalk build output 316 | *.btp.cs 317 | *.btm.cs 318 | *.odx.cs 319 | *.xsd.cs 320 | 321 | # OpenCover UI analysis results 322 | OpenCover/ 323 | 324 | # Azure Stream Analytics local run output 325 | ASALocalRun/ 326 | 327 | # MSBuild Binary and Structured Log 328 | *.binlog 329 | 330 | # NVidia Nsight GPU debugger configuration file 331 | *.nvuser 332 | 333 | # MFractors (Xamarin productivity tool) working folder 334 | .mfractor/ 335 | 336 | # Local History for Visual Studio 337 | .localhistory/ 338 | 339 | # BeatPulse healthcheck temp database 340 | healthchecksdb -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/Data/Migrations/ApplicationDbContextModelSnapshot.cs: -------------------------------------------------------------------------------- 1 | // 2 | using IdentityServerAspNetIdentity.Data; 3 | using Microsoft.EntityFrameworkCore; 4 | using Microsoft.EntityFrameworkCore.Infrastructure; 5 | using Microsoft.EntityFrameworkCore.Metadata; 6 | using Microsoft.EntityFrameworkCore.Migrations; 7 | using Microsoft.EntityFrameworkCore.Storage; 8 | using Microsoft.EntityFrameworkCore.Storage.Internal; 9 | using System; 10 | 11 | namespace IdentityServerAspNetIdentity.Data.Migrations 12 | { 13 | [DbContext(typeof(ApplicationDbContext))] 14 | partial class ApplicationDbContextModelSnapshot : ModelSnapshot 15 | { 16 | protected override void BuildModel(ModelBuilder modelBuilder) 17 | { 18 | #pragma warning disable 612, 618 19 | modelBuilder 20 | .HasAnnotation("ProductVersion", "2.0.1-rtm-125"); 21 | 22 | modelBuilder.Entity("IdentityServerAspNetIdentity.Models.ApplicationUser", b => 23 | { 24 | b.Property("Id") 25 | .ValueGeneratedOnAdd(); 26 | 27 | b.Property("AccessFailedCount"); 28 | 29 | b.Property("ConcurrencyStamp") 30 | .IsConcurrencyToken(); 31 | 32 | b.Property("Email") 33 | .HasMaxLength(256); 34 | 35 | b.Property("EmailConfirmed"); 36 | 37 | b.Property("LockoutEnabled"); 38 | 39 | b.Property("LockoutEnd"); 40 | 41 | b.Property("NormalizedEmail") 42 | .HasMaxLength(256); 43 | 44 | b.Property("NormalizedUserName") 45 | .HasMaxLength(256); 46 | 47 | b.Property("PasswordHash"); 48 | 49 | b.Property("PhoneNumber"); 50 | 51 | b.Property("PhoneNumberConfirmed"); 52 | 53 | b.Property("SecurityStamp"); 54 | 55 | b.Property("TwoFactorEnabled"); 56 | 57 | b.Property("UserName") 58 | .HasMaxLength(256); 59 | 60 | b.HasKey("Id"); 61 | 62 | b.HasIndex("NormalizedEmail") 63 | .HasName("EmailIndex"); 64 | 65 | b.HasIndex("NormalizedUserName") 66 | .IsUnique() 67 | .HasName("UserNameIndex"); 68 | 69 | b.ToTable("AspNetUsers"); 70 | }); 71 | 72 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => 73 | { 74 | b.Property("Id") 75 | .ValueGeneratedOnAdd(); 76 | 77 | b.Property("ConcurrencyStamp") 78 | .IsConcurrencyToken(); 79 | 80 | b.Property("Name") 81 | .HasMaxLength(256); 82 | 83 | b.Property("NormalizedName") 84 | .HasMaxLength(256); 85 | 86 | b.HasKey("Id"); 87 | 88 | b.HasIndex("NormalizedName") 89 | .IsUnique() 90 | .HasName("RoleNameIndex"); 91 | 92 | b.ToTable("AspNetRoles"); 93 | }); 94 | 95 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => 96 | { 97 | b.Property("Id") 98 | .ValueGeneratedOnAdd(); 99 | 100 | b.Property("ClaimType"); 101 | 102 | b.Property("ClaimValue"); 103 | 104 | b.Property("RoleId") 105 | .IsRequired(); 106 | 107 | b.HasKey("Id"); 108 | 109 | b.HasIndex("RoleId"); 110 | 111 | b.ToTable("AspNetRoleClaims"); 112 | }); 113 | 114 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => 115 | { 116 | b.Property("Id") 117 | .ValueGeneratedOnAdd(); 118 | 119 | b.Property("ClaimType"); 120 | 121 | b.Property("ClaimValue"); 122 | 123 | b.Property("UserId") 124 | .IsRequired(); 125 | 126 | b.HasKey("Id"); 127 | 128 | b.HasIndex("UserId"); 129 | 130 | b.ToTable("AspNetUserClaims"); 131 | }); 132 | 133 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => 134 | { 135 | b.Property("LoginProvider"); 136 | 137 | b.Property("ProviderKey"); 138 | 139 | b.Property("ProviderDisplayName"); 140 | 141 | b.Property("UserId") 142 | .IsRequired(); 143 | 144 | b.HasKey("LoginProvider", "ProviderKey"); 145 | 146 | b.HasIndex("UserId"); 147 | 148 | b.ToTable("AspNetUserLogins"); 149 | }); 150 | 151 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => 152 | { 153 | b.Property("UserId"); 154 | 155 | b.Property("RoleId"); 156 | 157 | b.HasKey("UserId", "RoleId"); 158 | 159 | b.HasIndex("RoleId"); 160 | 161 | b.ToTable("AspNetUserRoles"); 162 | }); 163 | 164 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => 165 | { 166 | b.Property("UserId"); 167 | 168 | b.Property("LoginProvider"); 169 | 170 | b.Property("Name"); 171 | 172 | b.Property("Value"); 173 | 174 | b.HasKey("UserId", "LoginProvider", "Name"); 175 | 176 | b.ToTable("AspNetUserTokens"); 177 | }); 178 | 179 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => 180 | { 181 | b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") 182 | .WithMany() 183 | .HasForeignKey("RoleId") 184 | .OnDelete(DeleteBehavior.Cascade); 185 | }); 186 | 187 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => 188 | { 189 | b.HasOne("IdentityServerAspNetIdentity.Models.ApplicationUser") 190 | .WithMany() 191 | .HasForeignKey("UserId") 192 | .OnDelete(DeleteBehavior.Cascade); 193 | }); 194 | 195 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => 196 | { 197 | b.HasOne("IdentityServerAspNetIdentity.Models.ApplicationUser") 198 | .WithMany() 199 | .HasForeignKey("UserId") 200 | .OnDelete(DeleteBehavior.Cascade); 201 | }); 202 | 203 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => 204 | { 205 | b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") 206 | .WithMany() 207 | .HasForeignKey("RoleId") 208 | .OnDelete(DeleteBehavior.Cascade); 209 | 210 | b.HasOne("IdentityServerAspNetIdentity.Models.ApplicationUser") 211 | .WithMany() 212 | .HasForeignKey("UserId") 213 | .OnDelete(DeleteBehavior.Cascade); 214 | }); 215 | 216 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => 217 | { 218 | b.HasOne("IdentityServerAspNetIdentity.Models.ApplicationUser") 219 | .WithMany() 220 | .HasForeignKey("UserId") 221 | .OnDelete(DeleteBehavior.Cascade); 222 | }); 223 | #pragma warning restore 612, 618 224 | } 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/Data/Migrations/20180109192453_CreateIdentitySchema.Designer.cs: -------------------------------------------------------------------------------- 1 | // 2 | using IdentityServerAspNetIdentity.Data; 3 | using Microsoft.EntityFrameworkCore; 4 | using Microsoft.EntityFrameworkCore.Infrastructure; 5 | using Microsoft.EntityFrameworkCore.Metadata; 6 | using Microsoft.EntityFrameworkCore.Migrations; 7 | using Microsoft.EntityFrameworkCore.Storage; 8 | using Microsoft.EntityFrameworkCore.Storage.Internal; 9 | using System; 10 | 11 | namespace IdentityServerAspNetIdentity.Data.Migrations 12 | { 13 | [DbContext(typeof(ApplicationDbContext))] 14 | [Migration("20180109192453_CreateIdentitySchema")] 15 | partial class CreateIdentitySchema 16 | { 17 | protected override void BuildTargetModel(ModelBuilder modelBuilder) 18 | { 19 | #pragma warning disable 612, 618 20 | modelBuilder 21 | .HasAnnotation("ProductVersion", "2.0.1-rtm-125"); 22 | 23 | modelBuilder.Entity("IdentityServerAspNetIdentity.Models.ApplicationUser", b => 24 | { 25 | b.Property("Id") 26 | .ValueGeneratedOnAdd(); 27 | 28 | b.Property("AccessFailedCount"); 29 | 30 | b.Property("ConcurrencyStamp") 31 | .IsConcurrencyToken(); 32 | 33 | b.Property("Email") 34 | .HasMaxLength(256); 35 | 36 | b.Property("EmailConfirmed"); 37 | 38 | b.Property("LockoutEnabled"); 39 | 40 | b.Property("LockoutEnd"); 41 | 42 | b.Property("NormalizedEmail") 43 | .HasMaxLength(256); 44 | 45 | b.Property("NormalizedUserName") 46 | .HasMaxLength(256); 47 | 48 | b.Property("PasswordHash"); 49 | 50 | b.Property("PhoneNumber"); 51 | 52 | b.Property("PhoneNumberConfirmed"); 53 | 54 | b.Property("SecurityStamp"); 55 | 56 | b.Property("TwoFactorEnabled"); 57 | 58 | b.Property("UserName") 59 | .HasMaxLength(256); 60 | 61 | b.HasKey("Id"); 62 | 63 | b.HasIndex("NormalizedEmail") 64 | .HasName("EmailIndex"); 65 | 66 | b.HasIndex("NormalizedUserName") 67 | .IsUnique() 68 | .HasName("UserNameIndex"); 69 | 70 | b.ToTable("AspNetUsers"); 71 | }); 72 | 73 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => 74 | { 75 | b.Property("Id") 76 | .ValueGeneratedOnAdd(); 77 | 78 | b.Property("ConcurrencyStamp") 79 | .IsConcurrencyToken(); 80 | 81 | b.Property("Name") 82 | .HasMaxLength(256); 83 | 84 | b.Property("NormalizedName") 85 | .HasMaxLength(256); 86 | 87 | b.HasKey("Id"); 88 | 89 | b.HasIndex("NormalizedName") 90 | .IsUnique() 91 | .HasName("RoleNameIndex"); 92 | 93 | b.ToTable("AspNetRoles"); 94 | }); 95 | 96 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => 97 | { 98 | b.Property("Id") 99 | .ValueGeneratedOnAdd(); 100 | 101 | b.Property("ClaimType"); 102 | 103 | b.Property("ClaimValue"); 104 | 105 | b.Property("RoleId") 106 | .IsRequired(); 107 | 108 | b.HasKey("Id"); 109 | 110 | b.HasIndex("RoleId"); 111 | 112 | b.ToTable("AspNetRoleClaims"); 113 | }); 114 | 115 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => 116 | { 117 | b.Property("Id") 118 | .ValueGeneratedOnAdd(); 119 | 120 | b.Property("ClaimType"); 121 | 122 | b.Property("ClaimValue"); 123 | 124 | b.Property("UserId") 125 | .IsRequired(); 126 | 127 | b.HasKey("Id"); 128 | 129 | b.HasIndex("UserId"); 130 | 131 | b.ToTable("AspNetUserClaims"); 132 | }); 133 | 134 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => 135 | { 136 | b.Property("LoginProvider"); 137 | 138 | b.Property("ProviderKey"); 139 | 140 | b.Property("ProviderDisplayName"); 141 | 142 | b.Property("UserId") 143 | .IsRequired(); 144 | 145 | b.HasKey("LoginProvider", "ProviderKey"); 146 | 147 | b.HasIndex("UserId"); 148 | 149 | b.ToTable("AspNetUserLogins"); 150 | }); 151 | 152 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => 153 | { 154 | b.Property("UserId"); 155 | 156 | b.Property("RoleId"); 157 | 158 | b.HasKey("UserId", "RoleId"); 159 | 160 | b.HasIndex("RoleId"); 161 | 162 | b.ToTable("AspNetUserRoles"); 163 | }); 164 | 165 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => 166 | { 167 | b.Property("UserId"); 168 | 169 | b.Property("LoginProvider"); 170 | 171 | b.Property("Name"); 172 | 173 | b.Property("Value"); 174 | 175 | b.HasKey("UserId", "LoginProvider", "Name"); 176 | 177 | b.ToTable("AspNetUserTokens"); 178 | }); 179 | 180 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => 181 | { 182 | b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") 183 | .WithMany() 184 | .HasForeignKey("RoleId") 185 | .OnDelete(DeleteBehavior.Cascade); 186 | }); 187 | 188 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => 189 | { 190 | b.HasOne("IdentityServerAspNetIdentity.Models.ApplicationUser") 191 | .WithMany() 192 | .HasForeignKey("UserId") 193 | .OnDelete(DeleteBehavior.Cascade); 194 | }); 195 | 196 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => 197 | { 198 | b.HasOne("IdentityServerAspNetIdentity.Models.ApplicationUser") 199 | .WithMany() 200 | .HasForeignKey("UserId") 201 | .OnDelete(DeleteBehavior.Cascade); 202 | }); 203 | 204 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => 205 | { 206 | b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") 207 | .WithMany() 208 | .HasForeignKey("RoleId") 209 | .OnDelete(DeleteBehavior.Cascade); 210 | 211 | b.HasOne("IdentityServerAspNetIdentity.Models.ApplicationUser") 212 | .WithMany() 213 | .HasForeignKey("UserId") 214 | .OnDelete(DeleteBehavior.Cascade); 215 | }); 216 | 217 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => 218 | { 219 | b.HasOne("IdentityServerAspNetIdentity.Models.ApplicationUser") 220 | .WithMany() 221 | .HasForeignKey("UserId") 222 | .OnDelete(DeleteBehavior.Cascade); 223 | }); 224 | #pragma warning restore 612, 618 225 | } 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /BlazorId_App/wwwroot/css/open-iconic/font/css/open-iconic-bootstrap.min.css: -------------------------------------------------------------------------------- 1 | @font-face{font-family:Icons;src:url(../fonts/open-iconic.eot);src:url(../fonts/open-iconic.eot?#iconic-sm) format('embedded-opentype'),url(../fonts/open-iconic.woff) format('woff'),url(../fonts/open-iconic.ttf) format('truetype'),url(../fonts/open-iconic.otf) format('opentype'),url(../fonts/open-iconic.svg#iconic-sm) format('svg');font-weight:400;font-style:normal}.oi{position:relative;top:1px;display:inline-block;speak:none;font-family:Icons;font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.oi:empty:before{width:1em;text-align:center;box-sizing:content-box}.oi.oi-align-center:before{text-align:center}.oi.oi-align-left:before{text-align:left}.oi.oi-align-right:before{text-align:right}.oi.oi-flip-horizontal:before{-webkit-transform:scale(-1,1);-ms-transform:scale(-1,1);transform:scale(-1,1)}.oi.oi-flip-vertical:before{-webkit-transform:scale(1,-1);-ms-transform:scale(-1,1);transform:scale(1,-1)}.oi.oi-flip-horizontal-vertical:before{-webkit-transform:scale(-1,-1);-ms-transform:scale(-1,1);transform:scale(-1,-1)}.oi-account-login:before{content:'\e000'}.oi-account-logout:before{content:'\e001'}.oi-action-redo:before{content:'\e002'}.oi-action-undo:before{content:'\e003'}.oi-align-center:before{content:'\e004'}.oi-align-left:before{content:'\e005'}.oi-align-right:before{content:'\e006'}.oi-aperture:before{content:'\e007'}.oi-arrow-bottom:before{content:'\e008'}.oi-arrow-circle-bottom:before{content:'\e009'}.oi-arrow-circle-left:before{content:'\e00a'}.oi-arrow-circle-right:before{content:'\e00b'}.oi-arrow-circle-top:before{content:'\e00c'}.oi-arrow-left:before{content:'\e00d'}.oi-arrow-right:before{content:'\e00e'}.oi-arrow-thick-bottom:before{content:'\e00f'}.oi-arrow-thick-left:before{content:'\e010'}.oi-arrow-thick-right:before{content:'\e011'}.oi-arrow-thick-top:before{content:'\e012'}.oi-arrow-top:before{content:'\e013'}.oi-audio-spectrum:before{content:'\e014'}.oi-audio:before{content:'\e015'}.oi-badge:before{content:'\e016'}.oi-ban:before{content:'\e017'}.oi-bar-chart:before{content:'\e018'}.oi-basket:before{content:'\e019'}.oi-battery-empty:before{content:'\e01a'}.oi-battery-full:before{content:'\e01b'}.oi-beaker:before{content:'\e01c'}.oi-bell:before{content:'\e01d'}.oi-bluetooth:before{content:'\e01e'}.oi-bold:before{content:'\e01f'}.oi-bolt:before{content:'\e020'}.oi-book:before{content:'\e021'}.oi-bookmark:before{content:'\e022'}.oi-box:before{content:'\e023'}.oi-briefcase:before{content:'\e024'}.oi-british-pound:before{content:'\e025'}.oi-browser:before{content:'\e026'}.oi-brush:before{content:'\e027'}.oi-bug:before{content:'\e028'}.oi-bullhorn:before{content:'\e029'}.oi-calculator:before{content:'\e02a'}.oi-calendar:before{content:'\e02b'}.oi-camera-slr:before{content:'\e02c'}.oi-caret-bottom:before{content:'\e02d'}.oi-caret-left:before{content:'\e02e'}.oi-caret-right:before{content:'\e02f'}.oi-caret-top:before{content:'\e030'}.oi-cart:before{content:'\e031'}.oi-chat:before{content:'\e032'}.oi-check:before{content:'\e033'}.oi-chevron-bottom:before{content:'\e034'}.oi-chevron-left:before{content:'\e035'}.oi-chevron-right:before{content:'\e036'}.oi-chevron-top:before{content:'\e037'}.oi-circle-check:before{content:'\e038'}.oi-circle-x:before{content:'\e039'}.oi-clipboard:before{content:'\e03a'}.oi-clock:before{content:'\e03b'}.oi-cloud-download:before{content:'\e03c'}.oi-cloud-upload:before{content:'\e03d'}.oi-cloud:before{content:'\e03e'}.oi-cloudy:before{content:'\e03f'}.oi-code:before{content:'\e040'}.oi-cog:before{content:'\e041'}.oi-collapse-down:before{content:'\e042'}.oi-collapse-left:before{content:'\e043'}.oi-collapse-right:before{content:'\e044'}.oi-collapse-up:before{content:'\e045'}.oi-command:before{content:'\e046'}.oi-comment-square:before{content:'\e047'}.oi-compass:before{content:'\e048'}.oi-contrast:before{content:'\e049'}.oi-copywriting:before{content:'\e04a'}.oi-credit-card:before{content:'\e04b'}.oi-crop:before{content:'\e04c'}.oi-dashboard:before{content:'\e04d'}.oi-data-transfer-download:before{content:'\e04e'}.oi-data-transfer-upload:before{content:'\e04f'}.oi-delete:before{content:'\e050'}.oi-dial:before{content:'\e051'}.oi-document:before{content:'\e052'}.oi-dollar:before{content:'\e053'}.oi-double-quote-sans-left:before{content:'\e054'}.oi-double-quote-sans-right:before{content:'\e055'}.oi-double-quote-serif-left:before{content:'\e056'}.oi-double-quote-serif-right:before{content:'\e057'}.oi-droplet:before{content:'\e058'}.oi-eject:before{content:'\e059'}.oi-elevator:before{content:'\e05a'}.oi-ellipses:before{content:'\e05b'}.oi-envelope-closed:before{content:'\e05c'}.oi-envelope-open:before{content:'\e05d'}.oi-euro:before{content:'\e05e'}.oi-excerpt:before{content:'\e05f'}.oi-expand-down:before{content:'\e060'}.oi-expand-left:before{content:'\e061'}.oi-expand-right:before{content:'\e062'}.oi-expand-up:before{content:'\e063'}.oi-external-link:before{content:'\e064'}.oi-eye:before{content:'\e065'}.oi-eyedropper:before{content:'\e066'}.oi-file:before{content:'\e067'}.oi-fire:before{content:'\e068'}.oi-flag:before{content:'\e069'}.oi-flash:before{content:'\e06a'}.oi-folder:before{content:'\e06b'}.oi-fork:before{content:'\e06c'}.oi-fullscreen-enter:before{content:'\e06d'}.oi-fullscreen-exit:before{content:'\e06e'}.oi-globe:before{content:'\e06f'}.oi-graph:before{content:'\e070'}.oi-grid-four-up:before{content:'\e071'}.oi-grid-three-up:before{content:'\e072'}.oi-grid-two-up:before{content:'\e073'}.oi-hard-drive:before{content:'\e074'}.oi-header:before{content:'\e075'}.oi-headphones:before{content:'\e076'}.oi-heart:before{content:'\e077'}.oi-home:before{content:'\e078'}.oi-image:before{content:'\e079'}.oi-inbox:before{content:'\e07a'}.oi-infinity:before{content:'\e07b'}.oi-info:before{content:'\e07c'}.oi-italic:before{content:'\e07d'}.oi-justify-center:before{content:'\e07e'}.oi-justify-left:before{content:'\e07f'}.oi-justify-right:before{content:'\e080'}.oi-key:before{content:'\e081'}.oi-laptop:before{content:'\e082'}.oi-layers:before{content:'\e083'}.oi-lightbulb:before{content:'\e084'}.oi-link-broken:before{content:'\e085'}.oi-link-intact:before{content:'\e086'}.oi-list-rich:before{content:'\e087'}.oi-list:before{content:'\e088'}.oi-location:before{content:'\e089'}.oi-lock-locked:before{content:'\e08a'}.oi-lock-unlocked:before{content:'\e08b'}.oi-loop-circular:before{content:'\e08c'}.oi-loop-square:before{content:'\e08d'}.oi-loop:before{content:'\e08e'}.oi-magnifying-glass:before{content:'\e08f'}.oi-map-marker:before{content:'\e090'}.oi-map:before{content:'\e091'}.oi-media-pause:before{content:'\e092'}.oi-media-play:before{content:'\e093'}.oi-media-record:before{content:'\e094'}.oi-media-skip-backward:before{content:'\e095'}.oi-media-skip-forward:before{content:'\e096'}.oi-media-step-backward:before{content:'\e097'}.oi-media-step-forward:before{content:'\e098'}.oi-media-stop:before{content:'\e099'}.oi-medical-cross:before{content:'\e09a'}.oi-menu:before{content:'\e09b'}.oi-microphone:before{content:'\e09c'}.oi-minus:before{content:'\e09d'}.oi-monitor:before{content:'\e09e'}.oi-moon:before{content:'\e09f'}.oi-move:before{content:'\e0a0'}.oi-musical-note:before{content:'\e0a1'}.oi-paperclip:before{content:'\e0a2'}.oi-pencil:before{content:'\e0a3'}.oi-people:before{content:'\e0a4'}.oi-person:before{content:'\e0a5'}.oi-phone:before{content:'\e0a6'}.oi-pie-chart:before{content:'\e0a7'}.oi-pin:before{content:'\e0a8'}.oi-play-circle:before{content:'\e0a9'}.oi-plus:before{content:'\e0aa'}.oi-power-standby:before{content:'\e0ab'}.oi-print:before{content:'\e0ac'}.oi-project:before{content:'\e0ad'}.oi-pulse:before{content:'\e0ae'}.oi-puzzle-piece:before{content:'\e0af'}.oi-question-mark:before{content:'\e0b0'}.oi-rain:before{content:'\e0b1'}.oi-random:before{content:'\e0b2'}.oi-reload:before{content:'\e0b3'}.oi-resize-both:before{content:'\e0b4'}.oi-resize-height:before{content:'\e0b5'}.oi-resize-width:before{content:'\e0b6'}.oi-rss-alt:before{content:'\e0b7'}.oi-rss:before{content:'\e0b8'}.oi-script:before{content:'\e0b9'}.oi-share-boxed:before{content:'\e0ba'}.oi-share:before{content:'\e0bb'}.oi-shield:before{content:'\e0bc'}.oi-signal:before{content:'\e0bd'}.oi-signpost:before{content:'\e0be'}.oi-sort-ascending:before{content:'\e0bf'}.oi-sort-descending:before{content:'\e0c0'}.oi-spreadsheet:before{content:'\e0c1'}.oi-star:before{content:'\e0c2'}.oi-sun:before{content:'\e0c3'}.oi-tablet:before{content:'\e0c4'}.oi-tag:before{content:'\e0c5'}.oi-tags:before{content:'\e0c6'}.oi-target:before{content:'\e0c7'}.oi-task:before{content:'\e0c8'}.oi-terminal:before{content:'\e0c9'}.oi-text:before{content:'\e0ca'}.oi-thumb-down:before{content:'\e0cb'}.oi-thumb-up:before{content:'\e0cc'}.oi-timer:before{content:'\e0cd'}.oi-transfer:before{content:'\e0ce'}.oi-trash:before{content:'\e0cf'}.oi-underline:before{content:'\e0d0'}.oi-vertical-align-bottom:before{content:'\e0d1'}.oi-vertical-align-center:before{content:'\e0d2'}.oi-vertical-align-top:before{content:'\e0d3'}.oi-video:before{content:'\e0d4'}.oi-volume-high:before{content:'\e0d5'}.oi-volume-low:before{content:'\e0d6'}.oi-volume-off:before{content:'\e0d7'}.oi-warning:before{content:'\e0d8'}.oi-wifi:before{content:'\e0d9'}.oi-wrench:before{content:'\e0da'}.oi-x:before{content:'\e0db'}.oi-yen:before{content:'\e0dc'}.oi-zoom-in:before{content:'\e0dd'}.oi-zoom-out:before{content:'\e0de'} -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/Data/Migrations/20180109192453_CreateIdentitySchema.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations; 2 | using System; 3 | 4 | namespace IdentityServerAspNetIdentity.Data.Migrations 5 | { 6 | public partial class CreateIdentitySchema : Migration 7 | { 8 | protected override void Up(MigrationBuilder migrationBuilder) 9 | { 10 | migrationBuilder.CreateTable( 11 | name: "AspNetRoles", 12 | columns: table => new 13 | { 14 | Id = table.Column(nullable: false), 15 | ConcurrencyStamp = table.Column(nullable: true), 16 | Name = table.Column(maxLength: 256, nullable: true), 17 | NormalizedName = table.Column(maxLength: 256, nullable: true) 18 | }, 19 | constraints: table => 20 | { 21 | table.PrimaryKey("PK_AspNetRoles", x => x.Id); 22 | }); 23 | 24 | migrationBuilder.CreateTable( 25 | name: "AspNetUsers", 26 | columns: table => new 27 | { 28 | Id = table.Column(nullable: false), 29 | AccessFailedCount = table.Column(nullable: false), 30 | ConcurrencyStamp = table.Column(nullable: true), 31 | Email = table.Column(maxLength: 256, nullable: true), 32 | EmailConfirmed = table.Column(nullable: false), 33 | LockoutEnabled = table.Column(nullable: false), 34 | LockoutEnd = table.Column(nullable: true), 35 | NormalizedEmail = table.Column(maxLength: 256, nullable: true), 36 | NormalizedUserName = table.Column(maxLength: 256, nullable: true), 37 | PasswordHash = table.Column(nullable: true), 38 | PhoneNumber = table.Column(nullable: true), 39 | PhoneNumberConfirmed = table.Column(nullable: false), 40 | SecurityStamp = table.Column(nullable: true), 41 | TwoFactorEnabled = table.Column(nullable: false), 42 | UserName = table.Column(maxLength: 256, nullable: true) 43 | }, 44 | constraints: table => 45 | { 46 | table.PrimaryKey("PK_AspNetUsers", x => x.Id); 47 | }); 48 | 49 | migrationBuilder.CreateTable( 50 | name: "AspNetRoleClaims", 51 | columns: table => new 52 | { 53 | Id = table.Column(nullable: false) 54 | .Annotation("Sqlite:Autoincrement", true), 55 | ClaimType = table.Column(nullable: true), 56 | ClaimValue = table.Column(nullable: true), 57 | RoleId = table.Column(nullable: false) 58 | }, 59 | constraints: table => 60 | { 61 | table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id); 62 | table.ForeignKey( 63 | name: "FK_AspNetRoleClaims_AspNetRoles_RoleId", 64 | column: x => x.RoleId, 65 | principalTable: "AspNetRoles", 66 | principalColumn: "Id", 67 | onDelete: ReferentialAction.Cascade); 68 | }); 69 | 70 | migrationBuilder.CreateTable( 71 | name: "AspNetUserClaims", 72 | columns: table => new 73 | { 74 | Id = table.Column(nullable: false) 75 | .Annotation("Sqlite:Autoincrement", true), 76 | ClaimType = table.Column(nullable: true), 77 | ClaimValue = table.Column(nullable: true), 78 | UserId = table.Column(nullable: false) 79 | }, 80 | constraints: table => 81 | { 82 | table.PrimaryKey("PK_AspNetUserClaims", x => x.Id); 83 | table.ForeignKey( 84 | name: "FK_AspNetUserClaims_AspNetUsers_UserId", 85 | column: x => x.UserId, 86 | principalTable: "AspNetUsers", 87 | principalColumn: "Id", 88 | onDelete: ReferentialAction.Cascade); 89 | }); 90 | 91 | migrationBuilder.CreateTable( 92 | name: "AspNetUserLogins", 93 | columns: table => new 94 | { 95 | LoginProvider = table.Column(nullable: false), 96 | ProviderKey = table.Column(nullable: false), 97 | ProviderDisplayName = table.Column(nullable: true), 98 | UserId = table.Column(nullable: false) 99 | }, 100 | constraints: table => 101 | { 102 | table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey }); 103 | table.ForeignKey( 104 | name: "FK_AspNetUserLogins_AspNetUsers_UserId", 105 | column: x => x.UserId, 106 | principalTable: "AspNetUsers", 107 | principalColumn: "Id", 108 | onDelete: ReferentialAction.Cascade); 109 | }); 110 | 111 | migrationBuilder.CreateTable( 112 | name: "AspNetUserRoles", 113 | columns: table => new 114 | { 115 | UserId = table.Column(nullable: false), 116 | RoleId = table.Column(nullable: false) 117 | }, 118 | constraints: table => 119 | { 120 | table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId }); 121 | table.ForeignKey( 122 | name: "FK_AspNetUserRoles_AspNetRoles_RoleId", 123 | column: x => x.RoleId, 124 | principalTable: "AspNetRoles", 125 | principalColumn: "Id", 126 | onDelete: ReferentialAction.Cascade); 127 | table.ForeignKey( 128 | name: "FK_AspNetUserRoles_AspNetUsers_UserId", 129 | column: x => x.UserId, 130 | principalTable: "AspNetUsers", 131 | principalColumn: "Id", 132 | onDelete: ReferentialAction.Cascade); 133 | }); 134 | 135 | migrationBuilder.CreateTable( 136 | name: "AspNetUserTokens", 137 | columns: table => new 138 | { 139 | UserId = table.Column(nullable: false), 140 | LoginProvider = table.Column(nullable: false), 141 | Name = table.Column(nullable: false), 142 | Value = table.Column(nullable: true) 143 | }, 144 | constraints: table => 145 | { 146 | table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name }); 147 | table.ForeignKey( 148 | name: "FK_AspNetUserTokens_AspNetUsers_UserId", 149 | column: x => x.UserId, 150 | principalTable: "AspNetUsers", 151 | principalColumn: "Id", 152 | onDelete: ReferentialAction.Cascade); 153 | }); 154 | 155 | migrationBuilder.CreateIndex( 156 | name: "IX_AspNetRoleClaims_RoleId", 157 | table: "AspNetRoleClaims", 158 | column: "RoleId"); 159 | 160 | migrationBuilder.CreateIndex( 161 | name: "RoleNameIndex", 162 | table: "AspNetRoles", 163 | column: "NormalizedName", 164 | unique: true); 165 | 166 | migrationBuilder.CreateIndex( 167 | name: "IX_AspNetUserClaims_UserId", 168 | table: "AspNetUserClaims", 169 | column: "UserId"); 170 | 171 | migrationBuilder.CreateIndex( 172 | name: "IX_AspNetUserLogins_UserId", 173 | table: "AspNetUserLogins", 174 | column: "UserId"); 175 | 176 | migrationBuilder.CreateIndex( 177 | name: "IX_AspNetUserRoles_RoleId", 178 | table: "AspNetUserRoles", 179 | column: "RoleId"); 180 | 181 | migrationBuilder.CreateIndex( 182 | name: "EmailIndex", 183 | table: "AspNetUsers", 184 | column: "NormalizedEmail"); 185 | 186 | migrationBuilder.CreateIndex( 187 | name: "UserNameIndex", 188 | table: "AspNetUsers", 189 | column: "NormalizedUserName", 190 | unique: true); 191 | } 192 | 193 | protected override void Down(MigrationBuilder migrationBuilder) 194 | { 195 | migrationBuilder.DropTable( 196 | name: "AspNetRoleClaims"); 197 | 198 | migrationBuilder.DropTable( 199 | name: "AspNetUserClaims"); 200 | 201 | migrationBuilder.DropTable( 202 | name: "AspNetUserLogins"); 203 | 204 | migrationBuilder.DropTable( 205 | name: "AspNetUserRoles"); 206 | 207 | migrationBuilder.DropTable( 208 | name: "AspNetUserTokens"); 209 | 210 | migrationBuilder.DropTable( 211 | name: "AspNetRoles"); 212 | 213 | migrationBuilder.DropTable( 214 | name: "AspNetUsers"); 215 | } 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/Quickstart/Device/DeviceController.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Brock Allen & Dominick Baier. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | 5 | using System; 6 | using System.Linq; 7 | using System.Threading.Tasks; 8 | using IdentityServer4.Events; 9 | using IdentityServer4.Extensions; 10 | using IdentityServer4.Models; 11 | using IdentityServer4.Services; 12 | using IdentityServer4.Stores; 13 | using Microsoft.AspNetCore.Authorization; 14 | using Microsoft.AspNetCore.Mvc; 15 | using Microsoft.Extensions.Logging; 16 | 17 | namespace IdentityServer4.Quickstart.UI.Device 18 | { 19 | [Authorize] 20 | [SecurityHeaders] 21 | public class DeviceController : Controller 22 | { 23 | private readonly IDeviceFlowInteractionService _interaction; 24 | private readonly IClientStore _clientStore; 25 | private readonly IResourceStore _resourceStore; 26 | private readonly IEventService _events; 27 | private readonly ILogger _logger; 28 | 29 | public DeviceController( 30 | IDeviceFlowInteractionService interaction, 31 | IClientStore clientStore, 32 | IResourceStore resourceStore, 33 | IEventService eventService, 34 | ILogger logger) 35 | { 36 | _interaction = interaction; 37 | _clientStore = clientStore; 38 | _resourceStore = resourceStore; 39 | _events = eventService; 40 | _logger = logger; 41 | } 42 | 43 | [HttpGet] 44 | public async Task Index([FromQuery(Name = "user_code")] string userCode) 45 | { 46 | if (string.IsNullOrWhiteSpace(userCode)) return View("UserCodeCapture"); 47 | 48 | var vm = await BuildViewModelAsync(userCode); 49 | if (vm == null) return View("Error"); 50 | 51 | vm.ConfirmUserCode = true; 52 | return View("UserCodeConfirmation", vm); 53 | } 54 | 55 | [HttpPost] 56 | [ValidateAntiForgeryToken] 57 | public async Task UserCodeCapture(string userCode) 58 | { 59 | var vm = await BuildViewModelAsync(userCode); 60 | if (vm == null) return View("Error"); 61 | 62 | return View("UserCodeConfirmation", vm); 63 | } 64 | 65 | [HttpPost] 66 | [ValidateAntiForgeryToken] 67 | public async Task Callback(DeviceAuthorizationInputModel model) 68 | { 69 | if (model == null) throw new ArgumentNullException(nameof(model)); 70 | 71 | var result = await ProcessConsent(model); 72 | if (result.HasValidationError) return View("Error"); 73 | 74 | return View("Success"); 75 | } 76 | 77 | private async Task ProcessConsent(DeviceAuthorizationInputModel model) 78 | { 79 | var result = new ProcessConsentResult(); 80 | 81 | var request = await _interaction.GetAuthorizationContextAsync(model.UserCode); 82 | if (request == null) return result; 83 | 84 | ConsentResponse grantedConsent = null; 85 | 86 | // user clicked 'no' - send back the standard 'access_denied' response 87 | if (model.Button == "no") 88 | { 89 | grantedConsent = ConsentResponse.Denied; 90 | 91 | // emit event 92 | await _events.RaiseAsync(new ConsentDeniedEvent(User.GetSubjectId(), request.ClientId, request.ScopesRequested)); 93 | } 94 | // user clicked 'yes' - validate the data 95 | else if (model.Button == "yes") 96 | { 97 | // if the user consented to some scope, build the response model 98 | if (model.ScopesConsented != null && model.ScopesConsented.Any()) 99 | { 100 | var scopes = model.ScopesConsented; 101 | if (ConsentOptions.EnableOfflineAccess == false) 102 | { 103 | scopes = scopes.Where(x => x != IdentityServer4.IdentityServerConstants.StandardScopes.OfflineAccess); 104 | } 105 | 106 | grantedConsent = new ConsentResponse 107 | { 108 | RememberConsent = model.RememberConsent, 109 | ScopesConsented = scopes.ToArray() 110 | }; 111 | 112 | // emit event 113 | await _events.RaiseAsync(new ConsentGrantedEvent(User.GetSubjectId(), request.ClientId, request.ScopesRequested, grantedConsent.ScopesConsented, grantedConsent.RememberConsent)); 114 | } 115 | else 116 | { 117 | result.ValidationError = ConsentOptions.MustChooseOneErrorMessage; 118 | } 119 | } 120 | else 121 | { 122 | result.ValidationError = ConsentOptions.InvalidSelectionErrorMessage; 123 | } 124 | 125 | if (grantedConsent != null) 126 | { 127 | // communicate outcome of consent back to identityserver 128 | await _interaction.HandleRequestAsync(model.UserCode, grantedConsent); 129 | 130 | // indicate that's it ok to redirect back to authorization endpoint 131 | result.RedirectUri = model.ReturnUrl; 132 | result.ClientId = request.ClientId; 133 | } 134 | else 135 | { 136 | // we need to redisplay the consent UI 137 | result.ViewModel = await BuildViewModelAsync(model.UserCode, model); 138 | } 139 | 140 | return result; 141 | } 142 | 143 | private async Task BuildViewModelAsync(string userCode, DeviceAuthorizationInputModel model = null) 144 | { 145 | var request = await _interaction.GetAuthorizationContextAsync(userCode); 146 | if (request != null) 147 | { 148 | var client = await _clientStore.FindEnabledClientByIdAsync(request.ClientId); 149 | if (client != null) 150 | { 151 | var resources = await _resourceStore.FindEnabledResourcesByScopeAsync(request.ScopesRequested); 152 | if (resources != null && (resources.IdentityResources.Any() || resources.ApiResources.Any())) 153 | { 154 | return CreateConsentViewModel(userCode, model, client, resources); 155 | } 156 | else 157 | { 158 | _logger.LogError("No scopes matching: {0}", request.ScopesRequested.Aggregate((x, y) => x + ", " + y)); 159 | } 160 | } 161 | else 162 | { 163 | _logger.LogError("Invalid client id: {0}", request.ClientId); 164 | } 165 | } 166 | 167 | return null; 168 | } 169 | 170 | private DeviceAuthorizationViewModel CreateConsentViewModel(string userCode, DeviceAuthorizationInputModel model, Client client, Resources resources) 171 | { 172 | var vm = new DeviceAuthorizationViewModel 173 | { 174 | UserCode = userCode, 175 | 176 | RememberConsent = model?.RememberConsent ?? true, 177 | ScopesConsented = model?.ScopesConsented ?? Enumerable.Empty(), 178 | 179 | ClientName = client.ClientName ?? client.ClientId, 180 | ClientUrl = client.ClientUri, 181 | ClientLogoUrl = client.LogoUri, 182 | AllowRememberConsent = client.AllowRememberConsent 183 | }; 184 | 185 | vm.IdentityScopes = resources.IdentityResources.Select(x => CreateScopeViewModel(x, vm.ScopesConsented.Contains(x.Name) || model == null)).ToArray(); 186 | vm.ResourceScopes = resources.ApiResources.SelectMany(x => x.Scopes).Select(x => CreateScopeViewModel(x, vm.ScopesConsented.Contains(x.Name) || model == null)).ToArray(); 187 | if (ConsentOptions.EnableOfflineAccess && resources.OfflineAccess) 188 | { 189 | vm.ResourceScopes = vm.ResourceScopes.Union(new[] 190 | { 191 | GetOfflineAccessScope(vm.ScopesConsented.Contains(IdentityServer4.IdentityServerConstants.StandardScopes.OfflineAccess) || model == null) 192 | }); 193 | } 194 | 195 | return vm; 196 | } 197 | 198 | private ScopeViewModel CreateScopeViewModel(IdentityResource identity, bool check) 199 | { 200 | return new ScopeViewModel 201 | { 202 | Name = identity.Name, 203 | DisplayName = identity.DisplayName, 204 | Description = identity.Description, 205 | Emphasize = identity.Emphasize, 206 | Required = identity.Required, 207 | Checked = check || identity.Required 208 | }; 209 | } 210 | 211 | public ScopeViewModel CreateScopeViewModel(Scope scope, bool check) 212 | { 213 | return new ScopeViewModel 214 | { 215 | Name = scope.Name, 216 | DisplayName = scope.DisplayName, 217 | Description = scope.Description, 218 | Emphasize = scope.Emphasize, 219 | Required = scope.Required, 220 | Checked = check || scope.Required 221 | }; 222 | } 223 | private ScopeViewModel GetOfflineAccessScope(bool check) 224 | { 225 | return new ScopeViewModel 226 | { 227 | Name = IdentityServer4.IdentityServerConstants.StandardScopes.OfflineAccess, 228 | DisplayName = ConsentOptions.OfflineAccessDisplayName, 229 | Description = ConsentOptions.OfflineAccessDescription, 230 | Emphasize = true, 231 | Checked = check 232 | }; 233 | } 234 | } 235 | } -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/Quickstart/Consent/ConsentController.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Brock Allen & Dominick Baier. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | 5 | using IdentityServer4.Events; 6 | using IdentityServer4.Models; 7 | using IdentityServer4.Services; 8 | using IdentityServer4.Stores; 9 | using IdentityServer4.Extensions; 10 | using Microsoft.AspNetCore.Authorization; 11 | using Microsoft.AspNetCore.Mvc; 12 | using Microsoft.Extensions.Logging; 13 | using System.Linq; 14 | using System.Threading.Tasks; 15 | 16 | namespace IdentityServer4.Quickstart.UI 17 | { 18 | /// 19 | /// This controller processes the consent UI 20 | /// 21 | [SecurityHeaders] 22 | [Authorize] 23 | public class ConsentController : Controller 24 | { 25 | private readonly IIdentityServerInteractionService _interaction; 26 | private readonly IClientStore _clientStore; 27 | private readonly IResourceStore _resourceStore; 28 | private readonly IEventService _events; 29 | private readonly ILogger _logger; 30 | 31 | public ConsentController( 32 | IIdentityServerInteractionService interaction, 33 | IClientStore clientStore, 34 | IResourceStore resourceStore, 35 | IEventService events, 36 | ILogger logger) 37 | { 38 | _interaction = interaction; 39 | _clientStore = clientStore; 40 | _resourceStore = resourceStore; 41 | _events = events; 42 | _logger = logger; 43 | } 44 | 45 | /// 46 | /// Shows the consent screen 47 | /// 48 | /// 49 | /// 50 | [HttpGet] 51 | public async Task Index(string returnUrl) 52 | { 53 | var vm = await BuildViewModelAsync(returnUrl); 54 | if (vm != null) 55 | { 56 | return View("Index", vm); 57 | } 58 | 59 | return View("Error"); 60 | } 61 | 62 | /// 63 | /// Handles the consent screen postback 64 | /// 65 | [HttpPost] 66 | [ValidateAntiForgeryToken] 67 | public async Task Index(ConsentInputModel model) 68 | { 69 | var result = await ProcessConsent(model); 70 | 71 | if (result.IsRedirect) 72 | { 73 | if (await _clientStore.IsPkceClientAsync(result.ClientId)) 74 | { 75 | // if the client is PKCE then we assume it's native, so this change in how to 76 | // return the response is for better UX for the end user. 77 | return View("Redirect", new RedirectViewModel { RedirectUrl = result.RedirectUri }); 78 | } 79 | 80 | return Redirect(result.RedirectUri); 81 | } 82 | 83 | if (result.HasValidationError) 84 | { 85 | ModelState.AddModelError(string.Empty, result.ValidationError); 86 | } 87 | 88 | if (result.ShowView) 89 | { 90 | return View("Index", result.ViewModel); 91 | } 92 | 93 | return View("Error"); 94 | } 95 | 96 | /*****************************************/ 97 | /* helper APIs for the ConsentController */ 98 | /*****************************************/ 99 | private async Task ProcessConsent(ConsentInputModel model) 100 | { 101 | var result = new ProcessConsentResult(); 102 | 103 | // validate return url is still valid 104 | var request = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl); 105 | if (request == null) return result; 106 | 107 | ConsentResponse grantedConsent = null; 108 | 109 | // user clicked 'no' - send back the standard 'access_denied' response 110 | if (model?.Button == "no") 111 | { 112 | grantedConsent = ConsentResponse.Denied; 113 | 114 | // emit event 115 | await _events.RaiseAsync(new ConsentDeniedEvent(User.GetSubjectId(), request.ClientId, request.ScopesRequested)); 116 | } 117 | // user clicked 'yes' - validate the data 118 | else if (model?.Button == "yes") 119 | { 120 | // if the user consented to some scope, build the response model 121 | if (model.ScopesConsented != null && model.ScopesConsented.Any()) 122 | { 123 | var scopes = model.ScopesConsented; 124 | if (ConsentOptions.EnableOfflineAccess == false) 125 | { 126 | scopes = scopes.Where(x => x != IdentityServer4.IdentityServerConstants.StandardScopes.OfflineAccess); 127 | } 128 | 129 | grantedConsent = new ConsentResponse 130 | { 131 | RememberConsent = model.RememberConsent, 132 | ScopesConsented = scopes.ToArray() 133 | }; 134 | 135 | // emit event 136 | await _events.RaiseAsync(new ConsentGrantedEvent(User.GetSubjectId(), request.ClientId, request.ScopesRequested, grantedConsent.ScopesConsented, grantedConsent.RememberConsent)); 137 | } 138 | else 139 | { 140 | result.ValidationError = ConsentOptions.MustChooseOneErrorMessage; 141 | } 142 | } 143 | else 144 | { 145 | result.ValidationError = ConsentOptions.InvalidSelectionErrorMessage; 146 | } 147 | 148 | if (grantedConsent != null) 149 | { 150 | // communicate outcome of consent back to identityserver 151 | await _interaction.GrantConsentAsync(request, grantedConsent); 152 | 153 | // indicate that's it ok to redirect back to authorization endpoint 154 | result.RedirectUri = model.ReturnUrl; 155 | result.ClientId = request.ClientId; 156 | } 157 | else 158 | { 159 | // we need to redisplay the consent UI 160 | result.ViewModel = await BuildViewModelAsync(model.ReturnUrl, model); 161 | } 162 | 163 | return result; 164 | } 165 | 166 | private async Task BuildViewModelAsync(string returnUrl, ConsentInputModel model = null) 167 | { 168 | var request = await _interaction.GetAuthorizationContextAsync(returnUrl); 169 | if (request != null) 170 | { 171 | var client = await _clientStore.FindEnabledClientByIdAsync(request.ClientId); 172 | if (client != null) 173 | { 174 | var resources = await _resourceStore.FindEnabledResourcesByScopeAsync(request.ScopesRequested); 175 | if (resources != null && (resources.IdentityResources.Any() || resources.ApiResources.Any())) 176 | { 177 | return CreateConsentViewModel(model, returnUrl, request, client, resources); 178 | } 179 | else 180 | { 181 | _logger.LogError("No scopes matching: {0}", request.ScopesRequested.Aggregate((x, y) => x + ", " + y)); 182 | } 183 | } 184 | else 185 | { 186 | _logger.LogError("Invalid client id: {0}", request.ClientId); 187 | } 188 | } 189 | else 190 | { 191 | _logger.LogError("No consent request matching request: {0}", returnUrl); 192 | } 193 | 194 | return null; 195 | } 196 | 197 | private ConsentViewModel CreateConsentViewModel( 198 | ConsentInputModel model, string returnUrl, 199 | AuthorizationRequest request, 200 | Client client, Resources resources) 201 | { 202 | var vm = new ConsentViewModel 203 | { 204 | RememberConsent = model?.RememberConsent ?? true, 205 | ScopesConsented = model?.ScopesConsented ?? Enumerable.Empty(), 206 | 207 | ReturnUrl = returnUrl, 208 | 209 | ClientName = client.ClientName ?? client.ClientId, 210 | ClientUrl = client.ClientUri, 211 | ClientLogoUrl = client.LogoUri, 212 | AllowRememberConsent = client.AllowRememberConsent 213 | }; 214 | 215 | vm.IdentityScopes = resources.IdentityResources.Select(x => CreateScopeViewModel(x, vm.ScopesConsented.Contains(x.Name) || model == null)).ToArray(); 216 | vm.ResourceScopes = resources.ApiResources.SelectMany(x => x.Scopes).Select(x => CreateScopeViewModel(x, vm.ScopesConsented.Contains(x.Name) || model == null)).ToArray(); 217 | if (ConsentOptions.EnableOfflineAccess && resources.OfflineAccess) 218 | { 219 | vm.ResourceScopes = vm.ResourceScopes.Union(new ScopeViewModel[] { 220 | GetOfflineAccessScope(vm.ScopesConsented.Contains(IdentityServer4.IdentityServerConstants.StandardScopes.OfflineAccess) || model == null) 221 | }); 222 | } 223 | 224 | return vm; 225 | } 226 | 227 | private ScopeViewModel CreateScopeViewModel(IdentityResource identity, bool check) 228 | { 229 | return new ScopeViewModel 230 | { 231 | Name = identity.Name, 232 | DisplayName = identity.DisplayName, 233 | Description = identity.Description, 234 | Emphasize = identity.Emphasize, 235 | Required = identity.Required, 236 | Checked = check || identity.Required 237 | }; 238 | } 239 | 240 | public ScopeViewModel CreateScopeViewModel(Scope scope, bool check) 241 | { 242 | return new ScopeViewModel 243 | { 244 | Name = scope.Name, 245 | DisplayName = scope.DisplayName, 246 | Description = scope.Description, 247 | Emphasize = scope.Emphasize, 248 | Required = scope.Required, 249 | Checked = check || scope.Required 250 | }; 251 | } 252 | 253 | private ScopeViewModel GetOfflineAccessScope(bool check) 254 | { 255 | return new ScopeViewModel 256 | { 257 | Name = IdentityServer4.IdentityServerConstants.StandardScopes.OfflineAccess, 258 | DisplayName = ConsentOptions.OfflineAccessDisplayName, 259 | Description = ConsentOptions.OfflineAccessDescription, 260 | Emphasize = true, 261 | Checked = check 262 | }; 263 | } 264 | } 265 | } -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/updateUI.ps1: -------------------------------------------------------------------------------- 1 | iex ((New-Object System.Net.WebClient).DownloadString('https://raw.githubusercontent.com/IdentityServer/IdentityServer4.Quickstart.UI.AspNetIdentity/master/getmaster.ps1')) 2 | # SIG # Begin signature block 3 | # MIIfnQYJKoZIhvcNAQcCoIIfjjCCH4oCAQExDzANBglghkgBZQMEAgEFADB5Bgor 4 | # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG 5 | # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAHBqDvspBu494k 6 | # uYMNuafp3nwe8Zo7LVhBGOgneS4sdqCCDgcwggPFMIICraADAgECAhACrFwmagtA 7 | # m48LefKuRiV3MA0GCSqGSIb3DQEBBQUAMGwxCzAJBgNVBAYTAlVTMRUwEwYDVQQK 8 | # EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xKzApBgNV 9 | # BAMTIkRpZ2lDZXJ0IEhpZ2ggQXNzdXJhbmNlIEVWIFJvb3QgQ0EwHhcNMDYxMTEw 10 | # MDAwMDAwWhcNMzExMTEwMDAwMDAwWjBsMQswCQYDVQQGEwJVUzEVMBMGA1UEChMM 11 | # RGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSswKQYDVQQD 12 | # EyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5jZSBFViBSb290IENBMIIBIjANBgkqhkiG 13 | # 9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxszlc+b71LvlLS0ypt/lgT/JzSVJtnEqw9WU 14 | # NGeiChywX2mmQLHEt7KP0JikqUFZOtPclNY823Q4pErMTSWC90qlUxI47vNJbXGR 15 | # fmO2q6Zfw6SE+E9iUb74xezbOJLjBuUIkQzEKEFV+8taiRV+ceg1v01yCT2+OjhQ 16 | # W3cxG42zxyRFmqesbQAUWgS3uhPrUQqYQUEiTmVhh4FBUKZ5XIneGUpX1S7mXRxT 17 | # LH6YzRoGFqRoc9A0BBNcoXHTWnxV215k4TeHMFYE5RG0KYAS8Xk5iKICEXwnZreI 18 | # t3jyygqoOKsKZMK/Zl2VhMGhJR6HXRpQCyASzEG7bgtROLhLywIDAQABo2MwYTAO 19 | # BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUsT7DaQP4 20 | # v0cB1JgmGggC72NkK8MwHwYDVR0jBBgwFoAUsT7DaQP4v0cB1JgmGggC72NkK8Mw 21 | # DQYJKoZIhvcNAQEFBQADggEBABwaBpfc15yfPIhmBghXIdshR/gqZ6q/GDJ2QBBX 22 | # wYrzetkRZY41+p78RbWe2UwxS7iR6EMsjrN4ztvjU3lx1uUhlAHaVYeaJGT2imbM 23 | # 3pw3zag0sWmbI8ieeCIrcEPjVUcxYRnvWMWFL04w9qAxFiPI5+JlFjPLvxoboD34 24 | # yl6LMYtgCIktDAZcUrfE+QqY0RVfnxK+fDZjOL1EpH/kJisKxJdpDemM4sAQV7jI 25 | # dhKRVfJIadi8KgJbD0TUIDHb9LpwJl2QYJ68SxcJL7TLHkNoyQcnwdJc9+ohuWgS 26 | # nDycv578gFybY83sR6olJ2egN/MAgn1U16n46S4To3foH0owggSRMIIDeaADAgEC 27 | # AhAHsEGNpR4UjDMbvN63E4MjMA0GCSqGSIb3DQEBCwUAMGwxCzAJBgNVBAYTAlVT 28 | # MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j 29 | # b20xKzApBgNVBAMTIkRpZ2lDZXJ0IEhpZ2ggQXNzdXJhbmNlIEVWIFJvb3QgQ0Ew 30 | # HhcNMTgwNDI3MTI0MTU5WhcNMjgwNDI3MTI0MTU5WjBaMQswCQYDVQQGEwJVUzEY 31 | # MBYGA1UEChMPLk5FVCBGb3VuZGF0aW9uMTEwLwYDVQQDEyguTkVUIEZvdW5kYXRp 32 | # b24gUHJvamVjdHMgQ29kZSBTaWduaW5nIENBMIIBIjANBgkqhkiG9w0BAQEFAAOC 33 | # AQ8AMIIBCgKCAQEAwQqv4aI0CI20XeYqTTZmyoxsSQgcCBGQnXnufbuDLhAB6GoT 34 | # NB7HuEhNSS8ftV+6yq8GztBzYAJ0lALdBjWypMfL451/84AO5ZiZB3V7MB2uxgWo 35 | # cV1ekDduU9bm1Q48jmR4SVkLItC+oQO/FIA2SBudVZUvYKeCJS5Ri9ibV7La4oo7 36 | # BJChFiP8uR+v3OU33dgm5BBhWmth4oTyq22zCfP3NO6gBWEIPFR5S+KcefUTYmn2 37 | # o7IvhvxzJsMCrNH1bxhwOyMl+DQcdWiVPuJBKDOO/hAKIxBG4i6ryQYBaKdhDgaA 38 | # NSCik0UgZasz8Qgl8n0A73+dISPumD8L/4mdywIDAQABo4IBPzCCATswHQYDVR0O 39 | # BBYEFMtck66Im/5Db1ZQUgJtePys4bFaMB8GA1UdIwQYMBaAFLE+w2kD+L9HAdSY 40 | # JhoIAu9jZCvDMA4GA1UdDwEB/wQEAwIBhjATBgNVHSUEDDAKBggrBgEFBQcDAzAS 41 | # BgNVHRMBAf8ECDAGAQH/AgEAMDQGCCsGAQUFBwEBBCgwJjAkBggrBgEFBQcwAYYY 42 | # aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEsGA1UdHwREMEIwQKA+oDyGOmh0dHA6 43 | # Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEhpZ2hBc3N1cmFuY2VFVlJvb3RD 44 | # QS5jcmwwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8v 45 | # d3d3LmRpZ2ljZXJ0LmNvbS9DUFMwDQYJKoZIhvcNAQELBQADggEBALNGxKTz6gq6 46 | # clMF01GjC3RmJ/ZAoK1V7rwkqOkY3JDl++v1F4KrFWEzS8MbZsI/p4W31Eketazo 47 | # Nxy23RT0zDsvJrwEC3R+/MRdkB7aTecsYmMeMHgtUrl3xEO3FubnQ0kKEU/HBCTd 48 | # hR14GsQEccQQE6grFVlglrew+FzehWUu3SUQEp9t+iWpX/KfviDWx0H1azilMX15 49 | # lzJUxK7kCzmflrk5jCOCjKqhOdGJoQqstmwP+07qXO18bcCzEC908P+TYkh0z9gV 50 | # rlj7tyW9K9zPVPJZsLRaBp/QjMcH65o9Y1hD1uWtFQYmbEYkT1K9tuXHtQYx1Rpf 51 | # /dC8Nbl4iukwggWlMIIEjaADAgECAhAL5Ofkz0TFYBmonpg7pehYMA0GCSqGSIb3 52 | # DQEBCwUAMFoxCzAJBgNVBAYTAlVTMRgwFgYDVQQKEw8uTkVUIEZvdW5kYXRpb24x 53 | # MTAvBgNVBAMTKC5ORVQgRm91bmRhdGlvbiBQcm9qZWN0cyBDb2RlIFNpZ25pbmcg 54 | # Q0EwHhcNMTgwNjExMDAwMDAwWhcNMjEwNjE1MTIwMDAwWjCBoDEUMBIGA1UEBRML 55 | # NjAzIDM4OSAwNjgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAw 56 | # DgYDVQQHEwdSZWRtb25kMSkwJwYDVQQKEyBJZGVudGl0eVNlcnZlciAoLk5FVCBG 57 | # b3VuZGF0aW9uKTEpMCcGA1UEAxMgSWRlbnRpdHlTZXJ2ZXIgKC5ORVQgRm91bmRh 58 | # dGlvbikwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCwEhFpSIYi3hrr 59 | # v/X9BtZkzufk7puhmTCVmQAPNm2R1eZZyMPhfxm5Sh/w/42CzlCG9LFgooha69z3 60 | # uoMMOKJEEQKZ6ByIV+r81o4lrHtSFbe4VlXavjQVFaVVjPSG6vWGykfHVCAeVpjx 61 | # fVk/HH6tEX506lBiHgOrQGogoQrwdVnObc3c6RiVSIuvFeCoHvk2GgiqyzFER7iO 62 | # R1055npVSAAAdxBvPA6KREcLb/qHukYCJZX4mY/SajBXwxupSnhRDbYhb+qHpFIL 63 | # x7s/azxg7tVRpVh49oJimHA2uZ/jzh/KgUsUe9MFzT7KPduBK/pfX/fXED9Pt1NN 64 | # 48VfPSuzAgMBAAGjggIeMIICGjAfBgNVHSMEGDAWgBTLXJOuiJv+Q29WUFICbXj8 65 | # rOGxWjAdBgNVHQ4EFgQU3CQnBPLvFkovKU/is0/LgQF/49swNAYDVR0RBC0wK6Ap 66 | # BggrBgEFBQcIA6AdMBsMGVVTLVdBU0hJTkdUT04tNjAzIDM4OSAwNjgwDgYDVR0P 67 | # AQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMIGZBgNVHR8EgZEwgY4wRaBD 68 | # oEGGP2h0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9ORVRGb3VuZGF0aW9uUHJvamVj 69 | # dHNDb2RlU2lnbmluZ0NBLmNybDBFoEOgQYY/aHR0cDovL2NybDQuZGlnaWNlcnQu 70 | # Y29tL05FVEZvdW5kYXRpb25Qcm9qZWN0c0NvZGVTaWduaW5nQ0EuY3JsMEwGA1Ud 71 | # IARFMEMwNwYJYIZIAYb9bAMBMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3LmRp 72 | # Z2ljZXJ0LmNvbS9DUFMwCAYGZ4EMAQQBMIGEBggrBgEFBQcBAQR4MHYwJAYIKwYB 73 | # BQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBOBggrBgEFBQcwAoZCaHR0 74 | # cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL05FVEZvdW5kYXRpb25Qcm9qZWN0c0Nv 75 | # ZGVTaWduaW5nQ0EuY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggEB 76 | # AH0FqXX4p5RlC27jX6tcTEf2HT4mAiqosnYU/napWgQE2U9m73IZO+2MuiQWkUQi 77 | # 2PLjbQQcOwfMwkt0SDaSAlfC1zhjZkZb2NcpJRg0cUAjcDzqh6hTzXRVJPD/UrW2 78 | # a5qBhYnSQDSWbYnVwfAQFFvnQcR5i/xnoOxq7+3LIvHoJafpsxcAFS57Vdsuw91u 79 | # keB6uasOfvdd06Mpl9BLWZHpyEdnPIKMv6ALibTdw9lNzCQ+EmdT5Fwky8wHE8BH 80 | # hhAdjSuGiyd+AzR3IuL96Q41h34c7pL827atOHwkkjUx+QTVkXbYoal6wwBKhi6I 81 | # QJEhT0s/yyFFM7BrLhQpRSsxghDsMIIQ6AIBATBuMFoxCzAJBgNVBAYTAlVTMRgw 82 | # FgYDVQQKEw8uTkVUIEZvdW5kYXRpb24xMTAvBgNVBAMTKC5ORVQgRm91bmRhdGlv 83 | # biBQcm9qZWN0cyBDb2RlIFNpZ25pbmcgQ0ECEAvk5+TPRMVgGaiemDul6FgwDQYJ 84 | # YIZIAWUDBAIBBQCggYQwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG 85 | # 9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIB 86 | # FTAvBgkqhkiG9w0BCQQxIgQgXugViXfpDsSmxuXmOZmlxS8caPztV818gfdU0O48 87 | # oNAwDQYJKoZIhvcNAQEBBQAEggEAD/9ysHEvC7c0xnPrziJS5wXLbO5jRzqNp6wy 88 | # 5zFtVonQGdz8Z0XUeENXVRNb/oYxjWbQwZxvuOEXO93P/oNoWB2HMAGekirvS3q0 89 | # yEmBmsPLM4LFgsIOrUYQqNR0HynWdy+g4FzHNxHOKK7rO0utnzxvNX3mADtHzJl+ 90 | # 8ajOC46BlXoubbRjahEYfXVU1PyqFzL8/ztwyUT2EHjchshtHPdJ+RaHuRFTtHmV 91 | # rLzFQvLNRaddSTxxAq0ykO81/e18O1qJnPLxdbPp5bwCxmz/DWbQui72YKuvfhtQ 92 | # gkXQiiAfVdZSnNfybvD6048glMZDWpsXmF7O6mIvCJzZCuctIqGCDsgwgg7EBgor 93 | # BgEEAYI3AwMBMYIOtDCCDrAGCSqGSIb3DQEHAqCCDqEwgg6dAgEDMQ8wDQYJYIZI 94 | # AWUDBAIBBQAwdwYLKoZIhvcNAQkQAQSgaARmMGQCAQEGCWCGSAGG/WwHATAxMA0G 95 | # CWCGSAFlAwQCAQUABCDxhlzu7w+OzWAwxQVBt+Syjgs8UHM7XlIuKgqyCBtWQgIQ 96 | # UVGJZi8uuY8M++HXK4Qd5BgPMjAxOTEwMTUxMDI1MThaoIILuzCCBoIwggVqoAMC 97 | # AQICEAnA/EbIBEITtVmLryhPTkEwDQYJKoZIhvcNAQELBQAwcjELMAkGA1UEBhMC 98 | # VVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0 99 | # LmNvbTExMC8GA1UEAxMoRGlnaUNlcnQgU0hBMiBBc3N1cmVkIElEIFRpbWVzdGFt 100 | # cGluZyBDQTAeFw0xNzAxMDQwMDAwMDBaFw0yODAxMTgwMDAwMDBaMEwxCzAJBgNV 101 | # BAYTAlVTMREwDwYDVQQKEwhEaWdpQ2VydDEqMCgGA1UEAxMhRGlnaUNlcnQgU0hB 102 | # MiBUaW1lc3RhbXAgUmVzcG9uZGVyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB 103 | # CgKCAQEAnpWYajQ7cxuofvzHvilpicdoJkZfPY1ic4eBo6Gc8LdbJDdaktT0Wdd2 104 | # ieTc1Sfw1Wa8Cu60KzFnrFjFSpFZK0UeCQHWZLNZ7o1mTfsjXswQDQuKZ+9SrqAI 105 | # kMJS9/WotW6bLHud57U++3jNMlAYv0C1TIy7V/SgTxFFbEJCueWv1t/0p3wKaJYP 106 | # 0l8pV877HTL/9BGhEyL7Esvv11PS65fLoqwbHZ1YIVGCwsLe6is/LCKE0EPsOzs/ 107 | # R8T2VtxFN5i0a3S1Wa94V2nIDwkCeN3YU8GZ22DEnequr+B+hkpcqVhhqF50igEo 108 | # aHJOp4adtQJSh3BmSNOO74EkzNzYZQIDAQABo4IDODCCAzQwDgYDVR0PAQH/BAQD 109 | # AgeAMAwGA1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgwggG/BgNV 110 | # HSAEggG2MIIBsjCCAaEGCWCGSAGG/WwHATCCAZIwKAYIKwYBBQUHAgEWHGh0dHBz 111 | # Oi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwggFkBggrBgEFBQcCAjCCAVYeggFSAEEA 112 | # bgB5ACAAdQBzAGUAIABvAGYAIAB0AGgAaQBzACAAQwBlAHIAdABpAGYAaQBjAGEA 113 | # dABlACAAYwBvAG4AcwB0AGkAdAB1AHQAZQBzACAAYQBjAGMAZQBwAHQAYQBuAGMA 114 | # ZQAgAG8AZgAgAHQAaABlACAARABpAGcAaQBDAGUAcgB0ACAAQwBQAC8AQwBQAFMA 115 | # IABhAG4AZAAgAHQAaABlACAAUgBlAGwAeQBpAG4AZwAgAFAAYQByAHQAeQAgAEEA 116 | # ZwByAGUAZQBtAGUAbgB0ACAAdwBoAGkAYwBoACAAbABpAG0AaQB0ACAAbABpAGEA 117 | # YgBpAGwAaQB0AHkAIABhAG4AZAAgAGEAcgBlACAAaQBuAGMAbwByAHAAbwByAGEA 118 | # dABlAGQAIABoAGUAcgBlAGkAbgAgAGIAeQAgAHIAZQBmAGUAcgBlAG4AYwBlAC4w 119 | # CwYJYIZIAYb9bAMVMB8GA1UdIwQYMBaAFPS24SAd/imu0uRhpbKiJbLIFzVuMB0G 120 | # A1UdDgQWBBThpzJK7gEhKH1U1fIHkm60Bw89hzBxBgNVHR8EajBoMDKgMKAuhixo 121 | # dHRwOi8vY3JsMy5kaWdpY2VydC5jb20vc2hhMi1hc3N1cmVkLXRzLmNybDAyoDCg 122 | # LoYsaHR0cDovL2NybDQuZGlnaWNlcnQuY29tL3NoYTItYXNzdXJlZC10cy5jcmww 123 | # gYUGCCsGAQUFBwEBBHkwdzAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNl 124 | # cnQuY29tME8GCCsGAQUFBzAChkNodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20v 125 | # RGlnaUNlcnRTSEEyQXNzdXJlZElEVGltZXN0YW1waW5nQ0EuY3J0MA0GCSqGSIb3 126 | # DQEBCwUAA4IBAQAe8EGCMq7t8bQ1E9xQwtWXriIinQ4OrzPTTP18v28BEaeUZSJc 127 | # xiKhyIlSa5qMc1zZXj8y3hZgTIs2/TGZCr3BhLeNHe+JJhMFVvNHzUdbrYSyOK9q 128 | # I7VF4x6IMkaA0remmSL9wXjP9YvYDIwFCe5E5oDVbXDMn1MeJ90qSN7ak2WtbmWj 129 | # mafCQA5zzFhPj0Uo5byciOYozmBdLSVdi3MupQ1bUdqaTv9QBYko2vJ4u9JYeI1E 130 | # p6w6AJF4aYlkBNNdlt8qv/mlTCyT/+aK3YKs8dKzooaawVWJVmpHP/rWM5VDNYkF 131 | # eFo6adoiuARD029oNTZ6FD5F6Zhkhg8TDCZKMIIFMTCCBBmgAwIBAgIQCqEl1tYy 132 | # G35B5AXaNpfCFTANBgkqhkiG9w0BAQsFADBlMQswCQYDVQQGEwJVUzEVMBMGA1UE 133 | # ChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYD 134 | # VQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwHhcNMTYwMTA3MTIwMDAw 135 | # WhcNMzEwMTA3MTIwMDAwWjByMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNl 136 | # cnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhEaWdp 137 | # Q2VydCBTSEEyIEFzc3VyZWQgSUQgVGltZXN0YW1waW5nIENBMIIBIjANBgkqhkiG 138 | # 9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvdAy7kvNj3/dqbqCmcU5VChXtiNKxA4HRTNR 139 | # EH3Q+X1NaH7ntqD0jbOI5Je/YyGQmL8TvFfTw+F+CNZqFAA49y4eO+7MpvYyWf5f 140 | # ZT/gm+vjRkcGGlV+Cyd+wKL1oODeIj8O/36V+/OjuiI+GKwR5PCZA207hXwJ0+5d 141 | # yJoLVOOoCXFr4M8iEA91z3FyTgqt30A6XLdR4aF5FMZNJCMwXbzsPGBqrC8HzP3w 142 | # 6kfZiFBe/WZuVmEnKYmEUeaC50ZQ/ZQqLKfkdT66mA+Ef58xFNat1fJky3seBdCE 143 | # GXIX8RcG7z3N1k3vBkL9olMqT4UdxB08r8/arBD13ays6Vb/kwIDAQABo4IBzjCC 144 | # AcowHQYDVR0OBBYEFPS24SAd/imu0uRhpbKiJbLIFzVuMB8GA1UdIwQYMBaAFEXr 145 | # oq/0ksuCMS1Ri6enIZ3zbcgPMBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/ 146 | # BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUFBwMIMHkGCCsGAQUFBwEBBG0wazAkBggr 147 | # BgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEMGCCsGAQUFBzAChjdo 148 | # dHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURSb290 149 | # Q0EuY3J0MIGBBgNVHR8EejB4MDqgOKA2hjRodHRwOi8vY3JsNC5kaWdpY2VydC5j 150 | # b20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3JsMDqgOKA2hjRodHRwOi8vY3Js 151 | # My5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3JsMFAGA1Ud 152 | # IARJMEcwOAYKYIZIAYb9bAACBDAqMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5k 153 | # aWdpY2VydC5jb20vQ1BTMAsGCWCGSAGG/WwHATANBgkqhkiG9w0BAQsFAAOCAQEA 154 | # cZUS6VGHVmnN793afKpjerN4zwY3QITvS4S/ys8DAv3Fp8MOIEIsr3fzKx8MIVoq 155 | # twU0HWqumfgnoma/Capg33akOpMP+LLR2HwZYuhegiUexLoceywh4tZbLBQ1QwRo 156 | # stt1AuByx5jWPGTlH0gQGF+JOGFNYkYkh2OMkVIsrymJ5Xgf1gsUpYDXEkdws3XV 157 | # k4WTfraSZ/tTYYmo9WuWwPRYaQ18yAGxuSh1t5ljhSKMYcp5lH5Z/IwP42+1ASa2 158 | # bKXuh1Eh5Fhgm7oMLSttosR+u8QlK0cCCHxJrhO24XxCQijGGFbPQTS2Zl22dHv1 159 | # VjMiLyI2skuiSpXY9aaOUjGCAk0wggJJAgEBMIGGMHIxCzAJBgNVBAYTAlVTMRUw 160 | # EwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20x 161 | # MTAvBgNVBAMTKERpZ2lDZXJ0IFNIQTIgQXNzdXJlZCBJRCBUaW1lc3RhbXBpbmcg 162 | # Q0ECEAnA/EbIBEITtVmLryhPTkEwDQYJYIZIAWUDBAIBBQCggZgwGgYJKoZIhvcN 163 | # AQkDMQ0GCyqGSIb3DQEJEAEEMBwGCSqGSIb3DQEJBTEPFw0xOTEwMTUxMDI1MTha 164 | # MCsGCyqGSIb3DQEJEAIMMRwwGjAYMBYEFEABkUdcmIkd66EEr0cJG1621MvLMC8G 165 | # CSqGSIb3DQEJBDEiBCAXLzdSc7XnoYoCnyVlEwU03DUS/ziexIC+sQaiBP4xfjAN 166 | # BgkqhkiG9w0BAQEFAASCAQBIQ9VL9ktSlmgeZgyg7/1pLUyOifml+xLTlPuI1HYu 167 | # TFH0SHSv+X3JavTfMJPHEuDG6oJL/pQ1QoZjeFYj6hV+CZY2rftBC+0FdIs9hDNq 168 | # rKXEx1CcUorLtUKp8nj68ZDavxHQXbAMp4bGH9aBEHUkJ5D+5/yyemiKJfD3b6C2 169 | # /Sjpaqh7fsWknGf6MSUt8CJoV1iyEr1l8ncKa6pCUOPmE/wwxEZ3jK48Gusdq5dP 170 | # tL9vmz4J6/jMA5JZUKu3tzWDmf2FWl1Wijs4O0HyeUlZ9VoCgYLYPR+jZpULFNGO 171 | # bbsOkp2jDu26oTY7tNa2pWUE6O12qib7YRCtSj1nG0up 172 | # SIG # End signature block 173 | -------------------------------------------------------------------------------- /IdentityServerAspNetIdentity/Quickstart/Account/ExternalController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Security.Claims; 5 | using System.Security.Principal; 6 | using System.Threading.Tasks; 7 | using IdentityModel; 8 | using IdentityServer4.Events; 9 | using IdentityServer4.Services; 10 | using IdentityServer4.Stores; 11 | using IdentityServerAspNetIdentity.Models; 12 | using Microsoft.AspNetCore.Authentication; 13 | using Microsoft.AspNetCore.Authorization; 14 | using Microsoft.AspNetCore.Http; 15 | using Microsoft.AspNetCore.Identity; 16 | using Microsoft.AspNetCore.Mvc; 17 | using Microsoft.Extensions.Logging; 18 | 19 | namespace IdentityServer4.Quickstart.UI 20 | { 21 | [SecurityHeaders] 22 | [AllowAnonymous] 23 | public class ExternalController : Controller 24 | { 25 | private readonly UserManager _userManager; 26 | private readonly SignInManager _signInManager; 27 | private readonly IIdentityServerInteractionService _interaction; 28 | private readonly IClientStore _clientStore; 29 | private readonly IEventService _events; 30 | private readonly ILogger _logger; 31 | 32 | public ExternalController( 33 | UserManager userManager, 34 | SignInManager signInManager, 35 | IIdentityServerInteractionService interaction, 36 | IClientStore clientStore, 37 | IEventService events, 38 | ILogger logger) 39 | { 40 | _userManager = userManager; 41 | _signInManager = signInManager; 42 | _interaction = interaction; 43 | _clientStore = clientStore; 44 | _events = events; 45 | _logger = logger; 46 | } 47 | 48 | /// 49 | /// initiate roundtrip to external authentication provider 50 | /// 51 | [HttpGet] 52 | public async Task Challenge(string provider, string returnUrl) 53 | { 54 | if (string.IsNullOrEmpty(returnUrl)) returnUrl = "~/"; 55 | 56 | // validate returnUrl - either it is a valid OIDC URL or back to a local page 57 | if (Url.IsLocalUrl(returnUrl) == false && _interaction.IsValidReturnUrl(returnUrl) == false) 58 | { 59 | // user might have clicked on a malicious link - should be logged 60 | throw new Exception("invalid return URL"); 61 | } 62 | 63 | if (AccountOptions.WindowsAuthenticationSchemeName == provider) 64 | { 65 | // windows authentication needs special handling 66 | return await ProcessWindowsLoginAsync(returnUrl); 67 | } 68 | else 69 | { 70 | // start challenge and roundtrip the return URL and scheme 71 | var props = new AuthenticationProperties 72 | { 73 | RedirectUri = Url.Action(nameof(Callback)), 74 | Items = 75 | { 76 | { "returnUrl", returnUrl }, 77 | { "scheme", provider }, 78 | } 79 | }; 80 | 81 | return Challenge(props, provider); 82 | } 83 | } 84 | 85 | /// 86 | /// Post processing of external authentication 87 | /// 88 | [HttpGet] 89 | public async Task Callback() 90 | { 91 | // read external identity from the temporary cookie 92 | var result = await HttpContext.AuthenticateAsync(IdentityConstants.ExternalScheme); 93 | if (result?.Succeeded != true) 94 | { 95 | throw new Exception("External authentication error"); 96 | } 97 | 98 | if (_logger.IsEnabled(LogLevel.Debug)) 99 | { 100 | var externalClaims = result.Principal.Claims.Select(c => $"{c.Type}: {c.Value}"); 101 | _logger.LogDebug("External claims: {@claims}", externalClaims); 102 | } 103 | 104 | // lookup our user and external provider info 105 | var (user, provider, providerUserId, claims) = await FindUserFromExternalProviderAsync(result); 106 | if (user == null) 107 | { 108 | // this might be where you might initiate a custom workflow for user registration 109 | // in this sample we don't show how that would be done, as our sample implementation 110 | // simply auto-provisions new external user 111 | user = await AutoProvisionUserAsync(provider, providerUserId, claims); 112 | } 113 | 114 | // this allows us to collect any additonal claims or properties 115 | // for the specific prtotocols used and store them in the local auth cookie. 116 | // this is typically used to store data needed for signout from those protocols. 117 | var additionalLocalClaims = new List(); 118 | var localSignInProps = new AuthenticationProperties(); 119 | ProcessLoginCallbackForOidc(result, additionalLocalClaims, localSignInProps); 120 | ProcessLoginCallbackForWsFed(result, additionalLocalClaims, localSignInProps); 121 | ProcessLoginCallbackForSaml2p(result, additionalLocalClaims, localSignInProps); 122 | 123 | // issue authentication cookie for user 124 | // we must issue the cookie maually, and can't use the SignInManager because 125 | // it doesn't expose an API to issue additional claims from the login workflow 126 | var principal = await _signInManager.CreateUserPrincipalAsync(user); 127 | additionalLocalClaims.AddRange(principal.Claims); 128 | var name = principal.FindFirst(JwtClaimTypes.Name)?.Value ?? user.Id; 129 | await HttpContext.SignInAsync(user.Id, name, provider, localSignInProps, additionalLocalClaims.ToArray()); 130 | 131 | // delete temporary cookie used during external authentication 132 | await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme); 133 | 134 | // retrieve return URL 135 | var returnUrl = result.Properties.Items["returnUrl"] ?? "~/"; 136 | 137 | // check if external login is in the context of an OIDC request 138 | var context = await _interaction.GetAuthorizationContextAsync(returnUrl); 139 | await _events.RaiseAsync(new UserLoginSuccessEvent(provider, providerUserId, user.Id, name, true, context?.ClientId)); 140 | 141 | if (context != null) 142 | { 143 | if (await _clientStore.IsPkceClientAsync(context.ClientId)) 144 | { 145 | // if the client is PKCE then we assume it's native, so this change in how to 146 | // return the response is for better UX for the end user. 147 | return View("Redirect", new RedirectViewModel { RedirectUrl = returnUrl }); 148 | } 149 | } 150 | 151 | return Redirect(returnUrl); 152 | } 153 | 154 | private async Task ProcessWindowsLoginAsync(string returnUrl) 155 | { 156 | // see if windows auth has already been requested and succeeded 157 | var result = await HttpContext.AuthenticateAsync(AccountOptions.WindowsAuthenticationSchemeName); 158 | if (result?.Principal is WindowsPrincipal wp) 159 | { 160 | // we will issue the external cookie and then redirect the 161 | // user back to the external callback, in essence, treating windows 162 | // auth the same as any other external authentication mechanism 163 | var props = new AuthenticationProperties() 164 | { 165 | RedirectUri = Url.Action("Callback"), 166 | Items = 167 | { 168 | { "returnUrl", returnUrl }, 169 | { "scheme", AccountOptions.WindowsAuthenticationSchemeName }, 170 | } 171 | }; 172 | 173 | var id = new ClaimsIdentity(AccountOptions.WindowsAuthenticationSchemeName); 174 | id.AddClaim(new Claim(JwtClaimTypes.Subject, wp.FindFirst(ClaimTypes.PrimarySid).Value)); 175 | id.AddClaim(new Claim(JwtClaimTypes.Name, wp.Identity.Name)); 176 | 177 | // add the groups as claims -- be careful if the number of groups is too large 178 | if (AccountOptions.IncludeWindowsGroups) 179 | { 180 | var wi = wp.Identity as WindowsIdentity; 181 | var groups = wi.Groups.Translate(typeof(NTAccount)); 182 | var roles = groups.Select(x => new Claim(JwtClaimTypes.Role, x.Value)); 183 | id.AddClaims(roles); 184 | } 185 | 186 | await HttpContext.SignInAsync( 187 | IdentityServer4.IdentityServerConstants.ExternalCookieAuthenticationScheme, 188 | new ClaimsPrincipal(id), 189 | props); 190 | return Redirect(props.RedirectUri); 191 | } 192 | else 193 | { 194 | // trigger windows auth 195 | // since windows auth don't support the redirect uri, 196 | // this URL is re-triggered when we call challenge 197 | return Challenge(AccountOptions.WindowsAuthenticationSchemeName); 198 | } 199 | } 200 | 201 | private async Task<(ApplicationUser user, string provider, string providerUserId, IEnumerable claims)> 202 | FindUserFromExternalProviderAsync(AuthenticateResult result) 203 | { 204 | var externalUser = result.Principal; 205 | 206 | // try to determine the unique id of the external user (issued by the provider) 207 | // the most common claim type for that are the sub claim and the NameIdentifier 208 | // depending on the external provider, some other claim type might be used 209 | var userIdClaim = externalUser.FindFirst(JwtClaimTypes.Subject) ?? 210 | externalUser.FindFirst(ClaimTypes.NameIdentifier) ?? 211 | throw new Exception("Unknown userid"); 212 | 213 | // remove the user id claim so we don't include it as an extra claim if/when we provision the user 214 | var claims = externalUser.Claims.ToList(); 215 | claims.Remove(userIdClaim); 216 | 217 | var provider = result.Properties.Items["scheme"]; 218 | var providerUserId = userIdClaim.Value; 219 | 220 | // find external user 221 | var user = await _userManager.FindByLoginAsync(provider, providerUserId); 222 | 223 | return (user, provider, providerUserId, claims); 224 | } 225 | 226 | private async Task AutoProvisionUserAsync(string provider, string providerUserId, IEnumerable claims) 227 | { 228 | // create a list of claims that we want to transfer into our store 229 | var filtered = new List(); 230 | 231 | // user's display name 232 | var name = claims.FirstOrDefault(x => x.Type == JwtClaimTypes.Name)?.Value ?? 233 | claims.FirstOrDefault(x => x.Type == ClaimTypes.Name)?.Value; 234 | if (name != null) 235 | { 236 | filtered.Add(new Claim(JwtClaimTypes.Name, name)); 237 | } 238 | else 239 | { 240 | var first = claims.FirstOrDefault(x => x.Type == JwtClaimTypes.GivenName)?.Value ?? 241 | claims.FirstOrDefault(x => x.Type == ClaimTypes.GivenName)?.Value; 242 | var last = claims.FirstOrDefault(x => x.Type == JwtClaimTypes.FamilyName)?.Value ?? 243 | claims.FirstOrDefault(x => x.Type == ClaimTypes.Surname)?.Value; 244 | if (first != null && last != null) 245 | { 246 | filtered.Add(new Claim(JwtClaimTypes.Name, first + " " + last)); 247 | } 248 | else if (first != null) 249 | { 250 | filtered.Add(new Claim(JwtClaimTypes.Name, first)); 251 | } 252 | else if (last != null) 253 | { 254 | filtered.Add(new Claim(JwtClaimTypes.Name, last)); 255 | } 256 | } 257 | 258 | // email 259 | var email = claims.FirstOrDefault(x => x.Type == JwtClaimTypes.Email)?.Value ?? 260 | claims.FirstOrDefault(x => x.Type == ClaimTypes.Email)?.Value; 261 | if (email != null) 262 | { 263 | filtered.Add(new Claim(JwtClaimTypes.Email, email)); 264 | } 265 | 266 | var user = new ApplicationUser 267 | { 268 | UserName = Guid.NewGuid().ToString(), 269 | }; 270 | var identityResult = await _userManager.CreateAsync(user); 271 | if (!identityResult.Succeeded) throw new Exception(identityResult.Errors.First().Description); 272 | 273 | if (filtered.Any()) 274 | { 275 | identityResult = await _userManager.AddClaimsAsync(user, filtered); 276 | if (!identityResult.Succeeded) throw new Exception(identityResult.Errors.First().Description); 277 | } 278 | 279 | identityResult = await _userManager.AddLoginAsync(user, new UserLoginInfo(provider, providerUserId, provider)); 280 | if (!identityResult.Succeeded) throw new Exception(identityResult.Errors.First().Description); 281 | 282 | return user; 283 | } 284 | 285 | 286 | private void ProcessLoginCallbackForOidc(AuthenticateResult externalResult, List localClaims, AuthenticationProperties localSignInProps) 287 | { 288 | // if the external system sent a session id claim, copy it over 289 | // so we can use it for single sign-out 290 | var sid = externalResult.Principal.Claims.FirstOrDefault(x => x.Type == JwtClaimTypes.SessionId); 291 | if (sid != null) 292 | { 293 | localClaims.Add(new Claim(JwtClaimTypes.SessionId, sid.Value)); 294 | } 295 | 296 | // if the external provider issued an id_token, we'll keep it for signout 297 | var id_token = externalResult.Properties.GetTokenValue("id_token"); 298 | if (id_token != null) 299 | { 300 | localSignInProps.StoreTokens(new[] { new AuthenticationToken { Name = "id_token", Value = id_token } }); 301 | } 302 | } 303 | 304 | private void ProcessLoginCallbackForWsFed(AuthenticateResult externalResult, List localClaims, AuthenticationProperties localSignInProps) 305 | { 306 | } 307 | 308 | private void ProcessLoginCallbackForSaml2p(AuthenticateResult externalResult, List localClaims, AuthenticationProperties localSignInProps) 309 | { 310 | } 311 | } 312 | } --------------------------------------------------------------------------------