Browse Source

implement Duende.IdentityServer 6.2.0

pull/2012/head
Tarun Jain 2 years ago
parent
commit
b90d2d1cd4
104 changed files with 2451 additions and 5484 deletions
  1. +1
    -1
      src/ApiGateways/Mobile.Bff.Shopping/aggregator/Startup.cs
  2. +1
    -1
      src/ApiGateways/Web.Bff.Shopping/aggregator/Startup.cs
  3. +0
    -31
      src/Services/Identity/Identity.API/Certificate/Certificate.cs
  4. BIN
      src/Services/Identity/Identity.API/Certificate/idsrv3test.pfx
  5. +25
    -25
      src/Services/Identity/Identity.API/Configuration/Config.cs
  6. +0
    -299
      src/Services/Identity/Identity.API/Controllers/AccountController.cs
  7. +0
    -249
      src/Services/Identity/Identity.API/Controllers/ConsentController.cs
  8. +0
    -46
      src/Services/Identity/Identity.API/Controllers/HomeController.cs
  9. +12
    -13
      src/Services/Identity/Identity.API/Data/ApplicationDbContext.cs
  10. +0
    -218
      src/Services/Identity/Identity.API/Data/ApplicationDbContextSeed.cs
  11. +0
    -83
      src/Services/Identity/Identity.API/Data/ConfigurationDbContextSeed.cs
  12. +10
    -10
      src/Services/Identity/Identity.API/Data/Migrations/20210914100206_InitialMigration.Designer.cs
  13. +1
    -1
      src/Services/Identity/Identity.API/Data/Migrations/20210914100206_InitialMigration.cs
  14. +9
    -9
      src/Services/Identity/Identity.API/Data/Migrations/ApplicationDbContextModelSnapshot.cs
  15. +0
    -23
      src/Services/Identity/Identity.API/Extensions/Extension.cs
  16. +0
    -46
      src/Services/Identity/Identity.API/Extensions/LinqSelectExtensions.cs
  17. +0
    -20
      src/Services/Identity/Identity.API/Factories/ApplicationDbContextFactory.cs
  18. +0
    -21
      src/Services/Identity/Identity.API/Factories/ConfigurationDbContextFactory.cs
  19. +0
    -21
      src/Services/Identity/Identity.API/Factories/PersistedGrantDbContextFactory.cs
  20. +10
    -10
      src/Services/Identity/Identity.API/GlobalUsings.cs
  21. +21
    -17
      src/Services/Identity/Identity.API/Identity.API.csproj
  22. +0
    -1096
      src/Services/Identity/Identity.API/Migrations/ConfigurationDb/20220324152912_InitialConfigurationDbMigration.Designer.cs
  23. +0
    -712
      src/Services/Identity/Identity.API/Migrations/ConfigurationDb/20220324152912_InitialConfigurationDbMigration.cs
  24. +0
    -1094
      src/Services/Identity/Identity.API/Migrations/ConfigurationDb/ConfigurationDbContextModelSnapshot.cs
  25. +0
    -240
      src/Services/Identity/Identity.API/Migrations/PersistedGrantDb/20220324152905_InitialPersistedGrantDbMigration.Designer.cs
  26. +0
    -177
      src/Services/Identity/Identity.API/Migrations/PersistedGrantDb/20220324152905_InitialPersistedGrantDbMigration.cs
  27. +0
    -238
      src/Services/Identity/Identity.API/Migrations/PersistedGrantDb/PersistedGrantDbContextModelSnapshot.cs
  28. +58
    -54
      src/Services/Identity/Identity.API/Program.cs
  29. +117
    -0
      src/Services/Identity/Identity.API/ProgramExtensions.cs
  30. +341
    -0
      src/Services/Identity/Identity.API/Quickstart/Account/AccountController.cs
  31. +16
    -0
      src/Services/Identity/Identity.API/Quickstart/Account/AccountOptions.cs
  32. +238
    -0
      src/Services/Identity/Identity.API/Quickstart/Account/ExternalController.cs
  33. +10
    -0
      src/Services/Identity/Identity.API/Quickstart/Account/ExternalProvider.cs
  34. +18
    -0
      src/Services/Identity/Identity.API/Quickstart/Account/LoggedOutViewModel.cs
  35. +15
    -0
      src/Services/Identity/Identity.API/Quickstart/Account/LoginInputModel.cs
  36. +17
    -0
      src/Services/Identity/Identity.API/Quickstart/Account/LoginViewModel.cs
  37. +10
    -0
      src/Services/Identity/Identity.API/Quickstart/Account/LogoutInputModel.cs
  38. +10
    -0
      src/Services/Identity/Identity.API/Quickstart/Account/LogoutViewModel.cs
  39. +11
    -0
      src/Services/Identity/Identity.API/Quickstart/Account/RedirectViewModel.cs
  40. +247
    -0
      src/Services/Identity/Identity.API/Quickstart/Consent/ConsentController.cs
  41. +14
    -0
      src/Services/Identity/Identity.API/Quickstart/Consent/ConsentInputModel.cs
  42. +15
    -0
      src/Services/Identity/Identity.API/Quickstart/Consent/ConsentOptions.cs
  43. +16
    -0
      src/Services/Identity/Identity.API/Quickstart/Consent/ConsentViewModel.cs
  44. +17
    -0
      src/Services/Identity/Identity.API/Quickstart/Consent/ProcessConsentResult.cs
  45. +15
    -0
      src/Services/Identity/Identity.API/Quickstart/Consent/ScopeViewModel.cs
  46. +10
    -0
      src/Services/Identity/Identity.API/Quickstart/Device/DeviceAuthorizationInputModel.cs
  47. +11
    -0
      src/Services/Identity/Identity.API/Quickstart/Device/DeviceAuthorizationViewModel.cs
  48. +214
    -0
      src/Services/Identity/Identity.API/Quickstart/Device/DeviceController.cs
  49. +22
    -0
      src/Services/Identity/Identity.API/Quickstart/Diagnostics/DiagnosticsController.cs
  50. +28
    -0
      src/Services/Identity/Identity.API/Quickstart/Diagnostics/DiagnosticsViewModel.cs
  51. +22
    -0
      src/Services/Identity/Identity.API/Quickstart/Extensions.cs
  52. +85
    -0
      src/Services/Identity/Identity.API/Quickstart/Grants/GrantsController.cs
  53. +23
    -0
      src/Services/Identity/Identity.API/Quickstart/Grants/GrantsViewModel.cs
  54. +18
    -0
      src/Services/Identity/Identity.API/Quickstart/Home/ErrorViewModel.cs
  55. +63
    -0
      src/Services/Identity/Identity.API/Quickstart/Home/HomeController.cs
  56. +52
    -0
      src/Services/Identity/Identity.API/Quickstart/SecurityHeadersAttribute.cs
  57. +124
    -0
      src/Services/Identity/Identity.API/SeedData.cs
  58. +0
    -2
      src/Services/Identity/Identity.API/Setup/Users.csv
  59. BIN
      src/Services/Identity/Identity.API/Setup/images.zip
  60. +0
    -167
      src/Services/Identity/Identity.API/Startup.cs
  61. +7
    -0
      src/Services/Identity/Identity.API/Views/Account/AccessDenied.cshtml
  62. +17
    -4
      src/Services/Identity/Identity.API/Views/Account/LoggedOut.cshtml
  63. +58
    -28
      src/Services/Identity/Identity.API/Views/Account/Login.cshtml
  64. +10
    -16
      src/Services/Identity/Identity.API/Views/Account/Logout.cshtml
  65. +0
    -3
      src/Services/Identity/Identity.API/Views/Account/Redirecting.cshtml
  66. +0
    -28
      src/Services/Identity/Identity.API/Views/Account/Register.cshtml
  67. +0
    -54
      src/Services/Identity/Identity.API/Views/Account/_LoginPartial-MVC.cshtml
  68. +0
    -47
      src/Services/Identity/Identity.API/Views/Account/_LoginPartial-SPA.cshtml
  69. +0
    -100
      src/Services/Identity/Identity.API/Views/Account/_RegisterPartial-MVC.cshtml
  70. +0
    -100
      src/Services/Identity/Identity.API/Views/Account/_RegisterPartial-SPA.cshtml
  71. +2
    -2
      src/Services/Identity/Identity.API/Views/Consent/Index.cshtml
  72. +7
    -0
      src/Services/Identity/Identity.API/Views/Device/Success.cshtml
  73. +23
    -0
      src/Services/Identity/Identity.API/Views/Device/UserCodeCapture.cshtml
  74. +108
    -0
      src/Services/Identity/Identity.API/Views/Device/UserCodeConfirmation.cshtml
  75. +64
    -0
      src/Services/Identity/Identity.API/Views/Diagnostics/Index.cshtml
  76. +87
    -0
      src/Services/Identity/Identity.API/Views/Grants/Index.cshtml
  77. +30
    -28
      src/Services/Identity/Identity.API/Views/Home/Index.cshtml
  78. +8
    -2
      src/Services/Identity/Identity.API/Views/Shared/Error.cshtml
  79. +11
    -0
      src/Services/Identity/Identity.API/Views/Shared/Redirect.cshtml
  80. +0
    -54
      src/Services/Identity/Identity.API/Views/Shared/_Layout-SPA.cshtml
  81. +32
    -40
      src/Services/Identity/Identity.API/Views/Shared/_Layout.cshtml
  82. +0
    -27
      src/Services/Identity/Identity.API/Views/Shared/_LoginPartial.cshtml
  83. +3
    -3
      src/Services/Identity/Identity.API/Views/Shared/_ScopeListItem.cshtml
  84. +0
    -14
      src/Services/Identity/Identity.API/Views/Shared/_ValidationScriptsPartial.cshtml
  85. +1
    -1
      src/Services/Identity/Identity.API/Views/Shared/_ValidationSummary.cshtml
  86. +2
    -1
      src/Services/Identity/Identity.API/Views/_ViewImports.cshtml
  87. +1
    -1
      src/Services/Identity/Identity.API/Views/_ViewStart.cshtml
  88. +1
    -0
      src/Services/Identity/Identity.API/keys/is-signing-key-10C452043E09C5A22FC8D97669868B8F.json
  89. +1
    -0
      src/Services/Identity/Identity.API/keys/is-signing-key-1417AE0BAF26F393609EF30FA355D06C.json
  90. +1
    -0
      src/Services/Identity/Identity.API/keys/is-signing-key-22178AEDB80CD0A41DF6874FE93F794D.json
  91. +1
    -0
      src/Services/Identity/Identity.API/keys/is-signing-key-870FA7120249C21C30ADE458B07918C1.json
  92. +1
    -0
      src/Services/Identity/Identity.API/keys/is-signing-key-B0B34AEB91E2638F61EB9A5543F3940F.json
  93. +1
    -0
      src/Services/Identity/Identity.API/keys/is-signing-key-CFE19FEED1112F0740C7CEAAE490834F.json
  94. +1
    -0
      src/Services/Identity/Identity.API/tempkey.jwk
  95. +1
    -0
      src/Services/Identity/Identity.API/wwwroot/js/signin-redirect.js
  96. +6
    -0
      src/Services/Identity/Identity.API/wwwroot/js/signout-redirect.js
  97. +1
    -1
      src/Services/Ordering/Ordering.API/Program.cs
  98. +12
    -0
      src/Services/Ordering/Ordering.API/Startup.cs
  99. +1
    -1
      src/Web/WebMVC/Controllers/AccountController.cs
  100. +1
    -1
      src/Web/WebMVC/Controllers/CartController.cs

+ 1
- 1
src/ApiGateways/Mobile.Bff.Shopping/aggregator/Startup.cs View File

@ -86,7 +86,7 @@ public static class ServiceCollectionExtensions
services.AddSwaggerGen(options => services.AddSwaggerGen(options =>
{ {
options.DescribeAllEnumsAsStrings();
//options.DescribeAllEnumsAsStrings();
options.SwaggerDoc("v1", new OpenApiInfo options.SwaggerDoc("v1", new OpenApiInfo
{ {
Title = "Shopping Aggregator for Mobile Clients", Title = "Shopping Aggregator for Mobile Clients",


+ 1
- 1
src/ApiGateways/Web.Bff.Shopping/aggregator/Startup.cs View File

@ -113,7 +113,7 @@ public static class ServiceCollectionExtensions
services.AddSwaggerGen(options => services.AddSwaggerGen(options =>
{ {
options.DescribeAllEnumsAsStrings();
//options.DescribeAllEnumsAsStrings();
options.SwaggerDoc("v1", new OpenApiInfo options.SwaggerDoc("v1", new OpenApiInfo
{ {


+ 0
- 31
src/Services/Identity/Identity.API/Certificate/Certificate.cs View File

@ -1,31 +0,0 @@
namespace Microsoft.eShopOnContainers.Services.Identity.API.Certificates
{
static class Certificate
{
public static X509Certificate2 Get()
{
var assembly = typeof(Certificate).GetTypeInfo().Assembly;
var names = assembly.GetManifestResourceNames();
/***********************************************************************************************
* Please note that here we are using a local certificate only for testing purposes. In a
* real environment the certificate should be created and stored in a secure way, which is out
* of the scope of this project.
**********************************************************************************************/
using var stream = assembly.GetManifestResourceStream("Identity.API.Certificate.idsrv3test.pfx");
return new X509Certificate2(ReadStream(stream), "idsrv3test");
}
private static byte[] ReadStream(Stream input)
{
byte[] buffer = new byte[16 * 1024];
using MemoryStream ms = new MemoryStream();
int read;
while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
{
ms.Write(buffer, 0, read);
}
return ms.ToArray();
}
}
}

BIN
src/Services/Identity/Identity.API/Certificate/idsrv3test.pfx View File


+ 25
- 25
src/Services/Identity/Identity.API/Configuration/Config.cs View File

@ -45,7 +45,7 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Configuration
} }
// client want to access resources (aka scopes) // client want to access resources (aka scopes)
public static IEnumerable<Client> GetClients(Dictionary<string, string> clientsUrl)
public static IEnumerable<Client> GetClients(IConfiguration configuration)
{ {
return new List<Client> return new List<Client>
{ {
@ -56,10 +56,10 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Configuration
ClientName = "eShop SPA OpenId Client", ClientName = "eShop SPA OpenId Client",
AllowedGrantTypes = GrantTypes.Implicit, AllowedGrantTypes = GrantTypes.Implicit,
AllowAccessTokensViaBrowser = true, AllowAccessTokensViaBrowser = true,
RedirectUris = { $"{clientsUrl["Spa"]}/" },
RedirectUris = { $"{configuration["SpaClient"]}/" },
RequireConsent = false, RequireConsent = false,
PostLogoutRedirectUris = { $"{clientsUrl["Spa"]}/" },
AllowedCorsOrigins = { $"{clientsUrl["Spa"]}" },
PostLogoutRedirectUris = { $"{configuration["SpaClient"]}/" },
AllowedCorsOrigins = { $"{configuration["SpaClient"]}" },
AllowedScopes = AllowedScopes =
{ {
IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.OpenId,
@ -81,10 +81,10 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Configuration
{ {
new Secret("secret".Sha256()) new Secret("secret".Sha256())
}, },
RedirectUris = { clientsUrl["Xamarin"] },
RedirectUris = { configuration["XamarinCallback"] },
RequireConsent = false, RequireConsent = false,
RequirePkce = true, RequirePkce = true,
PostLogoutRedirectUris = { $"{clientsUrl["Xamarin"]}/Account/Redirecting" },
PostLogoutRedirectUris = { $"{configuration["XamarinCallback"]}/Account/Redirecting" },
//AllowedCorsOrigins = { "http://eshopxamarin" }, //AllowedCorsOrigins = { "http://eshopxamarin" },
AllowedScopes = new List<string> AllowedScopes = new List<string>
{ {
@ -109,7 +109,7 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Configuration
new Secret("secret".Sha256()) new Secret("secret".Sha256())
}, },
ClientUri = $"{clientsUrl["Mvc"]}", // public uri of the client
ClientUri = $"{configuration["MvcClient"]}", // public uri of the client
AllowedGrantTypes = GrantTypes.Hybrid, AllowedGrantTypes = GrantTypes.Hybrid,
AllowAccessTokensViaBrowser = false, AllowAccessTokensViaBrowser = false,
RequireConsent = false, RequireConsent = false,
@ -118,11 +118,11 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Configuration
RequirePkce = false, RequirePkce = false,
RedirectUris = new List<string> RedirectUris = new List<string>
{ {
$"{clientsUrl["Mvc"]}/signin-oidc"
$"{configuration["MvcClient"]}/signin-oidc"
}, },
PostLogoutRedirectUris = new List<string> PostLogoutRedirectUris = new List<string>
{ {
$"{clientsUrl["Mvc"]}/signout-callback-oidc"
$"{configuration["MvcClient"]}/signout-callback-oidc"
}, },
AllowedScopes = new List<string> AllowedScopes = new List<string>
{ {
@ -146,7 +146,7 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Configuration
{ {
new Secret("secret".Sha256()) new Secret("secret".Sha256())
}, },
ClientUri = $"{clientsUrl["WebhooksWeb"]}", // public uri of the client
ClientUri = $"{configuration["WebhooksWebClient"]}", // public uri of the client
AllowedGrantTypes = GrantTypes.Hybrid, AllowedGrantTypes = GrantTypes.Hybrid,
AllowAccessTokensViaBrowser = false, AllowAccessTokensViaBrowser = false,
RequireConsent = false, RequireConsent = false,
@ -154,11 +154,11 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Configuration
AlwaysIncludeUserClaimsInIdToken = true, AlwaysIncludeUserClaimsInIdToken = true,
RedirectUris = new List<string> RedirectUris = new List<string>
{ {
$"{clientsUrl["WebhooksWeb"]}/signin-oidc"
$"{configuration["WebhooksWebClient"]}/signin-oidc"
}, },
PostLogoutRedirectUris = new List<string> PostLogoutRedirectUris = new List<string>
{ {
$"{clientsUrl["WebhooksWeb"]}/signout-callback-oidc"
$"{configuration["WebhooksWebClient"]}/signout-callback-oidc"
}, },
AllowedScopes = new List<string> AllowedScopes = new List<string>
{ {
@ -178,18 +178,18 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Configuration
{ {
new Secret("secret".Sha256()) new Secret("secret".Sha256())
}, },
ClientUri = $"{clientsUrl["Mvc"]}", // public uri of the client
ClientUri = $"{configuration["Mvc"]}", // public uri of the client
AllowedGrantTypes = GrantTypes.Hybrid, AllowedGrantTypes = GrantTypes.Hybrid,
AllowAccessTokensViaBrowser = true, AllowAccessTokensViaBrowser = true,
RequireConsent = false, RequireConsent = false,
AllowOfflineAccess = true, AllowOfflineAccess = true,
RedirectUris = new List<string> RedirectUris = new List<string>
{ {
$"{clientsUrl["Mvc"]}/signin-oidc"
$"{configuration["MvcClient"]}/signin-oidc"
}, },
PostLogoutRedirectUris = new List<string> PostLogoutRedirectUris = new List<string>
{ {
$"{clientsUrl["Mvc"]}/signout-callback-oidc"
$"{configuration["MvcClient"]}/signout-callback-oidc"
}, },
AllowedScopes = new List<string> AllowedScopes = new List<string>
{ {
@ -209,8 +209,8 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Configuration
AllowedGrantTypes = GrantTypes.Implicit, AllowedGrantTypes = GrantTypes.Implicit,
AllowAccessTokensViaBrowser = true, AllowAccessTokensViaBrowser = true,
RedirectUris = { $"{clientsUrl["BasketApi"]}/swagger/oauth2-redirect.html" },
PostLogoutRedirectUris = { $"{clientsUrl["BasketApi"]}/swagger/" },
RedirectUris = { $"{configuration["BasketApiClient"]}/swagger/oauth2-redirect.html" },
PostLogoutRedirectUris = { $"{configuration["BasketApiClient"]}/swagger/" },
AllowedScopes = AllowedScopes =
{ {
@ -224,8 +224,8 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Configuration
AllowedGrantTypes = GrantTypes.Implicit, AllowedGrantTypes = GrantTypes.Implicit,
AllowAccessTokensViaBrowser = true, AllowAccessTokensViaBrowser = true,
RedirectUris = { $"{clientsUrl["OrderingApi"]}/swagger/oauth2-redirect.html" },
PostLogoutRedirectUris = { $"{clientsUrl["OrderingApi"]}/swagger/" },
RedirectUris = { $"{configuration["OrderingApiClient"]}/swagger/oauth2-redirect.html" },
PostLogoutRedirectUris = { $"{configuration["OrderingApiClient"]}/swagger/" },
AllowedScopes = AllowedScopes =
{ {
@ -239,8 +239,8 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Configuration
AllowedGrantTypes = GrantTypes.Implicit, AllowedGrantTypes = GrantTypes.Implicit,
AllowAccessTokensViaBrowser = true, AllowAccessTokensViaBrowser = true,
RedirectUris = { $"{clientsUrl["MobileShoppingAgg"]}/swagger/oauth2-redirect.html" },
PostLogoutRedirectUris = { $"{clientsUrl["MobileShoppingAgg"]}/swagger/" },
RedirectUris = { $"{configuration["MobileShoppingAggClient"]}/swagger/oauth2-redirect.html" },
PostLogoutRedirectUris = { $"{configuration["MobileShoppingAggClient"]}/swagger/" },
AllowedScopes = AllowedScopes =
{ {
@ -254,8 +254,8 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Configuration
AllowedGrantTypes = GrantTypes.Implicit, AllowedGrantTypes = GrantTypes.Implicit,
AllowAccessTokensViaBrowser = true, AllowAccessTokensViaBrowser = true,
RedirectUris = { $"{clientsUrl["WebShoppingAgg"]}/swagger/oauth2-redirect.html" },
PostLogoutRedirectUris = { $"{clientsUrl["WebShoppingAgg"]}/swagger/" },
RedirectUris = { $"{configuration["WebShoppingAggClient"]}/swagger/oauth2-redirect.html" },
PostLogoutRedirectUris = { $"{configuration["WebShoppingAggClient"]}/swagger/" },
AllowedScopes = AllowedScopes =
{ {
@ -270,8 +270,8 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Configuration
AllowedGrantTypes = GrantTypes.Implicit, AllowedGrantTypes = GrantTypes.Implicit,
AllowAccessTokensViaBrowser = true, AllowAccessTokensViaBrowser = true,
RedirectUris = { $"{clientsUrl["WebhooksApi"]}/swagger/oauth2-redirect.html" },
PostLogoutRedirectUris = { $"{clientsUrl["WebhooksApi"]}/swagger/" },
RedirectUris = { $"{configuration["WebhooksApiClient"]}/swagger/oauth2-redirect.html" },
PostLogoutRedirectUris = { $"{configuration["WebhooksApiClient"]}/swagger/" },
AllowedScopes = AllowedScopes =
{ {


+ 0
- 299
src/Services/Identity/Identity.API/Controllers/AccountController.cs View File

@ -1,299 +0,0 @@
namespace Microsoft.eShopOnContainers.Services.Identity.API.Controllers
{
/// <summary>
/// This sample controller implements a typical login/logout/provision workflow for local accounts.
/// The login service encapsulates the interactions with the user data store. This data store is in-memory only and cannot be used for production!
/// The interaction service provides a way for the UI to communicate with identityserver for validation and context retrieval
/// </summary>
public class AccountController : Controller
{
//private readonly InMemoryUserLoginService _loginService;
private readonly ILoginService<ApplicationUser> _loginService;
private readonly IIdentityServerInteractionService _interaction;
private readonly IClientStore _clientStore;
private readonly ILogger<AccountController> _logger;
private readonly UserManager<ApplicationUser> _userManager;
private readonly IConfiguration _configuration;
public AccountController(
//InMemoryUserLoginService loginService,
ILoginService<ApplicationUser> loginService,
IIdentityServerInteractionService interaction,
IClientStore clientStore,
ILogger<AccountController> logger,
UserManager<ApplicationUser> userManager,
IConfiguration configuration)
{
_loginService = loginService;
_interaction = interaction;
_clientStore = clientStore;
_logger = logger;
_userManager = userManager;
_configuration = configuration;
}
/// <summary>
/// Show login page
/// </summary>
[HttpGet]
public async Task<IActionResult> Login(string returnUrl)
{
var context = await _interaction.GetAuthorizationContextAsync(returnUrl);
if (context?.IdP != null)
{
throw new NotImplementedException("External login is not implemented!");
}
var vm = await BuildLoginViewModelAsync(returnUrl, context);
ViewData["ReturnUrl"] = returnUrl;
return View(vm);
}
/// <summary>
/// Handle postback from username/password login
/// </summary>
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginViewModel model)
{
if (ModelState.IsValid)
{
var user = await _loginService.FindByUsername(model.Email);
if (await _loginService.ValidateCredentials(user, model.Password))
{
var tokenLifetime = _configuration.GetValue("TokenLifetimeMinutes", 120);
var props = new AuthenticationProperties
{
ExpiresUtc = DateTimeOffset.UtcNow.AddMinutes(tokenLifetime),
AllowRefresh = true,
RedirectUri = model.ReturnUrl
};
if (model.RememberMe)
{
var permanentTokenLifetime = _configuration.GetValue("PermanentTokenLifetimeDays", 365);
props.ExpiresUtc = DateTimeOffset.UtcNow.AddDays(permanentTokenLifetime);
props.IsPersistent = true;
};
await _loginService.SignInAsync(user, props);
// make sure the returnUrl is still valid, and if yes - redirect back to authorize endpoint
if (_interaction.IsValidReturnUrl(model.ReturnUrl))
{
return Redirect(model.ReturnUrl);
}
return Redirect("~/");
}
ModelState.AddModelError("", "Invalid username or password.");
}
// something went wrong, show form with error
var vm = await BuildLoginViewModelAsync(model);
ViewData["ReturnUrl"] = model.ReturnUrl;
return View(vm);
}
private async Task<LoginViewModel> BuildLoginViewModelAsync(string returnUrl, AuthorizationRequest context)
{
var allowLocal = true;
if (context?.Client.ClientId != null)
{
var client = await _clientStore.FindEnabledClientByIdAsync(context.Client.ClientId);
if (client != null)
{
allowLocal = client.EnableLocalLogin;
}
}
return new LoginViewModel
{
ReturnUrl = returnUrl,
Email = context?.LoginHint,
};
}
private async Task<LoginViewModel> BuildLoginViewModelAsync(LoginViewModel model)
{
var context = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl);
var vm = await BuildLoginViewModelAsync(model.ReturnUrl, context);
vm.Email = model.Email;
vm.RememberMe = model.RememberMe;
return vm;
}
/// <summary>
/// Show logout page
/// </summary>
[HttpGet]
public async Task<IActionResult> Logout(string logoutId)
{
if (User.Identity.IsAuthenticated == false)
{
// if the user is not authenticated, then just show logged out page
return await Logout(new LogoutViewModel { LogoutId = logoutId });
}
//Test for Xamarin.
var context = await _interaction.GetLogoutContextAsync(logoutId);
if (context?.ShowSignoutPrompt == false)
{
//it's safe to automatically sign-out
return await Logout(new LogoutViewModel { LogoutId = logoutId });
}
// show the logout prompt. this prevents attacks where the user
// is automatically signed out by another malicious web page.
var vm = new LogoutViewModel
{
LogoutId = logoutId
};
return View(vm);
}
/// <summary>
/// Handle logout page postback
/// </summary>
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Logout(LogoutViewModel model)
{
var idp = User?.FindFirst(JwtClaimTypes.IdentityProvider)?.Value;
if (idp != null && idp != IdentityServerConstants.LocalIdentityProvider)
{
if (model.LogoutId == null)
{
// if there's no current logout context, we need to create one
// this captures necessary info from the current logged in user
// before we signout and redirect away to the external IdP for signout
model.LogoutId = await _interaction.CreateLogoutContextAsync();
}
string url = "/Account/Logout?logoutId=" + model.LogoutId;
try
{
// hack: try/catch to handle social providers that throw
await HttpContext.SignOutAsync(idp, new AuthenticationProperties
{
RedirectUri = url
});
}
catch (Exception ex)
{
_logger.LogError(ex, "LOGOUT ERROR: {ExceptionMessage}", ex.Message);
}
}
// delete authentication cookie
await HttpContext.SignOutAsync();
await HttpContext.SignOutAsync(IdentityConstants.ApplicationScheme);
// set this so UI rendering sees an anonymous user
HttpContext.User = new ClaimsPrincipal(new ClaimsIdentity());
// get context information (client name, post logout redirect URI and iframe for federated signout)
var logout = await _interaction.GetLogoutContextAsync(model.LogoutId);
return Redirect(logout?.PostLogoutRedirectUri);
}
public async Task<IActionResult> DeviceLogOut(string redirectUrl)
{
// delete authentication cookie
await HttpContext.SignOutAsync();
// set this so UI rendering sees an anonymous user
HttpContext.User = new ClaimsPrincipal(new ClaimsIdentity());
return Redirect(redirectUrl);
}
// GET: /Account/Register
[HttpGet]
[AllowAnonymous]
public IActionResult Register(string returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
return View();
}
//
// POST: /Account/Register
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Register(RegisterViewModel model, string returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
if (ModelState.IsValid)
{
var user = new ApplicationUser
{
UserName = model.Email,
Email = model.Email,
CardHolderName = model.User.CardHolderName,
CardNumber = model.User.CardNumber,
CardType = model.User.CardType,
City = model.User.City,
Country = model.User.Country,
Expiration = model.User.Expiration,
LastName = model.User.LastName,
Name = model.User.Name,
Street = model.User.Street,
State = model.User.State,
ZipCode = model.User.ZipCode,
PhoneNumber = model.User.PhoneNumber,
SecurityNumber = model.User.SecurityNumber
};
var result = await _userManager.CreateAsync(user, model.Password);
if (result.Errors.Count() > 0)
{
AddErrors(result);
// If we got this far, something failed, redisplay form
return View(model);
}
}
if (returnUrl != null)
{
if (HttpContext.User.Identity.IsAuthenticated)
return Redirect(returnUrl);
else
if (ModelState.IsValid)
return RedirectToAction("login", "account", new { returnUrl = returnUrl });
else
return View(model);
}
return RedirectToAction("index", "home");
}
[HttpGet]
public IActionResult Redirecting()
{
return View();
}
private void AddErrors(IdentityResult result)
{
foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
}
}
}

+ 0
- 249
src/Services/Identity/Identity.API/Controllers/ConsentController.cs View File

@ -1,249 +0,0 @@
using Duende.IdentityServer.Events;
using Duende.IdentityServer.Extensions;
using Identity.API.Extensions;
using Microsoft.eShopOnContainers.Services.Identity.API.Models.ConsentViewModels;
namespace Microsoft.eShopOnContainers.Services.Identity.API.Controllers
{
/// <summary>
/// This controller implements the consent logic
/// </summary>
public class ConsentController : Controller
{
private readonly ILogger<ConsentController> _logger;
private readonly IIdentityServerInteractionService _interaction;
private readonly IEventService _events;
public ConsentController(
ILogger<ConsentController> logger,
IIdentityServerInteractionService interaction,
IEventService events)
{
_interaction = interaction;
_events = events;
_logger = logger;
}
/// <summary>
/// Shows the consent screen
/// </summary>
/// <param name="returnUrl"></param>
/// <returns></returns>
[HttpGet]
public async Task<IActionResult> Index(string returnUrl)
{
var vm = await BuildViewModelAsync(returnUrl);
if (vm != null)
{
return View("Index", vm);
}
return View("Error");
}
/// <summary>
/// Handles the consent screen postback
/// </summary>
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Index(ConsentInputModel model)
{
var result = await ProcessConsent(model);
if (result.IsRedirect)
{
var context = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl);
if (context?.IsNativeClient() == true)
{
// The client is native, so this change in how to
// return the response is for better UX for the end user.
return this.LoadingPage("Redirect", result.RedirectUri);
}
return Redirect(result.RedirectUri);
}
if (result.HasValidationError)
{
ModelState.AddModelError(string.Empty, result.ValidationError);
}
if (result.ShowView)
{
return View("Index", result.ViewModel);
}
return View("Error");
}
/*****************************************/
/* helper APIs for the ConsentController */
/*****************************************/
private async Task<ProcessConsentResult> ProcessConsent(ConsentInputModel model)
{
var result = new ProcessConsentResult();
// validate return url is still valid
var request = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl);
if (request == null) return result;
ConsentResponse grantedConsent = null;
// user clicked 'no' - send back the standard 'access_denied' response
if (model?.Button == "no")
{
grantedConsent = new ConsentResponse { Error = AuthorizationError.AccessDenied };
// emit event
await _events.RaiseAsync(new ConsentDeniedEvent(User.GetSubjectId(), request.Client.ClientId, request.ValidatedResources.RawScopeValues));
}
// user clicked 'yes' - validate the data
else if (model?.Button == "yes")
{
// if the user consented to some scope, build the response model
if (model.ScopesConsented != null && model.ScopesConsented.Any())
{
var scopes = model.ScopesConsented;
if (ConsentOptions.EnableOfflineAccess == false)
{
scopes = scopes.Where(x => x != Duende.IdentityServer.IdentityServerConstants.StandardScopes.OfflineAccess);
}
grantedConsent = new ConsentResponse
{
RememberConsent = model.RememberConsent,
ScopesValuesConsented = scopes.ToArray(),
Description = model.Description
};
// emit event
await _events.RaiseAsync(new ConsentGrantedEvent(User.GetSubjectId(), request.Client.ClientId, request.ValidatedResources.RawScopeValues, grantedConsent.ScopesValuesConsented, grantedConsent.RememberConsent));
}
else
{
result.ValidationError = ConsentOptions.MustChooseOneErrorMessage;
}
}
else
{
result.ValidationError = ConsentOptions.InvalidSelectionErrorMessage;
}
if (grantedConsent != null)
{
// communicate outcome of consent back to identityserver
await _interaction.GrantConsentAsync(request, grantedConsent);
// indicate that's it ok to redirect back to authorization endpoint
result.RedirectUri = model.ReturnUrl;
result.Client = request.Client;
}
else
{
// we need to redisplay the consent UI
result.ViewModel = await BuildViewModelAsync(model.ReturnUrl, model);
}
return result;
}
private async Task<ConsentViewModel> BuildViewModelAsync(string returnUrl, ConsentInputModel model = null)
{
var request = await _interaction.GetAuthorizationContextAsync(returnUrl);
if (request != null)
{
return CreateConsentViewModel(model, returnUrl, request);
}
else
{
_logger.LogError("No consent request matching request: {0}", returnUrl);
}
return null;
}
private ConsentViewModel CreateConsentViewModel(
ConsentInputModel model, string returnUrl,
AuthorizationRequest request)
{
var vm = new ConsentViewModel
{
RememberConsent = model?.RememberConsent ?? true,
ScopesConsented = model?.ScopesConsented ?? Enumerable.Empty<string>(),
Description = model?.Description,
ReturnUrl = returnUrl,
ClientName = request.Client.ClientName ?? request.Client.ClientId,
ClientUrl = request.Client.ClientUri,
ClientLogoUrl = request.Client.LogoUri,
AllowRememberConsent = request.Client.AllowRememberConsent
};
vm.IdentityScopes = request.ValidatedResources.Resources.IdentityResources.Select(x => CreateScopeViewModel(x, vm.ScopesConsented.Contains(x.Name) || model == null)).ToArray();
var apiScopes = new List<ScopeViewModel>();
foreach (var parsedScope in request.ValidatedResources.ParsedScopes)
{
var apiScope = request.ValidatedResources.Resources.FindApiScope(parsedScope.ParsedName);
if (apiScope != null)
{
var scopeVm = CreateScopeViewModel(parsedScope, apiScope, vm.ScopesConsented.Contains(parsedScope.RawValue) || model == null);
apiScopes.Add(scopeVm);
}
}
if (ConsentOptions.EnableOfflineAccess && request.ValidatedResources.Resources.OfflineAccess)
{
apiScopes.Add(GetOfflineAccessScope(vm.ScopesConsented.Contains(IdentityServerConstants.StandardScopes.OfflineAccess) || model == null));
}
vm.ApiScopes = apiScopes;
return vm;
}
private ScopeViewModel CreateScopeViewModel(IdentityResource identity, bool check)
{
return new ScopeViewModel
{
Value = identity.Name,
DisplayName = identity.DisplayName ?? identity.Name,
Description = identity.Description,
Emphasize = identity.Emphasize,
Required = identity.Required,
Checked = check || identity.Required
};
}
public ScopeViewModel CreateScopeViewModel(ParsedScopeValue parsedScopeValue, ApiScope apiScope, bool check)
{
var displayName = apiScope.DisplayName ?? apiScope.Name;
if (!String.IsNullOrWhiteSpace(parsedScopeValue.ParsedParameter))
{
displayName += ":" + parsedScopeValue.ParsedParameter;
}
return new ScopeViewModel
{
Value = parsedScopeValue.RawValue,
DisplayName = displayName,
Description = apiScope.Description,
Emphasize = apiScope.Emphasize,
Required = apiScope.Required,
Checked = check || apiScope.Required
};
}
private ScopeViewModel GetOfflineAccessScope(bool check)
{
return new ScopeViewModel
{
Value = Duende.IdentityServer.IdentityServerConstants.StandardScopes.OfflineAccess,
DisplayName = ConsentOptions.OfflineAccessDisplayName,
Description = ConsentOptions.OfflineAccessDescription,
Emphasize = true,
Checked = check
};
}
}
}

+ 0
- 46
src/Services/Identity/Identity.API/Controllers/HomeController.cs View File

@ -1,46 +0,0 @@
namespace Microsoft.eShopOnContainers.Services.Identity.API.Controllers
{
public class HomeController : Controller
{
private readonly IIdentityServerInteractionService _interaction;
private readonly IOptionsSnapshot<AppSettings> _settings;
private readonly IRedirectService _redirectSvc;
public HomeController(IIdentityServerInteractionService interaction, IOptionsSnapshot<AppSettings> settings, IRedirectService redirectSvc)
{
_interaction = interaction;
_settings = settings;
_redirectSvc = redirectSvc;
}
public IActionResult Index(string returnUrl)
{
return View();
}
public IActionResult ReturnToOriginalApplication(string returnUrl)
{
if (returnUrl != null)
return Redirect(_redirectSvc.ExtractRedirectUriFromReturnUrl(returnUrl));
else
return RedirectToAction("Index", "Home");
}
/// <summary>
/// Shows the error page
/// </summary>
public async Task<IActionResult> Error(string errorId)
{
var vm = new ErrorViewModel();
// retrieve error details from identityserver
var message = await _interaction.GetErrorContextAsync(errorId);
if (message != null)
{
vm.Error = message;
}
return View("Error", vm);
}
}
}

+ 12
- 13
src/Services/Identity/Identity.API/Data/ApplicationDbContext.cs View File

@ -1,18 +1,17 @@
namespace Microsoft.eShopOnContainers.Services.Identity.API.Data
namespace Microsoft.eShopOnContainers.Services.Identity.API.Data;
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{ {
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{ {
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// Customize the ASP.NET Identity model and override the defaults if needed.
// For example, you can rename the ASP.NET Identity table names and more.
// Add your customizations after calling base.OnModelCreating(builder);
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// Customize the ASP.NET Identity model and override the defaults if needed.
// For example, you can rename the ASP.NET Identity table names and more.
// Add your customizations after calling base.OnModelCreating(builder);
} }
} }

+ 0
- 218
src/Services/Identity/Identity.API/Data/ApplicationDbContextSeed.cs View File

@ -1,218 +0,0 @@
namespace Microsoft.eShopOnContainers.Services.Identity.API.Data
{
using Microsoft.Extensions.Logging;
public class ApplicationDbContextSeed
{
private readonly IPasswordHasher<ApplicationUser> _passwordHasher = new PasswordHasher<ApplicationUser>();
public async Task SeedAsync(ApplicationDbContext context, IWebHostEnvironment env,
ILogger<ApplicationDbContextSeed> logger, IOptions<AppSettings> settings, int? retry = 0)
{
int retryForAvaiability = retry.Value;
try
{
var useCustomizationData = settings.Value.UseCustomizationData;
var contentRootPath = env.ContentRootPath;
var webroot = env.WebRootPath;
if (!context.Users.Any())
{
context.Users.AddRange(useCustomizationData
? GetUsersFromFile(contentRootPath, logger)
: GetDefaultUser());
await context.SaveChangesAsync();
}
if (useCustomizationData)
{
GetPreconfiguredImages(contentRootPath, webroot, logger);
}
}
catch (Exception ex)
{
if (retryForAvaiability < 10)
{
retryForAvaiability++;
logger.LogError(ex, "EXCEPTION ERROR while migrating {DbContextName}", nameof(ApplicationDbContext));
await SeedAsync(context, env, logger, settings, retryForAvaiability);
}
}
}
private IEnumerable<ApplicationUser> GetUsersFromFile(string contentRootPath, ILogger logger)
{
string csvFileUsers = Path.Combine(contentRootPath, "Setup", "Users.csv");
if (!File.Exists(csvFileUsers))
{
return GetDefaultUser();
}
string[] csvheaders;
try
{
string[] requiredHeaders = {
"cardholdername", "cardnumber", "cardtype", "city", "country",
"email", "expiration", "lastname", "name", "phonenumber",
"username", "zipcode", "state", "street", "securitynumber",
"normalizedemail", "normalizedusername", "password"
};
csvheaders = GetHeaders(requiredHeaders, csvFileUsers);
}
catch (Exception ex)
{
logger.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message);
return GetDefaultUser();
}
List<ApplicationUser> users = File.ReadAllLines(csvFileUsers)
.Skip(1) // skip header column
.Select(row => Regex.Split(row, ",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)"))
.SelectTry(column => CreateApplicationUser(column, csvheaders))
.OnCaughtException(ex => { logger.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message); return null; })
.Where(x => x != null)
.ToList();
return users;
}
private ApplicationUser CreateApplicationUser(string[] column, string[] headers)
{
if (column.Count() != headers.Count())
{
throw new Exception($"column count '{column.Count()}' not the same as headers count'{headers.Count()}'");
}
string cardtypeString = column[Array.IndexOf(headers, "cardtype")].Trim('"').Trim();
if (!int.TryParse(cardtypeString, out int cardtype))
{
throw new Exception($"cardtype='{cardtypeString}' is not a number");
}
var user = new ApplicationUser
{
CardHolderName = column[Array.IndexOf(headers, "cardholdername")].Trim('"').Trim(),
CardNumber = column[Array.IndexOf(headers, "cardnumber")].Trim('"').Trim(),
CardType = cardtype,
City = column[Array.IndexOf(headers, "city")].Trim('"').Trim(),
Country = column[Array.IndexOf(headers, "country")].Trim('"').Trim(),
Email = column[Array.IndexOf(headers, "email")].Trim('"').Trim(),
Expiration = column[Array.IndexOf(headers, "expiration")].Trim('"').Trim(),
Id = Guid.NewGuid().ToString(),
LastName = column[Array.IndexOf(headers, "lastname")].Trim('"').Trim(),
Name = column[Array.IndexOf(headers, "name")].Trim('"').Trim(),
PhoneNumber = column[Array.IndexOf(headers, "phonenumber")].Trim('"').Trim(),
UserName = column[Array.IndexOf(headers, "username")].Trim('"').Trim(),
ZipCode = column[Array.IndexOf(headers, "zipcode")].Trim('"').Trim(),
State = column[Array.IndexOf(headers, "state")].Trim('"').Trim(),
Street = column[Array.IndexOf(headers, "street")].Trim('"').Trim(),
SecurityNumber = column[Array.IndexOf(headers, "securitynumber")].Trim('"').Trim(),
NormalizedEmail = column[Array.IndexOf(headers, "normalizedemail")].Trim('"').Trim(),
NormalizedUserName = column[Array.IndexOf(headers, "normalizedusername")].Trim('"').Trim(),
SecurityStamp = Guid.NewGuid().ToString("D"),
PasswordHash = column[Array.IndexOf(headers, "password")].Trim('"').Trim(), // Note: This is the password
};
user.PasswordHash = _passwordHasher.HashPassword(user, user.PasswordHash);
return user;
}
private IEnumerable<ApplicationUser> GetDefaultUser()
{
var user =
new ApplicationUser()
{
CardHolderName = "DemoUser",
CardNumber = "4012888888881881",
CardType = 1,
City = "Redmond",
Country = "U.S.",
Email = "demouser@microsoft.com",
Expiration = "12/25",
Id = Guid.NewGuid().ToString(),
LastName = "DemoLastName",
Name = "DemoUser",
PhoneNumber = "1234567890",
UserName = "demouser@microsoft.com",
ZipCode = "98052",
State = "WA",
Street = "15703 NE 61st Ct",
SecurityNumber = "535",
NormalizedEmail = "DEMOUSER@MICROSOFT.COM",
NormalizedUserName = "DEMOUSER@MICROSOFT.COM",
SecurityStamp = Guid.NewGuid().ToString("D"),
};
user.PasswordHash = _passwordHasher.HashPassword(user, "Pass@word1");
return new List<ApplicationUser>()
{
user
};
}
static string[] GetHeaders(string[] requiredHeaders, string csvfile)
{
string[] csvheaders = File.ReadLines(csvfile).First().ToLowerInvariant().Split(',');
if (csvheaders.Count() != requiredHeaders.Count())
{
throw new Exception($"requiredHeader count '{ requiredHeaders.Count()}' is different then read header '{csvheaders.Count()}'");
}
foreach (var requiredHeader in requiredHeaders)
{
if (!csvheaders.Contains(requiredHeader))
{
throw new Exception($"does not contain required header '{requiredHeader}'");
}
}
return csvheaders;
}
static void GetPreconfiguredImages(string contentRootPath, string webroot, ILogger logger)
{
try
{
string imagesZipFile = Path.Combine(contentRootPath, "Setup", "images.zip");
if (!File.Exists(imagesZipFile))
{
logger.LogError("Zip file '{ZipFileName}' does not exists.", imagesZipFile);
return;
}
string imagePath = Path.Combine(webroot, "images");
string[] imageFiles = Directory.GetFiles(imagePath).Select(file => Path.GetFileName(file)).ToArray();
using ZipArchive zip = ZipFile.Open(imagesZipFile, ZipArchiveMode.Read);
foreach (ZipArchiveEntry entry in zip.Entries)
{
if (imageFiles.Contains(entry.Name))
{
string destinationFilename = Path.Combine(imagePath, entry.Name);
if (File.Exists(destinationFilename))
{
File.Delete(destinationFilename);
}
entry.ExtractToFile(destinationFilename);
}
else
{
logger.LogWarning("Skipped file '{FileName}' in zipfile '{ZipFileName}'", entry.Name, imagesZipFile);
}
}
}
catch (Exception ex)
{
logger.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message); ;
}
}
}
}

+ 0
- 83
src/Services/Identity/Identity.API/Data/ConfigurationDbContextSeed.cs View File

@ -1,83 +0,0 @@
using Duende.IdentityServer.EntityFramework.Entities;
namespace Microsoft.eShopOnContainers.Services.Identity.API.Data
{
public class ConfigurationDbContextSeed
{
public async Task SeedAsync(ConfigurationDbContext context, IConfiguration configuration)
{
//callbacks urls from config:
var clientUrls = new Dictionary<string, string>();
clientUrls.Add("Mvc", configuration.GetValue<string>("MvcClient"));
clientUrls.Add("Spa", configuration.GetValue<string>("SpaClient"));
clientUrls.Add("Xamarin", configuration.GetValue<string>("XamarinCallback"));
clientUrls.Add("BasketApi", configuration.GetValue<string>("BasketApiClient"));
clientUrls.Add("OrderingApi", configuration.GetValue<string>("OrderingApiClient"));
clientUrls.Add("MobileShoppingAgg", configuration.GetValue<string>("MobileShoppingAggClient"));
clientUrls.Add("WebShoppingAgg", configuration.GetValue<string>("WebShoppingAggClient"));
clientUrls.Add("WebhooksApi", configuration.GetValue<string>("WebhooksApiClient"));
clientUrls.Add("WebhooksWeb", configuration.GetValue<string>("WebhooksWebClient"));
if (!context.Clients.Any())
{
foreach (var client in Config.GetClients(clientUrls))
{
context.Clients.Add(client.ToEntity());
}
await context.SaveChangesAsync();
}
// Checking always for old redirects to fix existing deployments
// to use new swagger-ui redirect uri as of v3.0.0
// There should be no problem for new ones
// ref: https://github.com/dotnet-architecture/eShopOnContainers/issues/586
else
{
List<ClientRedirectUri> oldRedirects = (await context.Clients.Include(c => c.RedirectUris).ToListAsync())
.SelectMany(c => c.RedirectUris)
.Where(ru => ru.RedirectUri.EndsWith("/o2c.html"))
.ToList();
if (oldRedirects.Any())
{
foreach (var ru in oldRedirects)
{
ru.RedirectUri = ru.RedirectUri.Replace("/o2c.html", "/oauth2-redirect.html");
context.Update(ru.Client);
}
await context.SaveChangesAsync();
}
}
if (!context.IdentityResources.Any())
{
foreach (var resource in Config.GetResources())
{
context.IdentityResources.Add(resource.ToEntity());
}
await context.SaveChangesAsync();
}
if (!context.ApiResources.Any())
{
foreach (var api in Config.GetApis())
{
context.ApiResources.Add(api.ToEntity());
}
await context.SaveChangesAsync();
}
if (!context.ApiScopes.Any())
{
foreach (var apiScope in Config.GetApiScopes())
{
context.ApiScopes.Add(apiScope.ToEntity());
}
await context.SaveChangesAsync();
}
}
}
}

src/Services/Identity/Identity.API/Migrations/20210813072445_InitialMigration.Designer.cs → src/Services/Identity/Identity.API/Data/Migrations/20210914100206_InitialMigration.Designer.cs View File

@ -7,10 +7,10 @@ using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Microsoft.eShopOnContainers.Services.Identity.API.Data; using Microsoft.eShopOnContainers.Services.Identity.API.Data;
namespace Identity.API.Migrations
namespace Microsoft.eShopOnContainers.Services.Identity.API.Data.Migrations
{ {
[DbContext(typeof(ApplicationDbContext))] [DbContext(typeof(ApplicationDbContext))]
[Migration("20210813072445_InitialMigration")]
[Migration("20210914100206_InitialMigration")]
partial class InitialMigration partial class InitialMigration
{ {
protected override void BuildTargetModel(ModelBuilder modelBuilder) protected override void BuildTargetModel(ModelBuilder modelBuilder)
@ -18,7 +18,7 @@ namespace Identity.API.Migrations
#pragma warning disable 612, 618 #pragma warning disable 612, 618
modelBuilder modelBuilder
.HasAnnotation("Relational:MaxIdentifierLength", 128) .HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("ProductVersion", "6.0.0")
.HasAnnotation("ProductVersion", "5.0.9")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
@ -45,7 +45,7 @@ namespace Identity.API.Migrations
.HasDatabaseName("RoleNameIndex") .HasDatabaseName("RoleNameIndex")
.HasFilter("[NormalizedName] IS NOT NULL"); .HasFilter("[NormalizedName] IS NOT NULL");
b.ToTable("AspNetRoles", (string)null);
b.ToTable("AspNetRoles");
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
@ -69,7 +69,7 @@ namespace Identity.API.Migrations
b.HasIndex("RoleId"); b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims", (string)null);
b.ToTable("AspNetRoleClaims");
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
@ -93,7 +93,7 @@ namespace Identity.API.Migrations
b.HasIndex("UserId"); b.HasIndex("UserId");
b.ToTable("AspNetUserClaims", (string)null);
b.ToTable("AspNetUserClaims");
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
@ -115,7 +115,7 @@ namespace Identity.API.Migrations
b.HasIndex("UserId"); b.HasIndex("UserId");
b.ToTable("AspNetUserLogins", (string)null);
b.ToTable("AspNetUserLogins");
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
@ -130,7 +130,7 @@ namespace Identity.API.Migrations
b.HasIndex("RoleId"); b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles", (string)null);
b.ToTable("AspNetUserRoles");
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
@ -149,7 +149,7 @@ namespace Identity.API.Migrations
b.HasKey("UserId", "LoginProvider", "Name"); b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens", (string)null);
b.ToTable("AspNetUserTokens");
}); });
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Identity.API.Models.ApplicationUser", b => modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Identity.API.Models.ApplicationUser", b =>
@ -261,7 +261,7 @@ namespace Identity.API.Migrations
.HasDatabaseName("UserNameIndex") .HasDatabaseName("UserNameIndex")
.HasFilter("[NormalizedUserName] IS NOT NULL"); .HasFilter("[NormalizedUserName] IS NOT NULL");
b.ToTable("AspNetUsers", (string)null);
b.ToTable("AspNetUsers");
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>

src/Services/Identity/Identity.API/Migrations/20210813072445_InitialMigration.cs → src/Services/Identity/Identity.API/Data/Migrations/20210914100206_InitialMigration.cs View File

@ -1,7 +1,7 @@
using System; using System;
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;
namespace Identity.API.Migrations
namespace Microsoft.eShopOnContainers.Services.Identity.API.Data.Migrations
{ {
public partial class InitialMigration : Migration public partial class InitialMigration : Migration
{ {

src/Services/Identity/Identity.API/Migrations/ApplicationDbContextModelSnapshot.cs → src/Services/Identity/Identity.API/Data/Migrations/ApplicationDbContextModelSnapshot.cs View File

@ -6,7 +6,7 @@ using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Microsoft.eShopOnContainers.Services.Identity.API.Data; using Microsoft.eShopOnContainers.Services.Identity.API.Data;
namespace Identity.API.Migrations
namespace Microsoft.eShopOnContainers.Services.Identity.API.Data.Migrations
{ {
[DbContext(typeof(ApplicationDbContext))] [DbContext(typeof(ApplicationDbContext))]
partial class ApplicationDbContextModelSnapshot : ModelSnapshot partial class ApplicationDbContextModelSnapshot : ModelSnapshot
@ -16,7 +16,7 @@ namespace Identity.API.Migrations
#pragma warning disable 612, 618 #pragma warning disable 612, 618
modelBuilder modelBuilder
.HasAnnotation("Relational:MaxIdentifierLength", 128) .HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("ProductVersion", "6.2.0")
.HasAnnotation("ProductVersion", "5.0.9")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
@ -43,7 +43,7 @@ namespace Identity.API.Migrations
.HasDatabaseName("RoleNameIndex") .HasDatabaseName("RoleNameIndex")
.HasFilter("[NormalizedName] IS NOT NULL"); .HasFilter("[NormalizedName] IS NOT NULL");
b.ToTable("AspNetRoles", (string)null);
b.ToTable("AspNetRoles");
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
@ -67,7 +67,7 @@ namespace Identity.API.Migrations
b.HasIndex("RoleId"); b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims", (string)null);
b.ToTable("AspNetRoleClaims");
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
@ -91,7 +91,7 @@ namespace Identity.API.Migrations
b.HasIndex("UserId"); b.HasIndex("UserId");
b.ToTable("AspNetUserClaims", (string)null);
b.ToTable("AspNetUserClaims");
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
@ -113,7 +113,7 @@ namespace Identity.API.Migrations
b.HasIndex("UserId"); b.HasIndex("UserId");
b.ToTable("AspNetUserLogins", (string)null);
b.ToTable("AspNetUserLogins");
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
@ -128,7 +128,7 @@ namespace Identity.API.Migrations
b.HasIndex("RoleId"); b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles", (string)null);
b.ToTable("AspNetUserRoles");
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
@ -147,7 +147,7 @@ namespace Identity.API.Migrations
b.HasKey("UserId", "LoginProvider", "Name"); b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens", (string)null);
b.ToTable("AspNetUserTokens");
}); });
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Identity.API.Models.ApplicationUser", b => modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Identity.API.Models.ApplicationUser", b =>
@ -259,7 +259,7 @@ namespace Identity.API.Migrations
.HasDatabaseName("UserNameIndex") .HasDatabaseName("UserNameIndex")
.HasFilter("[NormalizedUserName] IS NOT NULL"); .HasFilter("[NormalizedUserName] IS NOT NULL");
b.ToTable("AspNetUsers", (string)null);
b.ToTable("AspNetUsers");
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>

+ 0
- 23
src/Services/Identity/Identity.API/Extensions/Extension.cs View File

@ -1,23 +0,0 @@
namespace Identity.API.Extensions
{
public static class Extensions
{
/// <summary>
/// Checks if the redirect URI is for a native client.
/// </summary>
/// <returns></returns>
public static bool IsNativeClient(this AuthorizationRequest context)
{
return !context.RedirectUri.StartsWith("https", StringComparison.Ordinal)
&& !context.RedirectUri.StartsWith("http", StringComparison.Ordinal);
}
public static IActionResult LoadingPage(this Controller controller, string viewName, string redirectUri)
{
controller.HttpContext.Response.StatusCode = 200;
controller.HttpContext.Response.Headers["Location"] = "";
return controller.View(viewName, new RedirectViewModel { RedirectUrl = redirectUri });
}
}
}

+ 0
- 46
src/Services/Identity/Identity.API/Extensions/LinqSelectExtensions.cs View File

@ -1,46 +0,0 @@
namespace Microsoft.eShopOnContainers.Services.Identity.API.Extensions
{
public static class LinqSelectExtensions
{
public static IEnumerable<SelectTryResult<TSource, TResult>> SelectTry<TSource, TResult>(this IEnumerable<TSource> enumerable, Func<TSource, TResult> selector)
{
foreach (TSource element in enumerable)
{
SelectTryResult<TSource, TResult> returnedValue;
try
{
returnedValue = new SelectTryResult<TSource, TResult>(element, selector(element), null);
}
catch (Exception ex)
{
returnedValue = new SelectTryResult<TSource, TResult>(element, default(TResult), ex);
}
yield return returnedValue;
}
}
public static IEnumerable<TResult> OnCaughtException<TSource, TResult>(this IEnumerable<SelectTryResult<TSource, TResult>> enumerable, Func<Exception, TResult> exceptionHandler)
{
return enumerable.Select(x => x.CaughtException == null ? x.Result : exceptionHandler(x.CaughtException));
}
public static IEnumerable<TResult> OnCaughtException<TSource, TResult>(this IEnumerable<SelectTryResult<TSource, TResult>> enumerable, Func<TSource, Exception, TResult> exceptionHandler)
{
return enumerable.Select(x => x.CaughtException == null ? x.Result : exceptionHandler(x.Source, x.CaughtException));
}
public class SelectTryResult<TSource, TResult>
{
internal SelectTryResult(TSource source, TResult result, Exception exception)
{
Source = source;
Result = result;
CaughtException = exception;
}
public TSource Source { get; private set; }
public TResult Result { get; private set; }
public Exception CaughtException { get; private set; }
}
}
}

+ 0
- 20
src/Services/Identity/Identity.API/Factories/ApplicationDbContextFactory.cs View File

@ -1,20 +0,0 @@
namespace Identity.API.Factories
{
public class ApplicationDbContextFactory : IDesignTimeDbContextFactory<ApplicationDbContext>
{
public ApplicationDbContext CreateDbContext(string[] args)
{
var config = new ConfigurationBuilder()
.SetBasePath(Path.Combine(Directory.GetCurrentDirectory()))
.AddJsonFile("appsettings.json")
.AddEnvironmentVariables()
.Build();
var optionsBuilder = new DbContextOptionsBuilder<ApplicationDbContext>();
optionsBuilder.UseSqlServer(config["ConnectionString"], sqlServerOptionsAction: o => o.MigrationsAssembly("Identity.API"));
return new ApplicationDbContext(optionsBuilder.Options);
}
}
}

+ 0
- 21
src/Services/Identity/Identity.API/Factories/ConfigurationDbContextFactory.cs View File

@ -1,21 +0,0 @@
namespace Identity.API.Factories
{
public class ConfigurationDbContextFactory : IDesignTimeDbContextFactory<ConfigurationDbContext>
{
public ConfigurationDbContext CreateDbContext(string[] args)
{
var config = new ConfigurationBuilder()
.SetBasePath(Path.Combine(Directory.GetCurrentDirectory()))
.AddJsonFile("appsettings.json")
.AddEnvironmentVariables()
.Build();
var optionsBuilder = new DbContextOptionsBuilder<ConfigurationDbContext>();
var storeOptions = new ConfigurationStoreOptions();
optionsBuilder.UseSqlServer(config["ConnectionString"], sqlServerOptionsAction: o => o.MigrationsAssembly("Identity.API"));
return new ConfigurationDbContext(optionsBuilder.Options);
}
}
}

+ 0
- 21
src/Services/Identity/Identity.API/Factories/PersistedGrantDbContextFactory.cs View File

@ -1,21 +0,0 @@
namespace Identity.API.Factories
{
public class PersistedGrantDbContextFactory : IDesignTimeDbContextFactory<PersistedGrantDbContext>
{
public PersistedGrantDbContext CreateDbContext(string[] args)
{
var config = new ConfigurationBuilder()
.SetBasePath(Path.Combine(Directory.GetCurrentDirectory()))
.AddJsonFile("appsettings.json")
.AddEnvironmentVariables()
.Build();
var optionsBuilder = new DbContextOptionsBuilder<PersistedGrantDbContext>();
var operationOptions = new OperationalStoreOptions();
optionsBuilder.UseSqlServer(config["ConnectionString"], sqlServerOptionsAction: o => o.MigrationsAssembly("Identity.API"));
return new PersistedGrantDbContext(optionsBuilder.Options);
}
}
}

+ 10
- 10
src/Services/Identity/Identity.API/GlobalUsings.cs View File

@ -1,19 +1,18 @@
global using Microsoft.eShopOnContainers.Services.Identity.API.Extensions;
global using System.IO.Compression; global using System.IO.Compression;
global using Autofac.Extensions.DependencyInjection; global using Autofac.Extensions.DependencyInjection;
global using Autofac;
global using Azure.Core; global using Azure.Core;
global using Azure.Identity; global using Azure.Identity;
global using HealthChecks.UI.Client; global using HealthChecks.UI.Client;
global using IdentityModel; global using IdentityModel;
global using Duende.IdentityServer.EntityFramework.DbContexts;
global using Duende.IdentityServer.EntityFramework.Mappers;
global using Duende.IdentityServer.EntityFramework.Options;
global using Duende.IdentityServer;
global using Duende.IdentityServer.Configuration;
global using Duende.IdentityServer.Events;
global using Duende.IdentityServer.Extensions;
global using Duende.IdentityServer.Models; global using Duende.IdentityServer.Models;
global using Duende.IdentityServer.Services; global using Duende.IdentityServer.Services;
global using Duende.IdentityServer.Stores; global using Duende.IdentityServer.Stores;
global using Duende.IdentityServer.Validation; global using Duende.IdentityServer.Validation;
global using Duende.IdentityServer;
global using Microsoft.AspNetCore.Authentication; global using Microsoft.AspNetCore.Authentication;
global using Microsoft.AspNetCore.Authorization; global using Microsoft.AspNetCore.Authorization;
global using Microsoft.AspNetCore.Builder; global using Microsoft.AspNetCore.Builder;
@ -23,20 +22,21 @@ global using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
global using Microsoft.AspNetCore.Identity; global using Microsoft.AspNetCore.Identity;
global using Microsoft.AspNetCore.Mvc.Rendering; global using Microsoft.AspNetCore.Mvc.Rendering;
global using Microsoft.AspNetCore.Mvc; global using Microsoft.AspNetCore.Mvc;
global using Microsoft.AspNetCore.Mvc.Filters;
global using Microsoft.AspNetCore; global using Microsoft.AspNetCore;
global using Microsoft.EntityFrameworkCore.Design; global using Microsoft.EntityFrameworkCore.Design;
global using Microsoft.EntityFrameworkCore.Infrastructure; global using Microsoft.EntityFrameworkCore.Infrastructure;
global using Microsoft.EntityFrameworkCore.Metadata; global using Microsoft.EntityFrameworkCore.Metadata;
global using Microsoft.EntityFrameworkCore.Migrations; global using Microsoft.EntityFrameworkCore.Migrations;
global using Microsoft.EntityFrameworkCore; global using Microsoft.EntityFrameworkCore;
global using Microsoft.eShopOnContainers.Services.Identity.API.Certificates;
global using Microsoft.eShopOnContainers.Services.Identity.API.Configuration;
global using Microsoft.eShopOnContainers.Services.Identity.API;
global using Microsoft.eShopOnContainers.Services.Identity.API.Data; global using Microsoft.eShopOnContainers.Services.Identity.API.Data;
global using Microsoft.eShopOnContainers.Services.Identity.API.Configuration;
global using Microsoft.eShopOnContainers.Services.Identity.API.Devspaces; global using Microsoft.eShopOnContainers.Services.Identity.API.Devspaces;
global using Microsoft.eShopOnContainers.Services.Identity.API.Models.AccountViewModels; global using Microsoft.eShopOnContainers.Services.Identity.API.Models.AccountViewModels;
global using Microsoft.eShopOnContainers.Services.Identity.API.Models; global using Microsoft.eShopOnContainers.Services.Identity.API.Models;
global using Microsoft.eShopOnContainers.Services.Identity.API.Services; global using Microsoft.eShopOnContainers.Services.Identity.API.Services;
global using Microsoft.eShopOnContainers.Services.Identity.API;
global using Microsoft.Extensions.Configuration; global using Microsoft.Extensions.Configuration;
global using Microsoft.Extensions.DependencyInjection; global using Microsoft.Extensions.DependencyInjection;
global using Microsoft.Extensions.Diagnostics.HealthChecks; global using Microsoft.Extensions.Diagnostics.HealthChecks;
@ -44,7 +44,6 @@ global using Microsoft.Extensions.Hosting;
global using Microsoft.Extensions.Logging; global using Microsoft.Extensions.Logging;
global using Microsoft.Extensions.Options; global using Microsoft.Extensions.Options;
global using Polly; global using Polly;
global using Serilog;
global using StackExchange.Redis; global using StackExchange.Redis;
global using System.Collections.Generic; global using System.Collections.Generic;
global using System.ComponentModel.DataAnnotations; global using System.ComponentModel.DataAnnotations;
@ -58,6 +57,7 @@ global using System.Security.Cryptography.X509Certificates;
global using System.Text.RegularExpressions; global using System.Text.RegularExpressions;
global using System.Threading.Tasks; global using System.Threading.Tasks;
global using System; global using System;
global using Microsoft.AspNetCore.Http;


+ 21
- 17
src/Services/Identity/Identity.API/Identity.API.csproj View File

@ -8,12 +8,6 @@
<IsTransformWebConfigDisabled>true</IsTransformWebConfigDisabled> <IsTransformWebConfigDisabled>true</IsTransformWebConfigDisabled>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<Content Include="Setup\**\*;">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
</ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="AspNetCore.HealthChecks.SqlServer" Version="6.0.2" /> <PackageReference Include="AspNetCore.HealthChecks.SqlServer" Version="6.0.2" />
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="6.0.5" /> <PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="6.0.5" />
@ -31,11 +25,14 @@
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.HealthChecks" Version="2.2.0" /> <PackageReference Include="Microsoft.AspNetCore.Diagnostics.HealthChecks" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.HealthChecks" Version="1.0.0" /> <PackageReference Include="Microsoft.AspNetCore.HealthChecks" Version="1.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="7.0.0" /> <PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="7.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="7.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.0"> <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.0">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="7.0.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="7.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.AzureKeyVault" Version="3.1.24" /> <PackageReference Include="Microsoft.Extensions.Configuration.AzureKeyVault" Version="3.1.24" />
<PackageReference Include="Microsoft.Extensions.Logging.AzureAppServices" Version="7.0.0" /> <PackageReference Include="Microsoft.Extensions.Logging.AzureAppServices" Version="7.0.0" />
@ -55,18 +52,25 @@
<PackageReference Include="Azure.Identity" Version="1.9.0-beta.1" /> <PackageReference Include="Azure.Identity" Version="1.9.0-beta.1" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Certificate\idsrv3test.pfx" />
</ItemGroup>
<ItemGroup>
<None Update="Setup\*">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup> <ItemGroup>
<Folder Include="Extensions\" />
<None Include="Views\Account\AccessDenied.cshtml" />
<None Include="Views\Account\LoggedOut.cshtml" />
<None Include="Views\Account\Login.cshtml" />
<None Include="Views\Account\Logout.cshtml" />
<None Include="Views\Consent\Index.cshtml" />
<None Include="Views\Device\Success.cshtml" />
<None Include="Views\Device\UserCodeCapture.cshtml" />
<None Include="Views\Device\UserCodeConfirmation.cshtml" />
<None Include="Views\Diagnostics\Index.cshtml" />
<None Include="Views\Grants\Index.cshtml" />
<None Include="Views\Home\Index.cshtml" />
<None Include="Views\Shared\Error.cshtml" />
<None Include="Views\Shared\Redirect.cshtml" />
<None Include="Views\Shared\_Layout.cshtml" />
<None Include="Views\Shared\_ScopeListItem.cshtml" />
<None Include="Views\Shared\_ValidationSummary.cshtml" />
<None Include="Views\_ViewImports.cshtml" />
<None Include="Views\_ViewStart.cshtml" />
</ItemGroup> </ItemGroup>
<ProjectExtensions> <ProjectExtensions>
<VisualStudio> <VisualStudio>


+ 0
- 1096
src/Services/Identity/Identity.API/Migrations/ConfigurationDb/20220324152912_InitialConfigurationDbMigration.Designer.cs
File diff suppressed because it is too large
View File


+ 0
- 712
src/Services/Identity/Identity.API/Migrations/ConfigurationDb/20220324152912_InitialConfigurationDbMigration.cs View File

@ -1,712 +0,0 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Identity.API.Migrations.ConfigurationDb
{
public partial class Configuration : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "ApiResources",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Enabled = table.Column<bool>(type: "bit", nullable: false),
Name = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: false),
DisplayName = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: true),
Description = table.Column<string>(type: "nvarchar(1000)", maxLength: 1000, nullable: true),
AllowedAccessTokenSigningAlgorithms = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: true),
ShowInDiscoveryDocument = table.Column<bool>(type: "bit", nullable: false),
RequireResourceIndicator = table.Column<bool>(type: "bit", nullable: false),
Created = table.Column<DateTime>(type: "datetime2", nullable: false),
Updated = table.Column<DateTime>(type: "datetime2", nullable: true),
LastAccessed = table.Column<DateTime>(type: "datetime2", nullable: true),
NonEditable = table.Column<bool>(type: "bit", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ApiResources", x => x.Id);
});
migrationBuilder.CreateTable(
name: "ApiScopes",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Enabled = table.Column<bool>(type: "bit", nullable: false),
Name = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: false),
DisplayName = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: true),
Description = table.Column<string>(type: "nvarchar(1000)", maxLength: 1000, nullable: true),
Required = table.Column<bool>(type: "bit", nullable: false),
Emphasize = table.Column<bool>(type: "bit", nullable: false),
ShowInDiscoveryDocument = table.Column<bool>(type: "bit", nullable: false),
Created = table.Column<DateTime>(type: "datetime2", nullable: false),
Updated = table.Column<DateTime>(type: "datetime2", nullable: true),
LastAccessed = table.Column<DateTime>(type: "datetime2", nullable: true),
NonEditable = table.Column<bool>(type: "bit", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ApiScopes", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Clients",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Enabled = table.Column<bool>(type: "bit", nullable: false),
ClientId = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: false),
ProtocolType = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: false),
RequireClientSecret = table.Column<bool>(type: "bit", nullable: false),
ClientName = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: true),
Description = table.Column<string>(type: "nvarchar(1000)", maxLength: 1000, nullable: true),
ClientUri = table.Column<string>(type: "nvarchar(2000)", maxLength: 2000, nullable: true),
LogoUri = table.Column<string>(type: "nvarchar(2000)", maxLength: 2000, nullable: true),
RequireConsent = table.Column<bool>(type: "bit", nullable: false),
AllowRememberConsent = table.Column<bool>(type: "bit", nullable: false),
AlwaysIncludeUserClaimsInIdToken = table.Column<bool>(type: "bit", nullable: false),
RequirePkce = table.Column<bool>(type: "bit", nullable: false),
AllowPlainTextPkce = table.Column<bool>(type: "bit", nullable: false),
RequireRequestObject = table.Column<bool>(type: "bit", nullable: false),
AllowAccessTokensViaBrowser = table.Column<bool>(type: "bit", nullable: false),
FrontChannelLogoutUri = table.Column<string>(type: "nvarchar(2000)", maxLength: 2000, nullable: true),
FrontChannelLogoutSessionRequired = table.Column<bool>(type: "bit", nullable: false),
BackChannelLogoutUri = table.Column<string>(type: "nvarchar(2000)", maxLength: 2000, nullable: true),
BackChannelLogoutSessionRequired = table.Column<bool>(type: "bit", nullable: false),
AllowOfflineAccess = table.Column<bool>(type: "bit", nullable: false),
IdentityTokenLifetime = table.Column<int>(type: "int", nullable: false),
AllowedIdentityTokenSigningAlgorithms = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: true),
AccessTokenLifetime = table.Column<int>(type: "int", nullable: false),
AuthorizationCodeLifetime = table.Column<int>(type: "int", nullable: false),
ConsentLifetime = table.Column<int>(type: "int", nullable: true),
AbsoluteRefreshTokenLifetime = table.Column<int>(type: "int", nullable: false),
SlidingRefreshTokenLifetime = table.Column<int>(type: "int", nullable: false),
RefreshTokenUsage = table.Column<int>(type: "int", nullable: false),
UpdateAccessTokenClaimsOnRefresh = table.Column<bool>(type: "bit", nullable: false),
RefreshTokenExpiration = table.Column<int>(type: "int", nullable: false),
AccessTokenType = table.Column<int>(type: "int", nullable: false),
EnableLocalLogin = table.Column<bool>(type: "bit", nullable: false),
IncludeJwtId = table.Column<bool>(type: "bit", nullable: false),
AlwaysSendClientClaims = table.Column<bool>(type: "bit", nullable: false),
ClientClaimsPrefix = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: true),
PairWiseSubjectSalt = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: true),
UserSsoLifetime = table.Column<int>(type: "int", nullable: true),
UserCodeType = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: true),
DeviceCodeLifetime = table.Column<int>(type: "int", nullable: false),
CibaLifetime = table.Column<int>(type: "int", nullable: true),
PollingInterval = table.Column<int>(type: "int", nullable: true),
CoordinateLifetimeWithUserSession = table.Column<bool>(type: "bit", nullable: true),
Created = table.Column<DateTime>(type: "datetime2", nullable: false),
Updated = table.Column<DateTime>(type: "datetime2", nullable: true),
LastAccessed = table.Column<DateTime>(type: "datetime2", nullable: true),
NonEditable = table.Column<bool>(type: "bit", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Clients", x => x.Id);
});
migrationBuilder.CreateTable(
name: "IdentityProviders",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Scheme = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: false),
DisplayName = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: true),
Enabled = table.Column<bool>(type: "bit", nullable: false),
Type = table.Column<string>(type: "nvarchar(20)", maxLength: 20, nullable: false),
Properties = table.Column<string>(type: "nvarchar(max)", nullable: true),
Created = table.Column<DateTime>(type: "datetime2", nullable: false),
Updated = table.Column<DateTime>(type: "datetime2", nullable: true),
LastAccessed = table.Column<DateTime>(type: "datetime2", nullable: true),
NonEditable = table.Column<bool>(type: "bit", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_IdentityProviders", x => x.Id);
});
migrationBuilder.CreateTable(
name: "IdentityResources",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Enabled = table.Column<bool>(type: "bit", nullable: false),
Name = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: false),
DisplayName = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: true),
Description = table.Column<string>(type: "nvarchar(1000)", maxLength: 1000, nullable: true),
Required = table.Column<bool>(type: "bit", nullable: false),
Emphasize = table.Column<bool>(type: "bit", nullable: false),
ShowInDiscoveryDocument = table.Column<bool>(type: "bit", nullable: false),
Created = table.Column<DateTime>(type: "datetime2", nullable: false),
Updated = table.Column<DateTime>(type: "datetime2", nullable: true),
NonEditable = table.Column<bool>(type: "bit", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_IdentityResources", x => x.Id);
});
migrationBuilder.CreateTable(
name: "ApiResourceClaims",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
ApiResourceId = table.Column<int>(type: "int", nullable: false),
Type = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ApiResourceClaims", x => x.Id);
table.ForeignKey(
name: "FK_ApiResourceClaims_ApiResources_ApiResourceId",
column: x => x.ApiResourceId,
principalTable: "ApiResources",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "ApiResourceProperties",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
ApiResourceId = table.Column<int>(type: "int", nullable: false),
Key = table.Column<string>(type: "nvarchar(250)", maxLength: 250, nullable: false),
Value = table.Column<string>(type: "nvarchar(2000)", maxLength: 2000, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ApiResourceProperties", x => x.Id);
table.ForeignKey(
name: "FK_ApiResourceProperties_ApiResources_ApiResourceId",
column: x => x.ApiResourceId,
principalTable: "ApiResources",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "ApiResourceScopes",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Scope = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: false),
ApiResourceId = table.Column<int>(type: "int", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ApiResourceScopes", x => x.Id);
table.ForeignKey(
name: "FK_ApiResourceScopes_ApiResources_ApiResourceId",
column: x => x.ApiResourceId,
principalTable: "ApiResources",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "ApiResourceSecrets",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
ApiResourceId = table.Column<int>(type: "int", nullable: false),
Description = table.Column<string>(type: "nvarchar(1000)", maxLength: 1000, nullable: true),
Value = table.Column<string>(type: "nvarchar(4000)", maxLength: 4000, nullable: false),
Expiration = table.Column<DateTime>(type: "datetime2", nullable: true),
Type = table.Column<string>(type: "nvarchar(250)", maxLength: 250, nullable: false),
Created = table.Column<DateTime>(type: "datetime2", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ApiResourceSecrets", x => x.Id);
table.ForeignKey(
name: "FK_ApiResourceSecrets_ApiResources_ApiResourceId",
column: x => x.ApiResourceId,
principalTable: "ApiResources",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "ApiScopeClaims",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
ScopeId = table.Column<int>(type: "int", nullable: false),
Type = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ApiScopeClaims", x => x.Id);
table.ForeignKey(
name: "FK_ApiScopeClaims_ApiScopes_ScopeId",
column: x => x.ScopeId,
principalTable: "ApiScopes",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "ApiScopeProperties",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
ScopeId = table.Column<int>(type: "int", nullable: false),
Key = table.Column<string>(type: "nvarchar(250)", maxLength: 250, nullable: false),
Value = table.Column<string>(type: "nvarchar(2000)", maxLength: 2000, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ApiScopeProperties", x => x.Id);
table.ForeignKey(
name: "FK_ApiScopeProperties_ApiScopes_ScopeId",
column: x => x.ScopeId,
principalTable: "ApiScopes",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "ClientClaims",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Type = table.Column<string>(type: "nvarchar(250)", maxLength: 250, nullable: false),
Value = table.Column<string>(type: "nvarchar(250)", maxLength: 250, nullable: false),
ClientId = table.Column<int>(type: "int", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ClientClaims", x => x.Id);
table.ForeignKey(
name: "FK_ClientClaims_Clients_ClientId",
column: x => x.ClientId,
principalTable: "Clients",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "ClientCorsOrigins",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Origin = table.Column<string>(type: "nvarchar(150)", maxLength: 150, nullable: false),
ClientId = table.Column<int>(type: "int", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ClientCorsOrigins", x => x.Id);
table.ForeignKey(
name: "FK_ClientCorsOrigins_Clients_ClientId",
column: x => x.ClientId,
principalTable: "Clients",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "ClientGrantTypes",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
GrantType = table.Column<string>(type: "nvarchar(250)", maxLength: 250, nullable: false),
ClientId = table.Column<int>(type: "int", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ClientGrantTypes", x => x.Id);
table.ForeignKey(
name: "FK_ClientGrantTypes_Clients_ClientId",
column: x => x.ClientId,
principalTable: "Clients",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "ClientIdPRestrictions",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Provider = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: false),
ClientId = table.Column<int>(type: "int", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ClientIdPRestrictions", x => x.Id);
table.ForeignKey(
name: "FK_ClientIdPRestrictions_Clients_ClientId",
column: x => x.ClientId,
principalTable: "Clients",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "ClientPostLogoutRedirectUris",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
PostLogoutRedirectUri = table.Column<string>(type: "nvarchar(400)", maxLength: 400, nullable: false),
ClientId = table.Column<int>(type: "int", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ClientPostLogoutRedirectUris", x => x.Id);
table.ForeignKey(
name: "FK_ClientPostLogoutRedirectUris_Clients_ClientId",
column: x => x.ClientId,
principalTable: "Clients",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "ClientProperties",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
ClientId = table.Column<int>(type: "int", nullable: false),
Key = table.Column<string>(type: "nvarchar(250)", maxLength: 250, nullable: false),
Value = table.Column<string>(type: "nvarchar(2000)", maxLength: 2000, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ClientProperties", x => x.Id);
table.ForeignKey(
name: "FK_ClientProperties_Clients_ClientId",
column: x => x.ClientId,
principalTable: "Clients",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "ClientRedirectUris",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
RedirectUri = table.Column<string>(type: "nvarchar(400)", maxLength: 400, nullable: false),
ClientId = table.Column<int>(type: "int", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ClientRedirectUris", x => x.Id);
table.ForeignKey(
name: "FK_ClientRedirectUris_Clients_ClientId",
column: x => x.ClientId,
principalTable: "Clients",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "ClientScopes",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Scope = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: false),
ClientId = table.Column<int>(type: "int", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ClientScopes", x => x.Id);
table.ForeignKey(
name: "FK_ClientScopes_Clients_ClientId",
column: x => x.ClientId,
principalTable: "Clients",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "ClientSecrets",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
ClientId = table.Column<int>(type: "int", nullable: false),
Description = table.Column<string>(type: "nvarchar(2000)", maxLength: 2000, nullable: true),
Value = table.Column<string>(type: "nvarchar(4000)", maxLength: 4000, nullable: false),
Expiration = table.Column<DateTime>(type: "datetime2", nullable: true),
Type = table.Column<string>(type: "nvarchar(250)", maxLength: 250, nullable: false),
Created = table.Column<DateTime>(type: "datetime2", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ClientSecrets", x => x.Id);
table.ForeignKey(
name: "FK_ClientSecrets_Clients_ClientId",
column: x => x.ClientId,
principalTable: "Clients",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "IdentityResourceClaims",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
IdentityResourceId = table.Column<int>(type: "int", nullable: false),
Type = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_IdentityResourceClaims", x => x.Id);
table.ForeignKey(
name: "FK_IdentityResourceClaims_IdentityResources_IdentityResourceId",
column: x => x.IdentityResourceId,
principalTable: "IdentityResources",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "IdentityResourceProperties",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
IdentityResourceId = table.Column<int>(type: "int", nullable: false),
Key = table.Column<string>(type: "nvarchar(250)", maxLength: 250, nullable: false),
Value = table.Column<string>(type: "nvarchar(2000)", maxLength: 2000, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_IdentityResourceProperties", x => x.Id);
table.ForeignKey(
name: "FK_IdentityResourceProperties_IdentityResources_IdentityResourceId",
column: x => x.IdentityResourceId,
principalTable: "IdentityResources",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_ApiResourceClaims_ApiResourceId_Type",
table: "ApiResourceClaims",
columns: new[] { "ApiResourceId", "Type" },
unique: true);
migrationBuilder.CreateIndex(
name: "IX_ApiResourceProperties_ApiResourceId_Key",
table: "ApiResourceProperties",
columns: new[] { "ApiResourceId", "Key" },
unique: true);
migrationBuilder.CreateIndex(
name: "IX_ApiResources_Name",
table: "ApiResources",
column: "Name",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_ApiResourceScopes_ApiResourceId_Scope",
table: "ApiResourceScopes",
columns: new[] { "ApiResourceId", "Scope" },
unique: true);
migrationBuilder.CreateIndex(
name: "IX_ApiResourceSecrets_ApiResourceId",
table: "ApiResourceSecrets",
column: "ApiResourceId");
migrationBuilder.CreateIndex(
name: "IX_ApiScopeClaims_ScopeId_Type",
table: "ApiScopeClaims",
columns: new[] { "ScopeId", "Type" },
unique: true);
migrationBuilder.CreateIndex(
name: "IX_ApiScopeProperties_ScopeId_Key",
table: "ApiScopeProperties",
columns: new[] { "ScopeId", "Key" },
unique: true);
migrationBuilder.CreateIndex(
name: "IX_ApiScopes_Name",
table: "ApiScopes",
column: "Name",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_ClientClaims_ClientId_Type_Value",
table: "ClientClaims",
columns: new[] { "ClientId", "Type", "Value" },
unique: true);
migrationBuilder.CreateIndex(
name: "IX_ClientCorsOrigins_ClientId_Origin",
table: "ClientCorsOrigins",
columns: new[] { "ClientId", "Origin" },
unique: true);
migrationBuilder.CreateIndex(
name: "IX_ClientGrantTypes_ClientId_GrantType",
table: "ClientGrantTypes",
columns: new[] { "ClientId", "GrantType" },
unique: true);
migrationBuilder.CreateIndex(
name: "IX_ClientIdPRestrictions_ClientId_Provider",
table: "ClientIdPRestrictions",
columns: new[] { "ClientId", "Provider" },
unique: true);
migrationBuilder.CreateIndex(
name: "IX_ClientPostLogoutRedirectUris_ClientId_PostLogoutRedirectUri",
table: "ClientPostLogoutRedirectUris",
columns: new[] { "ClientId", "PostLogoutRedirectUri" },
unique: true);
migrationBuilder.CreateIndex(
name: "IX_ClientProperties_ClientId_Key",
table: "ClientProperties",
columns: new[] { "ClientId", "Key" },
unique: true);
migrationBuilder.CreateIndex(
name: "IX_ClientRedirectUris_ClientId_RedirectUri",
table: "ClientRedirectUris",
columns: new[] { "ClientId", "RedirectUri" },
unique: true);
migrationBuilder.CreateIndex(
name: "IX_Clients_ClientId",
table: "Clients",
column: "ClientId",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_ClientScopes_ClientId_Scope",
table: "ClientScopes",
columns: new[] { "ClientId", "Scope" },
unique: true);
migrationBuilder.CreateIndex(
name: "IX_ClientSecrets_ClientId",
table: "ClientSecrets",
column: "ClientId");
migrationBuilder.CreateIndex(
name: "IX_IdentityProviders_Scheme",
table: "IdentityProviders",
column: "Scheme",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_IdentityResourceClaims_IdentityResourceId_Type",
table: "IdentityResourceClaims",
columns: new[] { "IdentityResourceId", "Type" },
unique: true);
migrationBuilder.CreateIndex(
name: "IX_IdentityResourceProperties_IdentityResourceId_Key",
table: "IdentityResourceProperties",
columns: new[] { "IdentityResourceId", "Key" },
unique: true);
migrationBuilder.CreateIndex(
name: "IX_IdentityResources_Name",
table: "IdentityResources",
column: "Name",
unique: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "ApiResourceClaims");
migrationBuilder.DropTable(
name: "ApiResourceProperties");
migrationBuilder.DropTable(
name: "ApiResourceScopes");
migrationBuilder.DropTable(
name: "ApiResourceSecrets");
migrationBuilder.DropTable(
name: "ApiScopeClaims");
migrationBuilder.DropTable(
name: "ApiScopeProperties");
migrationBuilder.DropTable(
name: "ClientClaims");
migrationBuilder.DropTable(
name: "ClientCorsOrigins");
migrationBuilder.DropTable(
name: "ClientGrantTypes");
migrationBuilder.DropTable(
name: "ClientIdPRestrictions");
migrationBuilder.DropTable(
name: "ClientPostLogoutRedirectUris");
migrationBuilder.DropTable(
name: "ClientProperties");
migrationBuilder.DropTable(
name: "ClientRedirectUris");
migrationBuilder.DropTable(
name: "ClientScopes");
migrationBuilder.DropTable(
name: "ClientSecrets");
migrationBuilder.DropTable(
name: "IdentityProviders");
migrationBuilder.DropTable(
name: "IdentityResourceClaims");
migrationBuilder.DropTable(
name: "IdentityResourceProperties");
migrationBuilder.DropTable(
name: "ApiResources");
migrationBuilder.DropTable(
name: "ApiScopes");
migrationBuilder.DropTable(
name: "Clients");
migrationBuilder.DropTable(
name: "IdentityResources");
}
}
}

+ 0
- 1094
src/Services/Identity/Identity.API/Migrations/ConfigurationDb/ConfigurationDbContextModelSnapshot.cs
File diff suppressed because it is too large
View File


+ 0
- 240
src/Services/Identity/Identity.API/Migrations/PersistedGrantDb/20220324152905_InitialPersistedGrantDbMigration.Designer.cs View File

@ -1,240 +0,0 @@
// <auto-generated />
using System;
using Duende.IdentityServer.EntityFramework.DbContexts;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace Identity.API.Migrations.PersistedGrantDb
{
[DbContext(typeof(PersistedGrantDbContext))]
[Migration("20220324152905_Grants")]
partial class Grants
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "6.0.0")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 1);
modelBuilder.Entity("Duende.IdentityServer.EntityFramework.Entities.DeviceFlowCodes", b =>
{
b.Property<string>("UserCode")
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<string>("ClientId")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2");
b.Property<string>("Data")
.IsRequired()
.HasMaxLength(50000)
.HasColumnType("nvarchar(max)");
b.Property<string>("Description")
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<string>("DeviceCode")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<DateTime?>("Expiration")
.IsRequired()
.HasColumnType("datetime2");
b.Property<string>("SessionId")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<string>("SubjectId")
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.HasKey("UserCode");
b.HasIndex("DeviceCode")
.IsUnique();
b.HasIndex("Expiration");
b.ToTable("DeviceCodes", (string)null);
});
modelBuilder.Entity("Duende.IdentityServer.EntityFramework.Entities.Key", b =>
{
b.Property<string>("Id")
.HasColumnType("nvarchar(450)");
b.Property<string>("Algorithm")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<DateTime>("Created")
.HasColumnType("datetime2");
b.Property<string>("Data")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<bool>("DataProtected")
.HasColumnType("bit");
b.Property<bool>("IsX509Certificate")
.HasColumnType("bit");
b.Property<string>("Use")
.HasColumnType("nvarchar(450)");
b.Property<int>("Version")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("Use");
b.ToTable("Keys", (string)null);
});
modelBuilder.Entity("Duende.IdentityServer.EntityFramework.Entities.PersistedGrant", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"), 1L, 1);
b.Property<string>("ClientId")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<DateTime?>("ConsumedTime")
.HasColumnType("datetime2");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2");
b.Property<string>("Data")
.IsRequired()
.HasMaxLength(50000)
.HasColumnType("nvarchar(max)");
b.Property<string>("Description")
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<DateTime?>("Expiration")
.HasColumnType("datetime2");
b.Property<string>("Key")
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<string>("SessionId")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<string>("SubjectId")
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<string>("Type")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("nvarchar(50)");
b.HasKey("Id");
b.HasIndex("ConsumedTime");
b.HasIndex("Expiration");
b.HasIndex("Key")
.IsUnique()
.HasFilter("[Key] IS NOT NULL");
b.HasIndex("SubjectId", "ClientId", "Type");
b.HasIndex("SubjectId", "SessionId", "Type");
b.ToTable("PersistedGrants", (string)null);
});
modelBuilder.Entity("Duende.IdentityServer.EntityFramework.Entities.ServerSideSession", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"), 1L, 1);
b.Property<DateTime>("Created")
.HasColumnType("datetime2");
b.Property<string>("Data")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("DisplayName")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<DateTime?>("Expires")
.HasColumnType("datetime2");
b.Property<string>("Key")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<DateTime>("Renewed")
.HasColumnType("datetime2");
b.Property<string>("Scheme")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<string>("SessionId")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<string>("SubjectId")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.HasKey("Id");
b.HasIndex("DisplayName");
b.HasIndex("Expires");
b.HasIndex("Key")
.IsUnique();
b.HasIndex("SessionId");
b.HasIndex("SubjectId");
b.ToTable("ServerSideSessions", (string)null);
});
#pragma warning restore 612, 618
}
}
}

+ 0
- 177
src/Services/Identity/Identity.API/Migrations/PersistedGrantDb/20220324152905_InitialPersistedGrantDbMigration.cs View File

@ -1,177 +0,0 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Identity.API.Migrations.PersistedGrantDb
{
public partial class Grants : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "DeviceCodes",
columns: table => new
{
UserCode = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: false),
DeviceCode = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: false),
SubjectId = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: true),
SessionId = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: true),
ClientId = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: false),
Description = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: true),
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
Expiration = table.Column<DateTime>(type: "datetime2", nullable: false),
Data = table.Column<string>(type: "nvarchar(max)", maxLength: 50000, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_DeviceCodes", x => x.UserCode);
});
migrationBuilder.CreateTable(
name: "Keys",
columns: table => new
{
Id = table.Column<string>(type: "nvarchar(450)", nullable: false),
Version = table.Column<int>(type: "int", nullable: false),
Created = table.Column<DateTime>(type: "datetime2", nullable: false),
Use = table.Column<string>(type: "nvarchar(450)", nullable: true),
Algorithm = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
IsX509Certificate = table.Column<bool>(type: "bit", nullable: false),
DataProtected = table.Column<bool>(type: "bit", nullable: false),
Data = table.Column<string>(type: "nvarchar(max)", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Keys", x => x.Id);
});
migrationBuilder.CreateTable(
name: "PersistedGrants",
columns: table => new
{
Id = table.Column<long>(type: "bigint", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Key = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: true),
Type = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: false),
SubjectId = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: true),
SessionId = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: true),
ClientId = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: false),
Description = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: true),
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
Expiration = table.Column<DateTime>(type: "datetime2", nullable: true),
ConsumedTime = table.Column<DateTime>(type: "datetime2", nullable: true),
Data = table.Column<string>(type: "nvarchar(max)", maxLength: 50000, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_PersistedGrants", x => x.Id);
});
migrationBuilder.CreateTable(
name: "ServerSideSessions",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Key = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
Scheme = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
SubjectId = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
SessionId = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: true),
DisplayName = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: true),
Created = table.Column<DateTime>(type: "datetime2", nullable: false),
Renewed = table.Column<DateTime>(type: "datetime2", nullable: false),
Expires = table.Column<DateTime>(type: "datetime2", nullable: true),
Data = table.Column<string>(type: "nvarchar(max)", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ServerSideSessions", x => x.Id);
});
migrationBuilder.CreateIndex(
name: "IX_DeviceCodes_DeviceCode",
table: "DeviceCodes",
column: "DeviceCode",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_DeviceCodes_Expiration",
table: "DeviceCodes",
column: "Expiration");
migrationBuilder.CreateIndex(
name: "IX_Keys_Use",
table: "Keys",
column: "Use");
migrationBuilder.CreateIndex(
name: "IX_PersistedGrants_ConsumedTime",
table: "PersistedGrants",
column: "ConsumedTime");
migrationBuilder.CreateIndex(
name: "IX_PersistedGrants_Expiration",
table: "PersistedGrants",
column: "Expiration");
migrationBuilder.CreateIndex(
name: "IX_PersistedGrants_Key",
table: "PersistedGrants",
column: "Key",
unique: true,
filter: "[Key] IS NOT NULL");
migrationBuilder.CreateIndex(
name: "IX_PersistedGrants_SubjectId_ClientId_Type",
table: "PersistedGrants",
columns: new[] { "SubjectId", "ClientId", "Type" });
migrationBuilder.CreateIndex(
name: "IX_PersistedGrants_SubjectId_SessionId_Type",
table: "PersistedGrants",
columns: new[] { "SubjectId", "SessionId", "Type" });
migrationBuilder.CreateIndex(
name: "IX_ServerSideSessions_DisplayName",
table: "ServerSideSessions",
column: "DisplayName");
migrationBuilder.CreateIndex(
name: "IX_ServerSideSessions_Expires",
table: "ServerSideSessions",
column: "Expires");
migrationBuilder.CreateIndex(
name: "IX_ServerSideSessions_Key",
table: "ServerSideSessions",
column: "Key",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_ServerSideSessions_SessionId",
table: "ServerSideSessions",
column: "SessionId");
migrationBuilder.CreateIndex(
name: "IX_ServerSideSessions_SubjectId",
table: "ServerSideSessions",
column: "SubjectId");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "DeviceCodes");
migrationBuilder.DropTable(
name: "Keys");
migrationBuilder.DropTable(
name: "PersistedGrants");
migrationBuilder.DropTable(
name: "ServerSideSessions");
}
}
}

+ 0
- 238
src/Services/Identity/Identity.API/Migrations/PersistedGrantDb/PersistedGrantDbContextModelSnapshot.cs View File

@ -1,238 +0,0 @@
// <auto-generated />
using System;
using Duende.IdentityServer.EntityFramework.DbContexts;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace Identity.API.Migrations.PersistedGrantDb
{
[DbContext(typeof(PersistedGrantDbContext))]
partial class PersistedGrantDbContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "6.2.0")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 1);
modelBuilder.Entity("Duende.IdentityServer.EntityFramework.Entities.DeviceFlowCodes", b =>
{
b.Property<string>("UserCode")
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<string>("ClientId")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2");
b.Property<string>("Data")
.IsRequired()
.HasMaxLength(50000)
.HasColumnType("nvarchar(max)");
b.Property<string>("Description")
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<string>("DeviceCode")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<DateTime?>("Expiration")
.IsRequired()
.HasColumnType("datetime2");
b.Property<string>("SessionId")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<string>("SubjectId")
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.HasKey("UserCode");
b.HasIndex("DeviceCode")
.IsUnique();
b.HasIndex("Expiration");
b.ToTable("DeviceCodes", (string)null);
});
modelBuilder.Entity("Duende.IdentityServer.EntityFramework.Entities.Key", b =>
{
b.Property<string>("Id")
.HasColumnType("nvarchar(450)");
b.Property<string>("Algorithm")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<DateTime>("Created")
.HasColumnType("datetime2");
b.Property<string>("Data")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<bool>("DataProtected")
.HasColumnType("bit");
b.Property<bool>("IsX509Certificate")
.HasColumnType("bit");
b.Property<string>("Use")
.HasColumnType("nvarchar(450)");
b.Property<int>("Version")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("Use");
b.ToTable("Keys", (string)null);
});
modelBuilder.Entity("Duende.IdentityServer.EntityFramework.Entities.PersistedGrant", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"), 1L, 1);
b.Property<string>("ClientId")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<DateTime?>("ConsumedTime")
.HasColumnType("datetime2");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2");
b.Property<string>("Data")
.IsRequired()
.HasMaxLength(50000)
.HasColumnType("nvarchar(max)");
b.Property<string>("Description")
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<DateTime?>("Expiration")
.HasColumnType("datetime2");
b.Property<string>("Key")
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<string>("SessionId")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<string>("SubjectId")
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<string>("Type")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("nvarchar(50)");
b.HasKey("Id");
b.HasIndex("ConsumedTime");
b.HasIndex("Expiration");
b.HasIndex("Key")
.IsUnique()
.HasFilter("[Key] IS NOT NULL");
b.HasIndex("SubjectId", "ClientId", "Type");
b.HasIndex("SubjectId", "SessionId", "Type");
b.ToTable("PersistedGrants", (string)null);
});
modelBuilder.Entity("Duende.IdentityServer.EntityFramework.Entities.ServerSideSession", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"), 1L, 1);
b.Property<DateTime>("Created")
.HasColumnType("datetime2");
b.Property<string>("Data")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("DisplayName")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<DateTime?>("Expires")
.HasColumnType("datetime2");
b.Property<string>("Key")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<DateTime>("Renewed")
.HasColumnType("datetime2");
b.Property<string>("Scheme")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<string>("SessionId")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<string>("SubjectId")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.HasKey("Id");
b.HasIndex("DisplayName");
b.HasIndex("Expires");
b.HasIndex("Key")
.IsUnique();
b.HasIndex("SessionId");
b.HasIndex("SubjectId");
b.ToTable("ServerSideSessions", (string)null);
});
#pragma warning restore 612, 618
}
}
}

+ 58
- 54
src/Services/Identity/Identity.API/Program.cs View File

@ -1,71 +1,75 @@
string Namespace = typeof(Startup).Namespace;
string AppName = Namespace.Substring(Namespace.LastIndexOf('.', Namespace.LastIndexOf('.') - 1) + 1);
var appName = "Identity.API";
var builder = WebApplication.CreateBuilder();
var configuration = GetConfiguration();
builder.AddCustomConfiguration();
builder.AddCustomSerilog();
builder.AddCustomMvc();
builder.AddCustomDatabase();
builder.AddCustomIdentity();
builder.AddCustomIdentityServer();
builder.AddCustomAuthentication();
builder.AddCustomHealthChecks();
builder.AddCustomApplicationServices();
Log.Logger = CreateSerilogLogger(configuration);
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
var pathBase = builder.Configuration["PATH_BASE"];
if (!string.IsNullOrEmpty(pathBase))
{
app.UsePathBase(pathBase);
}
app.UseStaticFiles();
// This cookie policy fixes login issues with Chrome 80+ using HHTP
app.UseCookiePolicy(new CookiePolicyOptions { MinimumSameSitePolicy = SameSiteMode.Lax });
app.UseRouting();
app.UseIdentityServer();
app.UseAuthorization();
app.MapDefaultControllerRoute();
app.MapHealthChecks("/hc", new HealthCheckOptions()
{
Predicate = _ => true,
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});
app.MapHealthChecks("/liveness", new HealthCheckOptions
{
Predicate = r => r.Name.Contains("self")
});
try try
{ {
Log.Information("Configuring web host ({ApplicationContext})...", AppName);
var host = BuildWebHost(configuration, args);
Log.Information("Applying migrations ({ApplicationContext})...", AppName);
host.MigrateDbContext<PersistedGrantDbContext>((_, __) => { })
.MigrateDbContext<ApplicationDbContext>((context, services) =>
{
var env = services.GetService<IWebHostEnvironment>();
var logger = services.GetService<ILogger<ApplicationDbContextSeed>>();
var settings = services.GetService<IOptions<AppSettings>>();
new ApplicationDbContextSeed()
.SeedAsync(context, env, logger, settings)
.Wait();
})
.MigrateDbContext<ConfigurationDbContext>((context, services) =>
{
new ConfigurationDbContextSeed()
.SeedAsync(context, configuration)
.Wait();
});
Log.Information("Starting web host ({ApplicationContext})...", AppName);
host.Run();
app.Logger.LogInformation("Seeding database ({ApplicationName})...", appName);
// Apply database migration automatically. Note that this approach is not
// recommended for production scenarios. Consider generating SQL scripts from
// migrations instead.
using (var scope = app.Services.CreateScope())
{
await SeedData.EnsureSeedData(scope, app.Configuration, app.Logger);
}
app.Logger.LogInformation("Starting web host ({ApplicationName})...", appName);
app.Run();
return 0; return 0;
} }
catch (Exception ex) catch (Exception ex)
{ {
Log.Fatal(ex, "Program terminated unexpectedly ({ApplicationContext})!", AppName);
app.Logger.LogCritical(ex, "Host terminated unexpectedly ({ApplicationName})...", appName);
return 1; return 1;
} }
finally finally
{ {
Log.CloseAndFlush();
}
IWebHost BuildWebHost(IConfiguration configuration, string[] args) =>
WebHost.CreateDefaultBuilder(args)
.CaptureStartupErrors(false)
.ConfigureAppConfiguration(x => x.AddConfiguration(configuration))
.UseStartup<Startup>()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseSerilog()
.Build();
Serilog.ILogger CreateSerilogLogger(IConfiguration configuration)
{
var seqServerUrl = configuration["Serilog:SeqServerUrl"];
var logstashUrl = configuration["Serilog:LogstashgUrl"];
return new LoggerConfiguration()
.MinimumLevel.Verbose()
.Enrich.WithProperty("ApplicationContext", AppName)
.Enrich.FromLogContext()
.WriteTo.Console()
.WriteTo.Seq(string.IsNullOrWhiteSpace(seqServerUrl) ? "http://seq" : seqServerUrl)
.WriteTo.Http(string.IsNullOrWhiteSpace(logstashUrl) ? "http://localhost:8080" : logstashUrl,null)
.ReadFrom.Configuration(configuration)
.CreateLogger();
Serilog.Log.CloseAndFlush();
} }
IConfiguration GetConfiguration() IConfiguration GetConfiguration()


+ 117
- 0
src/Services/Identity/Identity.API/ProgramExtensions.cs View File

@ -0,0 +1,117 @@
using Serilog;
namespace Microsoft.eShopOnContainers.Services.Identity.API;
public static class ProgramExtensions
{
private const string AppName = "Identity API";
public static void AddCustomConfiguration(this WebApplicationBuilder builder)
{
builder.Configuration.AddConfiguration(GetConfiguration()).Build();
}
public static void AddCustomSerilog(this WebApplicationBuilder builder)
{
var seqServerUrl = builder.Configuration["SeqServerUrl"];
var logstashUrl = builder.Configuration["LogstashgUrl"];
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Verbose()
.Enrich.WithProperty("ApplicationContext", AppName)
.Enrich.FromLogContext()
.WriteTo.Console()
.WriteTo.Seq(string.IsNullOrWhiteSpace(seqServerUrl) ? "http://seq" : seqServerUrl)
.WriteTo.Http(string.IsNullOrWhiteSpace(logstashUrl) ? "http://localhost:8080" : logstashUrl, null)
.ReadFrom.Configuration(builder.Configuration)
.CreateLogger();
builder.Host.UseSerilog();
}
public static void AddCustomMvc(this WebApplicationBuilder builder)
{
builder.Services.AddControllersWithViews();
builder.Services.AddControllers();
builder.Services.AddRazorPages();
}
public static void AddCustomDatabase(this WebApplicationBuilder builder) =>
builder.Services.AddDbContext<ApplicationDbContext>(
options => options.UseSqlServer(builder.Configuration["ConnectionString"]));
public static void AddCustomIdentity(this WebApplicationBuilder builder)
{
builder.Services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
}
public static void AddCustomIdentityServer(this WebApplicationBuilder builder)
{
var identityServerBuilder = builder.Services.AddIdentityServer(options =>
{
options.IssuerUri = builder.Configuration["IssuerUrl"];
options.Authentication.CookieLifetime = TimeSpan.FromHours(2);
options.Events.RaiseErrorEvents = true;
options.Events.RaiseInformationEvents = true;
options.Events.RaiseFailureEvents = true;
options.Events.RaiseSuccessEvents = true;
})
.AddInMemoryIdentityResources(Config.GetResources())
.AddInMemoryApiScopes(Config.GetApiScopes())
.AddInMemoryApiResources(Config.GetApis())
.AddInMemoryClients(Config.GetClients(builder.Configuration))
.AddAspNetIdentity<ApplicationUser>();
// not recommended for production - you need to store your key material somewhere secure
identityServerBuilder.AddDeveloperSigningCredential();
}
public static void AddCustomAuthentication(this WebApplicationBuilder builder)
{
builder.Services.AddAuthentication();
}
public static void AddCustomHealthChecks(this WebApplicationBuilder builder)
{
builder.Services.AddHealthChecks()
.AddCheck("self", () => HealthCheckResult.Healthy())
.AddSqlServer(builder.Configuration["ConnectionString"],
name: "IdentityDB-check",
tags: new string[] { "IdentityDB" });
}
public static void AddCustomApplicationServices(this WebApplicationBuilder builder)
{
builder.Services.AddTransient<IProfileService, ProfileService>();
builder.Services.AddTransient<ILoginService<ApplicationUser>, EFLoginService>();
builder.Services.AddTransient<IRedirectService, RedirectService>();
}
static IConfiguration GetConfiguration()
{
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddEnvironmentVariables();
var config = builder.Build();
if (config.GetValue<bool>("UseVault", false))
{
TokenCredential credential = new ClientSecretCredential(
config["Vault:TenantId"],
config["Vault:ClientId"],
config["Vault:ClientSecret"]);
builder.AddAzureKeyVault(new Uri($"https://{config["Vault:Name"]}.vault.azure.net/"), credential);
}
return builder.Build();
}
}

+ 341
- 0
src/Services/Identity/Identity.API/Quickstart/Account/AccountController.cs View File

@ -0,0 +1,341 @@
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
using Duende.IdentityServer.Events;
using Duende.IdentityServer.Extensions;
using Duende.IdentityServer.Stores;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace IdentityServerHost.Quickstart.UI
{
[SecurityHeaders]
[AllowAnonymous]
public class AccountController : Controller
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly IIdentityServerInteractionService _interaction;
private readonly IClientStore _clientStore;
private readonly IAuthenticationSchemeProvider _schemeProvider;
private readonly IAuthenticationHandlerProvider _handlerProvider;
private readonly IEventService _events;
public AccountController(
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager,
IIdentityServerInteractionService interaction,
IClientStore clientStore,
IAuthenticationSchemeProvider schemeProvider,
IAuthenticationHandlerProvider handlerProvider,
IEventService events)
{
_userManager = userManager;
_signInManager = signInManager;
_interaction = interaction;
_clientStore = clientStore;
_schemeProvider = schemeProvider;
_handlerProvider = handlerProvider;
_events = events;
}
/// <summary>
/// Entry point into the login workflow
/// </summary>
[HttpGet]
public async Task<IActionResult> Login(string returnUrl)
{
// build a model so we know what to show on the login page
var vm = await BuildLoginViewModelAsync(returnUrl);
ViewData["ReturnUrl"] = returnUrl;
if (vm.IsExternalLoginOnly)
{
// we only have one option for logging in and it's an external provider
return RedirectToAction("Challenge", "External", new { scheme = vm.ExternalLoginScheme, returnUrl });
}
return View(vm);
}
/// <summary>
/// Handle postback from username/password login
/// </summary>
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginInputModel model, string button)
{
// check if we are in the context of an authorization request
var context = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl);
// the user clicked the "cancel" button
if (button != "login")
{
if (context != null)
{
// if the user cancels, send a result back into IdentityServer as if they
// denied the consent (even if this client does not require consent).
// this will send back an access denied OIDC error response to the client.
await _interaction.DenyAuthorizationAsync(context, AuthorizationError.AccessDenied);
// we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null
if (context.IsNativeClient())
{
// The client is native, so this change in how to
// return the response is for better UX for the end user.
return this.LoadingPage("Redirect", model.ReturnUrl);
}
return Redirect(model.ReturnUrl);
}
else
{
// since we don't have a valid context, then we just go back to the home page
return Redirect("~/");
}
}
if (ModelState.IsValid)
{
var result = await _signInManager.PasswordSignInAsync(model.Username, model.Password, model.RememberLogin, lockoutOnFailure: true);
if (result.Succeeded)
{
var user = await _userManager.FindByNameAsync(model.Username);
await _events.RaiseAsync(new UserLoginSuccessEvent(user.UserName, user.Id, user.UserName, clientId: context?.Client.ClientId));
if (context != null)
{
if (context.IsNativeClient())
{
// The client is native, so this change in how to
// return the response is for better UX for the end user.
return this.LoadingPage("Redirect", model.ReturnUrl);
}
// we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null
return Redirect(model.ReturnUrl);
}
// request for a local page
if (Url.IsLocalUrl(model.ReturnUrl))
{
return Redirect(model.ReturnUrl);
}
else if (string.IsNullOrEmpty(model.ReturnUrl))
{
return Redirect("~/");
}
else
{
// user might have clicked on a malicious link - should be logged
throw new Exception("invalid return URL");
}
}
await _events.RaiseAsync(new UserLoginFailureEvent(model.Username, "invalid credentials", clientId:context?.Client.ClientId));
ModelState.AddModelError(string.Empty, AccountOptions.InvalidCredentialsErrorMessage);
}
// something went wrong, show form with error
var vm = await BuildLoginViewModelAsync(model);
ViewData["ReturnUrl"] = model.ReturnUrl;
return View(vm);
}
/// <summary>
/// Show logout page
/// </summary>
[HttpGet]
public async Task<IActionResult> Logout(string logoutId)
{
// build a model so the logout page knows what to display
var vm = await BuildLogoutViewModelAsync(logoutId);
if (vm.ShowLogoutPrompt == false)
{
// if the request for logout was properly authenticated from IdentityServer, then
// we don't need to show the prompt and can just log the user out directly.
return await Logout(vm);
}
return View(vm);
}
/// <summary>
/// Handle logout page postback
/// </summary>
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Logout(LogoutInputModel model)
{
// build a model so the logged out page knows what to display
var vm = await BuildLoggedOutViewModelAsync(model.LogoutId);
if (User?.Identity.IsAuthenticated == true)
{
// delete local authentication cookie
await _signInManager.SignOutAsync();
// raise the logout event
await _events.RaiseAsync(new UserLogoutSuccessEvent(User.GetSubjectId(), User.GetDisplayName()));
}
// check if we need to trigger sign-out at an upstream identity provider
if (vm.TriggerExternalSignout)
{
// build a return URL so the upstream provider will redirect back
// to us after the user has logged out. this allows us to then
// complete our single sign-out processing.
string url = Url.Action("Logout", new { logoutId = vm.LogoutId });
// this triggers a redirect to the external provider for sign-out
return SignOut(new AuthenticationProperties { RedirectUri = url }, vm.ExternalAuthenticationScheme);
}
return View("LoggedOut", vm);
}
[HttpGet]
public IActionResult AccessDenied()
{
return View();
}
/*****************************************/
/* helper APIs for the AccountController */
/*****************************************/
private async Task<LoginViewModel> BuildLoginViewModelAsync(string returnUrl)
{
var context = await _interaction.GetAuthorizationContextAsync(returnUrl);
if (context?.IdP != null && await _schemeProvider.GetSchemeAsync(context.IdP) != null)
{
var local = context.IdP == IdentityServerConstants.LocalIdentityProvider;
// this is meant to short circuit the UI and only trigger the one external IdP
var vm = new LoginViewModel
{
EnableLocalLogin = local,
ReturnUrl = returnUrl,
Username = context?.LoginHint,
};
if (!local)
{
vm.ExternalProviders = new[] { new ExternalProvider { AuthenticationScheme = context.IdP } };
}
return vm;
}
var schemes = await _schemeProvider.GetAllSchemesAsync();
var providers = schemes
.Where(x => x.DisplayName != null)
.Select(x => new ExternalProvider
{
DisplayName = x.DisplayName ?? x.Name,
AuthenticationScheme = x.Name
}).ToList();
var allowLocal = true;
if (context?.Client.ClientId != null)
{
var client = await _clientStore.FindEnabledClientByIdAsync(context.Client.ClientId);
if (client != null)
{
allowLocal = client.EnableLocalLogin;
if (client.IdentityProviderRestrictions != null && client.IdentityProviderRestrictions.Any())
{
providers = providers.Where(provider => client.IdentityProviderRestrictions.Contains(provider.AuthenticationScheme)).ToList();
}
}
}
return new LoginViewModel
{
AllowRememberLogin = AccountOptions.AllowRememberLogin,
EnableLocalLogin = allowLocal && AccountOptions.AllowLocalLogin,
ReturnUrl = returnUrl,
Username = context?.LoginHint,
ExternalProviders = providers.ToArray()
};
}
private async Task<LoginViewModel> BuildLoginViewModelAsync(LoginInputModel model)
{
var vm = await BuildLoginViewModelAsync(model.ReturnUrl);
vm.Username = model.Username;
vm.RememberLogin = model.RememberLogin;
return vm;
}
private async Task<LogoutViewModel> BuildLogoutViewModelAsync(string logoutId)
{
var vm = new LogoutViewModel { LogoutId = logoutId, ShowLogoutPrompt = AccountOptions.ShowLogoutPrompt };
if (User?.Identity.IsAuthenticated != true)
{
// if the user is not authenticated, then just show logged out page
vm.ShowLogoutPrompt = false;
return vm;
}
var context = await _interaction.GetLogoutContextAsync(logoutId);
if (context?.ShowSignoutPrompt == false)
{
// it's safe to automatically sign-out
vm.ShowLogoutPrompt = false;
return vm;
}
// show the logout prompt. this prevents attacks where the user
// is automatically signed out by another malicious web page.
return vm;
}
private async Task<LoggedOutViewModel> BuildLoggedOutViewModelAsync(string logoutId)
{
// get context information (client name, post logout redirect URI and iframe for federated signout)
var logout = await _interaction.GetLogoutContextAsync(logoutId);
var vm = new LoggedOutViewModel
{
AutomaticRedirectAfterSignOut = AccountOptions.AutomaticRedirectAfterSignOut,
PostLogoutRedirectUri = logout?.PostLogoutRedirectUri,
ClientName = string.IsNullOrEmpty(logout?.ClientName) ? logout?.ClientId : logout?.ClientName,
SignOutIframeUrl = logout?.SignOutIFrameUrl,
LogoutId = logoutId
};
if (User?.Identity.IsAuthenticated == true)
{
var idp = User.FindFirst(JwtClaimTypes.IdentityProvider)?.Value;
if (idp != null && idp != IdentityServerConstants.LocalIdentityProvider)
{
var handler = await _handlerProvider.GetHandlerAsync(HttpContext, idp);
if (handler is IAuthenticationSignOutHandler)
{
if (vm.LogoutId == null)
{
// if there's no current logout context, we need to create one
// this captures necessary info from the current logged in user
// before we signout and redirect away to the external IdP for signout
vm.LogoutId = await _interaction.CreateLogoutContextAsync();
}
vm.ExternalAuthenticationScheme = idp;
}
}
}
return vm;
}
}
}

+ 16
- 0
src/Services/Identity/Identity.API/Quickstart/Account/AccountOptions.cs View File

@ -0,0 +1,16 @@
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
namespace IdentityServerHost.Quickstart.UI;
public class AccountOptions
{
public static bool AllowLocalLogin = true;
public static bool AllowRememberLogin = true;
public static TimeSpan RememberMeLoginDuration = TimeSpan.FromDays(30);
public static bool ShowLogoutPrompt = false;
public static bool AutomaticRedirectAfterSignOut = true;
public static string InvalidCredentialsErrorMessage = "Invalid username or password";
}

+ 238
- 0
src/Services/Identity/Identity.API/Quickstart/Account/ExternalController.cs View File

@ -0,0 +1,238 @@
namespace IdentityServerHost.Quickstart.UI;
[SecurityHeaders]
[AllowAnonymous]
public class ExternalController : Controller
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly IIdentityServerInteractionService _interaction;
private readonly IClientStore _clientStore;
private readonly IEventService _events;
private readonly ILogger<ExternalController> _logger;
public ExternalController(
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager,
IIdentityServerInteractionService interaction,
IClientStore clientStore,
IEventService events,
ILogger<ExternalController> logger)
{
_userManager = userManager;
_signInManager = signInManager;
_interaction = interaction;
_clientStore = clientStore;
_events = events;
_logger = logger;
}
/// <summary>
/// initiate roundtrip to external authentication provider
/// </summary>
[HttpGet]
public IActionResult Challenge(string scheme, string returnUrl)
{
if (string.IsNullOrEmpty(returnUrl)) returnUrl = "~/";
// validate returnUrl - either it is a valid OIDC URL or back to a local page
if (Url.IsLocalUrl(returnUrl) == false && _interaction.IsValidReturnUrl(returnUrl) == false)
{
// user might have clicked on a malicious link - should be logged
throw new Exception("invalid return URL");
}
// start challenge and roundtrip the return URL and scheme
var props = new AuthenticationProperties
{
RedirectUri = Url.Action(nameof(Callback)),
Items =
{
{ "returnUrl", returnUrl },
{ "scheme", scheme },
}
};
return Challenge(props, scheme);
}
/// <summary>
/// Post processing of external authentication
/// </summary>
[HttpGet]
public async Task<IActionResult> Callback()
{
// read external identity from the temporary cookie
var result = await HttpContext.AuthenticateAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme);
if (result?.Succeeded != true)
{
throw new Exception("External authentication error");
}
if (_logger.IsEnabled(LogLevel.Debug))
{
var externalClaims = result.Principal.Claims.Select(c => $"{c.Type}: {c.Value}");
_logger.LogDebug("External claims: {@claims}", externalClaims);
}
// lookup our user and external provider info
var (user, provider, providerUserId, claims) = await FindUserFromExternalProviderAsync(result);
if (user == null)
{
// this might be where you might initiate a custom workflow for user registration
// in this sample we don't show how that would be done, as our sample implementation
// simply auto-provisions new external user
user = await AutoProvisionUserAsync(provider, providerUserId, claims);
}
// this allows us to collect any additional claims or properties
// for the specific protocols used and store them in the local auth cookie.
// this is typically used to store data needed for signout from those protocols.
var additionalLocalClaims = new List<Claim>();
var localSignInProps = new AuthenticationProperties();
ProcessLoginCallback(result, additionalLocalClaims, localSignInProps);
// issue authentication cookie for user
// we must issue the cookie maually, and can't use the SignInManager because
// it doesn't expose an API to issue additional claims from the login workflow
var principal = await _signInManager.CreateUserPrincipalAsync(user);
additionalLocalClaims.AddRange(principal.Claims);
var name = principal.FindFirst(JwtClaimTypes.Name)?.Value ?? user.Id;
var isuser = new IdentityServerUser(user.Id)
{
DisplayName = name,
IdentityProvider = provider,
AdditionalClaims = additionalLocalClaims
};
await HttpContext.SignInAsync(isuser, localSignInProps);
// delete temporary cookie used during external authentication
await HttpContext.SignOutAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme);
// retrieve return URL
var returnUrl = result.Properties.Items["returnUrl"] ?? "~/";
// check if external login is in the context of an OIDC request
var context = await _interaction.GetAuthorizationContextAsync(returnUrl);
await _events.RaiseAsync(new UserLoginSuccessEvent(provider, providerUserId, user.Id, name, true, context?.Client.ClientId));
if (context != null)
{
if (context.IsNativeClient())
{
// The client is native, so this change in how to
// return the response is for better UX for the end user.
return this.LoadingPage("Redirect", returnUrl);
}
}
return Redirect(returnUrl);
}
private async Task<(ApplicationUser user, string provider, string providerUserId, IEnumerable<Claim> claims)>
FindUserFromExternalProviderAsync(AuthenticateResult result)
{
var externalUser = result.Principal;
// try to determine the unique id of the external user (issued by the provider)
// the most common claim type for that are the sub claim and the NameIdentifier
// depending on the external provider, some other claim type might be used
var userIdClaim = externalUser.FindFirst(JwtClaimTypes.Subject) ??
externalUser.FindFirst(ClaimTypes.NameIdentifier) ??
throw new Exception("Unknown userid");
// remove the user id claim so we don't include it as an extra claim if/when we provision the user
var claims = externalUser.Claims.ToList();
claims.Remove(userIdClaim);
var provider = result.Properties.Items["scheme"];
var providerUserId = userIdClaim.Value;
// find external user
var user = await _userManager.FindByLoginAsync(provider, providerUserId);
return (user, provider, providerUserId, claims);
}
private async Task<ApplicationUser> AutoProvisionUserAsync(string provider, string providerUserId, IEnumerable<Claim> claims)
{
// create a list of claims that we want to transfer into our store
var filtered = new List<Claim>();
// user's display name
var name = claims.FirstOrDefault(x => x.Type == JwtClaimTypes.Name)?.Value ??
claims.FirstOrDefault(x => x.Type == ClaimTypes.Name)?.Value;
if (name != null)
{
filtered.Add(new Claim(JwtClaimTypes.Name, name));
}
else
{
var first = claims.FirstOrDefault(x => x.Type == JwtClaimTypes.GivenName)?.Value ??
claims.FirstOrDefault(x => x.Type == ClaimTypes.GivenName)?.Value;
var last = claims.FirstOrDefault(x => x.Type == JwtClaimTypes.FamilyName)?.Value ??
claims.FirstOrDefault(x => x.Type == ClaimTypes.Surname)?.Value;
if (first != null && last != null)
{
filtered.Add(new Claim(JwtClaimTypes.Name, first + " " + last));
}
else if (first != null)
{
filtered.Add(new Claim(JwtClaimTypes.Name, first));
}
else if (last != null)
{
filtered.Add(new Claim(JwtClaimTypes.Name, last));
}
}
// email
var email = claims.FirstOrDefault(x => x.Type == JwtClaimTypes.Email)?.Value ??
claims.FirstOrDefault(x => x.Type == ClaimTypes.Email)?.Value;
if (email != null)
{
filtered.Add(new Claim(JwtClaimTypes.Email, email));
}
var user = new ApplicationUser
{
UserName = Guid.NewGuid().ToString(),
};
var identityResult = await _userManager.CreateAsync(user);
if (!identityResult.Succeeded) throw new Exception(identityResult.Errors.First().Description);
if (filtered.Any())
{
identityResult = await _userManager.AddClaimsAsync(user, filtered);
if (!identityResult.Succeeded) throw new Exception(identityResult.Errors.First().Description);
}
identityResult = await _userManager.AddLoginAsync(user, new UserLoginInfo(provider, providerUserId, provider));
if (!identityResult.Succeeded) throw new Exception(identityResult.Errors.First().Description);
return user;
}
// if the external login is OIDC-based, there are certain things we need to preserve to make logout work
// this will be different for WS-Fed, SAML2p or other protocols
private void ProcessLoginCallback(AuthenticateResult externalResult, List<Claim> localClaims, AuthenticationProperties localSignInProps)
{
// if the external system sent a session id claim, copy it over
// so we can use it for single sign-out
var sid = externalResult.Principal.Claims.FirstOrDefault(x => x.Type == JwtClaimTypes.SessionId);
if (sid != null)
{
localClaims.Add(new Claim(JwtClaimTypes.SessionId, sid.Value));
}
// if the external provider issued an id_token, we'll keep it for signout
var idToken = externalResult.Properties.GetTokenValue("id_token");
if (idToken != null)
{
localSignInProps.StoreTokens(new[] { new AuthenticationToken { Name = "id_token", Value = idToken } });
}
}
}

+ 10
- 0
src/Services/Identity/Identity.API/Quickstart/Account/ExternalProvider.cs View File

@ -0,0 +1,10 @@
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
namespace IdentityServerHost.Quickstart.UI;
public class ExternalProvider
{
public string DisplayName { get; set; }
public string AuthenticationScheme { get; set; }
}

+ 18
- 0
src/Services/Identity/Identity.API/Quickstart/Account/LoggedOutViewModel.cs View File

@ -0,0 +1,18 @@
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
namespace IdentityServerHost.Quickstart.UI;
public class LoggedOutViewModel
{
public string PostLogoutRedirectUri { get; set; }
public string ClientName { get; set; }
public string SignOutIframeUrl { get; set; }
public bool AutomaticRedirectAfterSignOut { get; set; }
public string LogoutId { get; set; }
public bool TriggerExternalSignout => ExternalAuthenticationScheme != null;
public string ExternalAuthenticationScheme { get; set; }
}

+ 15
- 0
src/Services/Identity/Identity.API/Quickstart/Account/LoginInputModel.cs View File

@ -0,0 +1,15 @@
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
namespace IdentityServerHost.Quickstart.UI;
public class LoginInputModel
{
[Required]
public string Username { get; set; }
[Required]
public string Password { get; set; }
public bool RememberLogin { get; set; }
public string ReturnUrl { get; set; }
}

+ 17
- 0
src/Services/Identity/Identity.API/Quickstart/Account/LoginViewModel.cs View File

@ -0,0 +1,17 @@
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
namespace IdentityServerHost.Quickstart.UI;
public class LoginViewModel : LoginInputModel
{
public bool AllowRememberLogin { get; set; } = true;
public bool EnableLocalLogin { get; set; } = true;
public IEnumerable<ExternalProvider> ExternalProviders { get; set; } = Enumerable.Empty<ExternalProvider>();
public IEnumerable<ExternalProvider> VisibleExternalProviders => ExternalProviders.Where(x => !String.IsNullOrWhiteSpace(x.DisplayName));
public bool IsExternalLoginOnly => EnableLocalLogin == false && ExternalProviders?.Count() == 1;
public string ExternalLoginScheme => IsExternalLoginOnly ? ExternalProviders?.SingleOrDefault()?.AuthenticationScheme : null;
}

+ 10
- 0
src/Services/Identity/Identity.API/Quickstart/Account/LogoutInputModel.cs View File

@ -0,0 +1,10 @@
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
namespace IdentityServerHost.Quickstart.UI;
public class LogoutInputModel
{
public string LogoutId { get; set; }
}

+ 10
- 0
src/Services/Identity/Identity.API/Quickstart/Account/LogoutViewModel.cs View File

@ -0,0 +1,10 @@
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
namespace IdentityServerHost.Quickstart.UI;
public class LogoutViewModel : LogoutInputModel
{
public bool ShowLogoutPrompt { get; set; } = true;
}

+ 11
- 0
src/Services/Identity/Identity.API/Quickstart/Account/RedirectViewModel.cs View File

@ -0,0 +1,11 @@
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
namespace IdentityServerHost.Quickstart.UI;
public class RedirectViewModel
{
public string RedirectUrl { get; set; }
}

+ 247
- 0
src/Services/Identity/Identity.API/Quickstart/Consent/ConsentController.cs View File

@ -0,0 +1,247 @@
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
namespace IdentityServerHost.Quickstart.UI;
/// <summary>
/// This controller processes the consent UI
/// </summary>
[SecurityHeaders]
[Authorize]
public class ConsentController : Controller
{
private readonly IIdentityServerInteractionService _interaction;
private readonly IEventService _events;
private readonly ILogger<ConsentController> _logger;
public ConsentController(
IIdentityServerInteractionService interaction,
IEventService events,
ILogger<ConsentController> logger)
{
_interaction = interaction;
_events = events;
_logger = logger;
}
/// <summary>
/// Shows the consent screen
/// </summary>
/// <param name="returnUrl"></param>
/// <returns></returns>
[HttpGet]
public async Task<IActionResult> Index(string returnUrl)
{
var vm = await BuildViewModelAsync(returnUrl);
if (vm != null)
{
return View("Index", vm);
}
return View("Error");
}
/// <summary>
/// Handles the consent screen postback
/// </summary>
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Index(ConsentInputModel model)
{
var result = await ProcessConsent(model);
if (result.IsRedirect)
{
var context = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl);
if (context?.IsNativeClient() == true)
{
// The client is native, so this change in how to
// return the response is for better UX for the end user.
return this.LoadingPage("Redirect", result.RedirectUri);
}
return Redirect(result.RedirectUri);
}
if (result.HasValidationError)
{
ModelState.AddModelError(string.Empty, result.ValidationError);
}
if (result.ShowView)
{
return View("Index", result.ViewModel);
}
return View("Error");
}
/*****************************************/
/* helper APIs for the ConsentController */
/*****************************************/
private async Task<ProcessConsentResult> ProcessConsent(ConsentInputModel model)
{
var result = new ProcessConsentResult();
// validate return url is still valid
var request = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl);
if (request == null) return result;
ConsentResponse grantedConsent = null;
// user clicked 'no' - send back the standard 'access_denied' response
if (model?.Button == "no")
{
grantedConsent = new ConsentResponse { Error = AuthorizationError.AccessDenied };
// emit event
await _events.RaiseAsync(new ConsentDeniedEvent(User.GetSubjectId(), request.Client.ClientId, request.ValidatedResources.RawScopeValues));
}
// user clicked 'yes' - validate the data
else if (model?.Button == "yes")
{
// if the user consented to some scope, build the response model
if (model.ScopesConsented != null && model.ScopesConsented.Any())
{
var scopes = model.ScopesConsented;
if (ConsentOptions.EnableOfflineAccess == false)
{
scopes = scopes.Where(x => x != IdentityServerConstants.StandardScopes.OfflineAccess);
}
grantedConsent = new ConsentResponse
{
RememberConsent = model.RememberConsent,
ScopesValuesConsented = scopes.ToArray(),
Description = model.Description
};
// emit event
await _events.RaiseAsync(new ConsentGrantedEvent(User.GetSubjectId(), request.Client.ClientId, request.ValidatedResources.RawScopeValues, grantedConsent.ScopesValuesConsented, grantedConsent.RememberConsent));
}
else
{
result.ValidationError = ConsentOptions.MustChooseOneErrorMessage;
}
}
else
{
result.ValidationError = ConsentOptions.InvalidSelectionErrorMessage;
}
if (grantedConsent != null)
{
// communicate outcome of consent back to identityserver
await _interaction.GrantConsentAsync(request, grantedConsent);
// indicate that's it ok to redirect back to authorization endpoint
result.RedirectUri = model.ReturnUrl;
result.Client = request.Client;
}
else
{
// we need to redisplay the consent UI
result.ViewModel = await BuildViewModelAsync(model.ReturnUrl, model);
}
return result;
}
private async Task<ConsentViewModel> BuildViewModelAsync(string returnUrl, ConsentInputModel model = null)
{
var request = await _interaction.GetAuthorizationContextAsync(returnUrl);
if (request != null)
{
return CreateConsentViewModel(model, returnUrl, request);
}
else
{
_logger.LogError("No consent request matching request: {0}", returnUrl);
}
return null;
}
private ConsentViewModel CreateConsentViewModel(
ConsentInputModel model, string returnUrl,
AuthorizationRequest request)
{
var vm = new ConsentViewModel
{
RememberConsent = model?.RememberConsent ?? true,
ScopesConsented = model?.ScopesConsented ?? Enumerable.Empty<string>(),
Description = model?.Description,
ReturnUrl = returnUrl,
ClientName = request.Client.ClientName ?? request.Client.ClientId,
ClientUrl = request.Client.ClientUri,
ClientLogoUrl = request.Client.LogoUri,
AllowRememberConsent = request.Client.AllowRememberConsent
};
vm.IdentityScopes = request.ValidatedResources.Resources.IdentityResources.Select(x => CreateScopeViewModel(x, vm.ScopesConsented.Contains(x.Name) || model == null)).ToArray();
var apiScopes = new List<ScopeViewModel>();
foreach (var parsedScope in request.ValidatedResources.ParsedScopes)
{
var apiScope = request.ValidatedResources.Resources.FindApiScope(parsedScope.ParsedName);
if (apiScope != null)
{
var scopeVm = CreateScopeViewModel(parsedScope, apiScope, vm.ScopesConsented.Contains(parsedScope.RawValue) || model == null);
apiScopes.Add(scopeVm);
}
}
if (ConsentOptions.EnableOfflineAccess && request.ValidatedResources.Resources.OfflineAccess)
{
apiScopes.Add(GetOfflineAccessScope(vm.ScopesConsented.Contains(IdentityServerConstants.StandardScopes.OfflineAccess) || model == null));
}
vm.ApiScopes = apiScopes;
return vm;
}
private ScopeViewModel CreateScopeViewModel(IdentityResource identity, bool check)
{
return new ScopeViewModel
{
Value = identity.Name,
DisplayName = identity.DisplayName ?? identity.Name,
Description = identity.Description,
Emphasize = identity.Emphasize,
Required = identity.Required,
Checked = check || identity.Required
};
}
public ScopeViewModel CreateScopeViewModel(ParsedScopeValue parsedScopeValue, ApiScope apiScope, bool check)
{
var displayName = apiScope.DisplayName ?? apiScope.Name;
if (!String.IsNullOrWhiteSpace(parsedScopeValue.ParsedParameter))
{
displayName += ":" + parsedScopeValue.ParsedParameter;
}
return new ScopeViewModel
{
Value = parsedScopeValue.RawValue,
DisplayName = displayName,
Description = apiScope.Description,
Emphasize = apiScope.Emphasize,
Required = apiScope.Required,
Checked = check || apiScope.Required
};
}
private ScopeViewModel GetOfflineAccessScope(bool check)
{
return new ScopeViewModel
{
Value = IdentityServerConstants.StandardScopes.OfflineAccess,
DisplayName = ConsentOptions.OfflineAccessDisplayName,
Description = ConsentOptions.OfflineAccessDescription,
Emphasize = true,
Checked = check
};
}
}

+ 14
- 0
src/Services/Identity/Identity.API/Quickstart/Consent/ConsentInputModel.cs View File

@ -0,0 +1,14 @@
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
namespace IdentityServerHost.Quickstart.UI;
public class ConsentInputModel
{
public string Button { get; set; }
public IEnumerable<string> ScopesConsented { get; set; }
public bool RememberConsent { get; set; }
public string ReturnUrl { get; set; }
public string Description { get; set; }
}

+ 15
- 0
src/Services/Identity/Identity.API/Quickstart/Consent/ConsentOptions.cs View File

@ -0,0 +1,15 @@
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
namespace IdentityServerHost.Quickstart.UI;
public class ConsentOptions
{
public static bool EnableOfflineAccess = true;
public static string OfflineAccessDisplayName = "Offline Access";
public static string OfflineAccessDescription = "Access to your applications and resources, even when you are offline";
public static readonly string MustChooseOneErrorMessage = "You must pick at least one permission";
public static readonly string InvalidSelectionErrorMessage = "Invalid selection";
}

+ 16
- 0
src/Services/Identity/Identity.API/Quickstart/Consent/ConsentViewModel.cs View File

@ -0,0 +1,16 @@
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
namespace IdentityServerHost.Quickstart.UI;
public class ConsentViewModel : ConsentInputModel
{
public string ClientName { get; set; }
public string ClientUrl { get; set; }
public string ClientLogoUrl { get; set; }
public bool AllowRememberConsent { get; set; }
public IEnumerable<ScopeViewModel> IdentityScopes { get; set; }
public IEnumerable<ScopeViewModel> ApiScopes { get; set; }
}

+ 17
- 0
src/Services/Identity/Identity.API/Quickstart/Consent/ProcessConsentResult.cs View File

@ -0,0 +1,17 @@
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
namespace IdentityServerHost.Quickstart.UI;
public class ProcessConsentResult
{
public bool IsRedirect => RedirectUri != null;
public string RedirectUri { get; set; }
public Client Client { get; set; }
public bool ShowView => ViewModel != null;
public ConsentViewModel ViewModel { get; set; }
public bool HasValidationError => ValidationError != null;
public string ValidationError { get; set; }
}

+ 15
- 0
src/Services/Identity/Identity.API/Quickstart/Consent/ScopeViewModel.cs View File

@ -0,0 +1,15 @@
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
namespace IdentityServerHost.Quickstart.UI;
public class ScopeViewModel
{
public string Value { get; set; }
public string DisplayName { get; set; }
public string Description { get; set; }
public bool Emphasize { get; set; }
public bool Required { get; set; }
public bool Checked { get; set; }
}

+ 10
- 0
src/Services/Identity/Identity.API/Quickstart/Device/DeviceAuthorizationInputModel.cs View File

@ -0,0 +1,10 @@
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
namespace IdentityServerHost.Quickstart.UI;
public class DeviceAuthorizationInputModel : ConsentInputModel
{
public string UserCode { get; set; }
}

+ 11
- 0
src/Services/Identity/Identity.API/Quickstart/Device/DeviceAuthorizationViewModel.cs View File

@ -0,0 +1,11 @@
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
namespace IdentityServerHost.Quickstart.UI;
public class DeviceAuthorizationViewModel : ConsentViewModel
{
public string UserCode { get; set; }
public bool ConfirmUserCode { get; set; }
}

+ 214
- 0
src/Services/Identity/Identity.API/Quickstart/Device/DeviceController.cs View File

@ -0,0 +1,214 @@
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
namespace IdentityServerHost.Quickstart.UI;
[Authorize]
[SecurityHeaders]
public class DeviceController : Controller
{
private readonly IDeviceFlowInteractionService _interaction;
private readonly IEventService _events;
private readonly IOptions<IdentityServerOptions> _options;
private readonly ILogger<DeviceController> _logger;
public DeviceController(
IDeviceFlowInteractionService interaction,
IEventService eventService,
IOptions<IdentityServerOptions> options,
ILogger<DeviceController> logger)
{
_interaction = interaction;
_events = eventService;
_options = options;
_logger = logger;
}
[HttpGet]
public async Task<IActionResult> Index()
{
string userCodeParamName = _options.Value.UserInteraction.DeviceVerificationUserCodeParameter;
string userCode = Request.Query[userCodeParamName];
if (string.IsNullOrWhiteSpace(userCode)) return View("UserCodeCapture");
var vm = await BuildViewModelAsync(userCode);
if (vm == null) return View("Error");
vm.ConfirmUserCode = true;
return View("UserCodeConfirmation", vm);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> UserCodeCapture(string userCode)
{
var vm = await BuildViewModelAsync(userCode);
if (vm == null) return View("Error");
return View("UserCodeConfirmation", vm);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Callback(DeviceAuthorizationInputModel model)
{
if (model == null) throw new ArgumentNullException(nameof(model));
var result = await ProcessConsent(model);
if (result.HasValidationError) return View("Error");
return View("Success");
}
private async Task<ProcessConsentResult> ProcessConsent(DeviceAuthorizationInputModel model)
{
var result = new ProcessConsentResult();
var request = await _interaction.GetAuthorizationContextAsync(model.UserCode);
if (request == null) return result;
ConsentResponse grantedConsent = null;
// user clicked 'no' - send back the standard 'access_denied' response
if (model.Button == "no")
{
grantedConsent = new ConsentResponse { Error = AuthorizationError.AccessDenied };
// emit event
await _events.RaiseAsync(new ConsentDeniedEvent(User.GetSubjectId(), request.Client.ClientId, request.ValidatedResources.RawScopeValues));
}
// user clicked 'yes' - validate the data
else if (model.Button == "yes")
{
// if the user consented to some scope, build the response model
if (model.ScopesConsented != null && model.ScopesConsented.Any())
{
var scopes = model.ScopesConsented;
if (ConsentOptions.EnableOfflineAccess == false)
{
scopes = scopes.Where(x => x != IdentityServerConstants.StandardScopes.OfflineAccess);
}
grantedConsent = new ConsentResponse
{
RememberConsent = model.RememberConsent,
ScopesValuesConsented = scopes.ToArray(),
Description = model.Description
};
// emit event
await _events.RaiseAsync(new ConsentGrantedEvent(User.GetSubjectId(), request.Client.ClientId, request.ValidatedResources.RawScopeValues, grantedConsent.ScopesValuesConsented, grantedConsent.RememberConsent));
}
else
{
result.ValidationError = ConsentOptions.MustChooseOneErrorMessage;
}
}
else
{
result.ValidationError = ConsentOptions.InvalidSelectionErrorMessage;
}
if (grantedConsent != null)
{
// communicate outcome of consent back to identityserver
await _interaction.HandleRequestAsync(model.UserCode, grantedConsent);
// indicate that's it ok to redirect back to authorization endpoint
result.RedirectUri = model.ReturnUrl;
result.Client = request.Client;
}
else
{
// we need to redisplay the consent UI
result.ViewModel = await BuildViewModelAsync(model.UserCode, model);
}
return result;
}
private async Task<DeviceAuthorizationViewModel> BuildViewModelAsync(string userCode, DeviceAuthorizationInputModel model = null)
{
var request = await _interaction.GetAuthorizationContextAsync(userCode);
if (request != null)
{
return CreateConsentViewModel(userCode, model, request);
}
return null;
}
private DeviceAuthorizationViewModel CreateConsentViewModel(string userCode, DeviceAuthorizationInputModel model, DeviceFlowAuthorizationRequest request)
{
var vm = new DeviceAuthorizationViewModel
{
UserCode = userCode,
Description = model?.Description,
RememberConsent = model?.RememberConsent ?? true,
ScopesConsented = model?.ScopesConsented ?? Enumerable.Empty<string>(),
ClientName = request.Client.ClientName ?? request.Client.ClientId,
ClientUrl = request.Client.ClientUri,
ClientLogoUrl = request.Client.LogoUri,
AllowRememberConsent = request.Client.AllowRememberConsent
};
vm.IdentityScopes = request.ValidatedResources.Resources.IdentityResources.Select(x => CreateScopeViewModel(x, vm.ScopesConsented.Contains(x.Name) || model == null)).ToArray();
var apiScopes = new List<ScopeViewModel>();
foreach (var parsedScope in request.ValidatedResources.ParsedScopes)
{
var apiScope = request.ValidatedResources.Resources.FindApiScope(parsedScope.ParsedName);
if (apiScope != null)
{
var scopeVm = CreateScopeViewModel(parsedScope, apiScope, vm.ScopesConsented.Contains(parsedScope.RawValue) || model == null);
apiScopes.Add(scopeVm);
}
}
if (ConsentOptions.EnableOfflineAccess && request.ValidatedResources.Resources.OfflineAccess)
{
apiScopes.Add(GetOfflineAccessScope(vm.ScopesConsented.Contains(IdentityServerConstants.StandardScopes.OfflineAccess) || model == null));
}
vm.ApiScopes = apiScopes;
return vm;
}
private ScopeViewModel CreateScopeViewModel(IdentityResource identity, bool check)
{
return new ScopeViewModel
{
Value = identity.Name,
DisplayName = identity.DisplayName ?? identity.Name,
Description = identity.Description,
Emphasize = identity.Emphasize,
Required = identity.Required,
Checked = check || identity.Required
};
}
public ScopeViewModel CreateScopeViewModel(ParsedScopeValue parsedScopeValue, ApiScope apiScope, bool check)
{
return new ScopeViewModel
{
Value = parsedScopeValue.RawValue,
DisplayName = apiScope.DisplayName ?? apiScope.Name,
Description = apiScope.Description,
Emphasize = apiScope.Emphasize,
Required = apiScope.Required,
Checked = check || apiScope.Required
};
}
private ScopeViewModel GetOfflineAccessScope(bool check)
{
return new ScopeViewModel
{
Value = IdentityServerConstants.StandardScopes.OfflineAccess,
DisplayName = ConsentOptions.OfflineAccessDisplayName,
Description = ConsentOptions.OfflineAccessDescription,
Emphasize = true,
Checked = check
};
}
}

+ 22
- 0
src/Services/Identity/Identity.API/Quickstart/Diagnostics/DiagnosticsController.cs View File

@ -0,0 +1,22 @@
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
namespace IdentityServerHost.Quickstart.UI;
[SecurityHeaders]
[Authorize]
public class DiagnosticsController : Controller
{
public async Task<IActionResult> Index()
{
var localAddresses = new string[] { "127.0.0.1", "::1", HttpContext.Connection.LocalIpAddress.ToString() };
if (!localAddresses.Contains(HttpContext.Connection.RemoteIpAddress.ToString()))
{
return NotFound();
}
var model = new DiagnosticsViewModel(await HttpContext.AuthenticateAsync());
return View(model);
}
}

+ 28
- 0
src/Services/Identity/Identity.API/Quickstart/Diagnostics/DiagnosticsViewModel.cs View File

@ -0,0 +1,28 @@
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
using System.Text;
using System.Text.Json;
namespace IdentityServerHost.Quickstart.UI;
public class DiagnosticsViewModel
{
public DiagnosticsViewModel(AuthenticateResult result)
{
AuthenticateResult = result;
if (result.Properties.Items.ContainsKey("client_list"))
{
var encoded = result.Properties.Items["client_list"];
var bytes = Base64Url.Decode(encoded);
var value = Encoding.UTF8.GetString(bytes);
Clients = JsonSerializer.Deserialize<string[]>(value);
}
}
public AuthenticateResult AuthenticateResult { get; }
public IEnumerable<string> Clients { get; } = new List<string>();
}

+ 22
- 0
src/Services/Identity/Identity.API/Quickstart/Extensions.cs View File

@ -0,0 +1,22 @@
namespace IdentityServerHost.Quickstart.UI;
public static class Extensions
{
/// <summary>
/// Checks if the redirect URI is for a native client.
/// </summary>
/// <returns></returns>
public static bool IsNativeClient(this AuthorizationRequest context)
{
return !context.RedirectUri.StartsWith("https", StringComparison.Ordinal)
&& !context.RedirectUri.StartsWith("http", StringComparison.Ordinal);
}
public static IActionResult LoadingPage(this Controller controller, string viewName, string redirectUri)
{
controller.HttpContext.Response.StatusCode = 200;
controller.HttpContext.Response.Headers["Location"] = "";
return controller.View(viewName, new RedirectViewModel { RedirectUrl = redirectUri });
}
}

+ 85
- 0
src/Services/Identity/Identity.API/Quickstart/Grants/GrantsController.cs View File

@ -0,0 +1,85 @@
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
namespace IdentityServerHost.Quickstart.UI;
/// <summary>
/// This sample controller allows a user to revoke grants given to clients
/// </summary>
[SecurityHeaders]
[Authorize]
public class GrantsController : Controller
{
private readonly IIdentityServerInteractionService _interaction;
private readonly IClientStore _clients;
private readonly IResourceStore _resources;
private readonly IEventService _events;
public GrantsController(IIdentityServerInteractionService interaction,
IClientStore clients,
IResourceStore resources,
IEventService events)
{
_interaction = interaction;
_clients = clients;
_resources = resources;
_events = events;
}
/// <summary>
/// Show list of grants
/// </summary>
[HttpGet]
public async Task<IActionResult> Index()
{
return View("Index", await BuildViewModelAsync());
}
/// <summary>
/// Handle postback to revoke a client
/// </summary>
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Revoke(string clientId)
{
await _interaction.RevokeUserConsentAsync(clientId);
await _events.RaiseAsync(new GrantsRevokedEvent(User.GetSubjectId(), clientId));
return RedirectToAction("Index");
}
private async Task<GrantsViewModel> BuildViewModelAsync()
{
var grants = await _interaction.GetAllUserGrantsAsync();
var list = new List<GrantViewModel>();
foreach (var grant in grants)
{
var client = await _clients.FindClientByIdAsync(grant.ClientId);
if (client != null)
{
var resources = await _resources.FindResourcesByScopeAsync(grant.Scopes);
var item = new GrantViewModel()
{
ClientId = client.ClientId,
ClientName = client.ClientName ?? client.ClientId,
ClientLogoUrl = client.LogoUri,
ClientUrl = client.ClientUri,
Description = grant.Description,
Created = grant.CreationTime,
Expires = grant.Expiration,
IdentityGrantNames = resources.IdentityResources.Select(x => x.DisplayName ?? x.Name).ToArray(),
ApiGrantNames = resources.ApiScopes.Select(x => x.DisplayName ?? x.Name).ToArray()
};
list.Add(item);
}
}
return new GrantsViewModel
{
Grants = list
};
}
}

+ 23
- 0
src/Services/Identity/Identity.API/Quickstart/Grants/GrantsViewModel.cs View File

@ -0,0 +1,23 @@
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
namespace IdentityServerHost.Quickstart.UI;
public class GrantsViewModel
{
public IEnumerable<GrantViewModel> Grants { get; set; }
}
public class GrantViewModel
{
public string ClientId { get; set; }
public string ClientName { get; set; }
public string ClientUrl { get; set; }
public string ClientLogoUrl { get; set; }
public string Description { get; set; }
public DateTime Created { get; set; }
public DateTime? Expires { get; set; }
public IEnumerable<string> IdentityGrantNames { get; set; }
public IEnumerable<string> ApiGrantNames { get; set; }
}

+ 18
- 0
src/Services/Identity/Identity.API/Quickstart/Home/ErrorViewModel.cs View File

@ -0,0 +1,18 @@
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
namespace IdentityServerHost.Quickstart.UI;
public class ErrorViewModel
{
public ErrorViewModel()
{
}
public ErrorViewModel(string error)
{
Error = new ErrorMessage { Error = error };
}
public ErrorMessage Error { get; set; }
}

+ 63
- 0
src/Services/Identity/Identity.API/Quickstart/Home/HomeController.cs View File

@ -0,0 +1,63 @@
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace IdentityServerHost.Quickstart.UI
{
[SecurityHeaders]
[AllowAnonymous]
public class HomeController : Controller
{
private readonly IIdentityServerInteractionService _interaction;
private readonly IWebHostEnvironment _environment;
private readonly ILogger _logger;
public HomeController(
IIdentityServerInteractionService interaction,
IWebHostEnvironment environment,
ILogger<HomeController> logger)
{
_interaction = interaction;
_environment = environment;
_logger = logger;
}
public IActionResult Index()
{
if (_environment.IsDevelopment())
{
// only show in development
return View();
}
_logger.LogInformation("Homepage is disabled in production. Returning 404.");
return NotFound();
}
/// <summary>
/// Shows the error page
/// </summary>
public async Task<IActionResult> Error(string errorId)
{
var vm = new ErrorViewModel();
// retrieve error details from identityserver
var message = await _interaction.GetErrorContextAsync(errorId);
if (message != null)
{
vm.Error = message;
if (!_environment.IsDevelopment())
{
// only show in development
message.ErrorDescription = null;
}
}
return View("Error", vm);
}
}
}

+ 52
- 0
src/Services/Identity/Identity.API/Quickstart/SecurityHeadersAttribute.cs View File

@ -0,0 +1,52 @@
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
namespace IdentityServerHost.Quickstart.UI;
public class SecurityHeadersAttribute : ActionFilterAttribute
{
public override void OnResultExecuting(ResultExecutingContext context)
{
var result = context.Result;
if (result is ViewResult)
{
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options
if (!context.HttpContext.Response.Headers.ContainsKey("X-Content-Type-Options"))
{
context.HttpContext.Response.Headers.Add("X-Content-Type-Options", "nosniff");
}
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
if (!context.HttpContext.Response.Headers.ContainsKey("X-Frame-Options"))
{
context.HttpContext.Response.Headers.Add("X-Frame-Options", "SAMEORIGIN");
}
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
var csp = "default-src 'self'; object-src 'none'; frame-ancestors 'none'; sandbox allow-forms allow-same-origin allow-scripts; base-uri 'self';";
// also consider adding upgrade-insecure-requests once you have HTTPS in place for production
//csp += "upgrade-insecure-requests;";
// also an example if you need client images to be displayed from twitter
// csp += "img-src 'self' https://pbs.twimg.com;";
// once for standards compliant browsers
if (!context.HttpContext.Response.Headers.ContainsKey("Content-Security-Policy"))
{
context.HttpContext.Response.Headers.Add("Content-Security-Policy", csp);
}
// and once again for IE
if (!context.HttpContext.Response.Headers.ContainsKey("X-Content-Security-Policy"))
{
context.HttpContext.Response.Headers.Add("X-Content-Security-Policy", csp);
}
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy
var referrer_policy = "no-referrer";
if (!context.HttpContext.Response.Headers.ContainsKey("Referrer-Policy"))
{
context.HttpContext.Response.Headers.Add("Referrer-Policy", referrer_policy);
}
}
}
}

+ 124
- 0
src/Services/Identity/Identity.API/SeedData.cs View File

@ -0,0 +1,124 @@
using System.Threading.Tasks;
using System;
namespace Microsoft.eShopOnContainers.Services.Identity.API;
public class SeedData
{
public static async Task EnsureSeedData(IServiceScope scope, IConfiguration configuration, Microsoft.Extensions.Logging.ILogger logger)
{
var retryPolicy = CreateRetryPolicy(configuration, logger);
var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
await retryPolicy.ExecuteAsync(async () =>
{
await context.Database.MigrateAsync();
var userMgr = scope.ServiceProvider.GetRequiredService<UserManager<ApplicationUser>>();
var alice = await userMgr.FindByNameAsync("alice");
if (alice == null)
{
alice = new ApplicationUser
{
UserName = "alice",
Email = "AliceSmith@email.com",
EmailConfirmed = true,
CardHolderName = "Alice Smith",
CardNumber = "4012888888881881",
CardType = 1,
City = "Redmond",
Country = "U.S.",
Expiration = "12/20",
Id = Guid.NewGuid().ToString(),
LastName = "Smith",
Name = "Alice",
PhoneNumber = "1234567890",
ZipCode = "98052",
State = "WA",
Street = "15703 NE 61st Ct",
SecurityNumber = "123"
};
var result = userMgr.CreateAsync(alice, "Pass123$").Result;
if (!result.Succeeded)
{
throw new Exception(result.Errors.First().Description);
}
logger.LogDebug("alice created");
}
else
{
logger.LogDebug("alice already exists");
}
var bob = await userMgr.FindByNameAsync("bob");
if (bob == null)
{
bob = new ApplicationUser
{
UserName = "bob",
Email = "BobSmith@email.com",
EmailConfirmed = true,
CardHolderName = "Bob Smith",
CardNumber = "4012888888881881",
CardType = 1,
City = "Redmond",
Country = "U.S.",
Expiration = "12/20",
Id = Guid.NewGuid().ToString(),
LastName = "Smith",
Name = "Bob",
PhoneNumber = "1234567890",
ZipCode = "98052",
State = "WA",
Street = "15703 NE 61st Ct",
SecurityNumber = "456"
};
var result = await userMgr.CreateAsync(bob, "Pass123$");
if (!result.Succeeded)
{
throw new Exception(result.Errors.First().Description);
}
logger.LogDebug("bob created");
}
else
{
logger.LogDebug("bob already exists");
}
});
}
private static AsyncPolicy CreateRetryPolicy(IConfiguration configuration, Microsoft.Extensions.Logging.ILogger logger)
{
var retryMigrations = false;
bool.TryParse(configuration["RetryMigrations"], out retryMigrations);
// Only use a retry policy if configured to do so.
// When running in an orchestrator/K8s, it will take care of restarting failed services.
if (retryMigrations)
{
return Policy.Handle<Exception>().
WaitAndRetryForeverAsync(
sleepDurationProvider: retry => TimeSpan.FromSeconds(5),
onRetry: (exception, retry, timeSpan) =>
{
logger.LogWarning(
exception,
"Exception {ExceptionType} with message {Message} detected during database migration (retry attempt {retry})",
exception.GetType().Name,
exception.Message,
retry);
}
);
}
return Policy.NoOpAsync();
}
}

+ 0
- 2
src/Services/Identity/Identity.API/Setup/Users.csv View File

@ -1,2 +0,0 @@
CardHolderName,CardNumber,CardType,City,Country,Email,Expiration,LastName,Name,PhoneNumber,UserName,ZipCode,State,Street,SecurityNumber,NormalizedEmail,NormalizedUserName,Password
DemoUser,4012888888881881,1,Redmond,U.S.,demouser@microsoft.com,12/25,DemoLastName,DemoUser,1234567890,demouser@microsoft.com,98052,WA,15703 NE 61st Ct,535,DEMOUSER@MICROSOFT.COM,DEMOUSER@MICROSOFT.COM,Pass@word1

BIN
src/Services/Identity/Identity.API/Setup/images.zip View File


+ 0
- 167
src/Services/Identity/Identity.API/Startup.cs View File

@ -1,167 +0,0 @@
using Microsoft.AspNetCore.DataProtection;
namespace Microsoft.eShopOnContainers.Services.Identity.API
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public IServiceProvider ConfigureServices(IServiceCollection services)
{
RegisterAppInsights(services);
// Add framework services.
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration["ConnectionString"],
sqlServerOptionsAction: sqlOptions =>
{
sqlOptions.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name);
//Configuring Connection Resiliency: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency
sqlOptions.EnableRetryOnFailure(maxRetryCount: 15, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null);
}));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.Configure<AppSettings>(Configuration);
if (Configuration.GetValue<string>("IsClusterEnv") == bool.TrueString)
{
services.AddDataProtection(opts =>
{
opts.ApplicationDiscriminator = "eshop.identity";
})
.PersistKeysToStackExchangeRedis(ConnectionMultiplexer.Connect(Configuration["DPConnectionString"]), "DataProtection-Keys");
}
services.AddHealthChecks()
.AddCheck("self", () => HealthCheckResult.Healthy())
.AddSqlServer(Configuration["ConnectionString"],
name: "IdentityDB-check",
tags: new string[] { "IdentityDB" });
services.AddTransient<ILoginService<ApplicationUser>, EFLoginService>();
services.AddTransient<IRedirectService, RedirectService>();
var connectionString = Configuration["ConnectionString"];
var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
// Adds IdentityServer
services.AddIdentityServer(x =>
{
x.IssuerUri = "null";
x.Authentication.CookieLifetime = TimeSpan.FromHours(2);
})
.AddServerSideSessions()
.AddDevspacesIfNeeded(Configuration.GetValue("EnableDevspaces", false))
.AddSigningCredential(Certificate.Get())
.AddAspNetIdentity<ApplicationUser>()
.AddConfigurationStore(options =>
{
options.ConfigureDbContext = builder => builder.UseSqlServer(connectionString,
sqlServerOptionsAction: sqlOptions =>
{
sqlOptions.MigrationsAssembly(migrationsAssembly);
//Configuring Connection Resiliency: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency
sqlOptions.EnableRetryOnFailure(maxRetryCount: 15, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null);
});
})
.AddOperationalStore(options =>
{
options.ConfigureDbContext = builder => builder.UseSqlServer(connectionString,
sqlServerOptionsAction: sqlOptions =>
{
sqlOptions.MigrationsAssembly(migrationsAssembly);
//Configuring Connection Resiliency: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency
sqlOptions.EnableRetryOnFailure(maxRetryCount: 15, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null);
});
})
.Services.AddTransient<IProfileService, ProfileService>();
//services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_3_0);
services.AddControllers();
services.AddControllersWithViews();
services.AddRazorPages();
var container = new ContainerBuilder();
container.Populate(services);
return new AutofacServiceProvider(container.Build());
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
{
//loggerFactory.AddConsole(Configuration.GetSection("Logging"));
//loggerFactory.AddDebug();
//loggerFactory.AddAzureWebAppDiagnostics();
//loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace);
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
var pathBase = Configuration["PATH_BASE"];
if (!string.IsNullOrEmpty(pathBase))
{
//loggerFactory.CreateLogger<Startup>().LogDebug("Using PATH BASE '{pathBase}'", pathBase);
app.UsePathBase(pathBase);
}
app.UseStaticFiles();
// Make work identity server redirections in Edge and lastest versions of browsers. WARN: Not valid in a production environment.
app.Use(async (context, next) =>
{
context.Response.Headers.Add("Content-Security-Policy", "script-src 'unsafe-inline'");
context.Response.Headers.Add("Access-Control-Allow-Origin", "*");
context.Response.Headers.Add("Access-Control-Allow-Headers", "*");
context.Response.Headers.Add("Access-Control-Allow-Methods", "GET, POST, PUT, OPTIONS");
await next();
});
app.UseForwardedHeaders();
// Adds IdentityServer
app.UseIdentityServer();
// Fix a problem with chrome. Chrome enabled a new feature "Cookies without SameSite must be secure",
// the cookies should be expired from https, but in eShop, the internal communication in aks and docker compose is http.
// To avoid this problem, the policy of cookies should be in Lax mode.
app.UseCookiePolicy(new CookiePolicyOptions { MinimumSameSitePolicy = AspNetCore.Http.SameSiteMode.Lax });
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
endpoints.MapControllers();
endpoints.MapHealthChecks("/hc", new HealthCheckOptions()
{
Predicate = _ => true,
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});
endpoints.MapHealthChecks("/liveness", new HealthCheckOptions
{
Predicate = r => r.Name.Contains("self")
});
});
}
private void RegisterAppInsights(IServiceCollection services)
{
services.AddApplicationInsightsTelemetry(Configuration);
services.AddApplicationInsightsKubernetesEnricher();
}
}
}

+ 7
- 0
src/Services/Identity/Identity.API/Views/Account/AccessDenied.cshtml View File

@ -0,0 +1,7 @@
<div class="container">
<div class="lead">
<h1>Access Denied</h1>
<p>You do not have access to that resource.</p>
</div>
</div>

+ 17
- 4
src/Services/Identity/Identity.API/Views/Account/LoggedOut.cshtml View File

@ -1,6 +1,11 @@
@model Microsoft.eShopOnContainers.Services.Identity.API.Models.AccountViewModels.LoggedOutViewModel
@model LoggedOutViewModel
<div class="container page-header">
@{
// set this so the layout rendering sees an anonymous user
ViewData["signed-out"] = true;
}
<div class="logged-out-page">
<h1> <h1>
Logout Logout
<small>You are now logged out</small> <small>You are now logged out</small>
@ -9,13 +14,21 @@
@if (Model.PostLogoutRedirectUri != null) @if (Model.PostLogoutRedirectUri != null)
{ {
<div> <div>
Click <a href="@Model.PostLogoutRedirectUri">here</a> to return to the
Click <a class="PostLogoutRedirectUri" href="@Model.PostLogoutRedirectUri">here</a> to return to the
<span>@Model.ClientName</span> application. <span>@Model.ClientName</span> application.
</div> </div>
} }
@if (Model.SignOutIframeUrl != null) @if (Model.SignOutIframeUrl != null)
{ {
<iframe style="display:none" width="0" height="0" class="signout" src="@Model.SignOutIframeUrl"></iframe>
<iframe width="0" height="0" class="signout" src="@Model.SignOutIframeUrl"></iframe>
} }
</div> </div>
@section scripts
{
@if (Model.AutomaticRedirectAfterSignOut)
{
<script src="~/js/signout-redirect.js"></script>
}
}

+ 58
- 28
src/Services/Identity/Identity.API/Views/Account/Login.cshtml View File

@ -1,28 +1,58 @@
@model Microsoft.eShopOnContainers.Services.Identity.API.Models.AccountViewModels.LoginViewModel
@{
ViewData["Title"] = "Log in";
var requestQuery = ViewContext.HttpContext.Request.Query;
requestQuery.TryGetValue("ReturnUrl", out var returnUrl);
string partialView;
if (returnUrl[0].Contains("client_id=js"))
{
Layout = "_Layout-SPA";
partialView = "_LoginPartial-SPA.cshtml";
}
else
{
partialView = "_LoginPartial-MVC.cshtml";
}
}
<partial name=@partialView model=@Model />
@section Scripts {
@{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
}
@model LoginViewModel
<div class="login-page">
<partial name="_ValidationSummary" />
<div class="container">
<div class="row top-buffer">
@if (Model.EnableLocalLogin)
{
<div class="col"></div>
<div class="col-sm-6">
<div class="card">
<div class="card-header">
<h2>Login</h2>
</div>
<div class="card-body">
<form asp-route="Login">
<input type="hidden" asp-for="ReturnUrl" />
<div class="form-group">
<label asp-for="Username"></label>
<input class="form-control" placeholder="Username" asp-for="Username" autofocus>
</div>
<div class="form-group">
<label asp-for="Password"></label>
<input type="password" class="form-control" placeholder="Password" asp-for="Password" autocomplete="off">
</div>
@if (Model.AllowRememberLogin)
{
<div class="form-group">
<div class="form-check">
<input class="form-check-input" asp-for="RememberLogin">
<label class="form-check-label" asp-for="RememberLogin">
Remember My Login
</label>
</div>
</div>
}
<div>
<p>The default users are alice/bob, password: Pass123$</p>
</div>
<button class="btn btn-primary" name="button" value="login">Login</button>
<button class="btn btn-secondary" name="button" value="cancel">Cancel</button>
</form>
</div>
</div>
</div>
<div class="col"></div>
}
</div>
</div>
</div>

+ 10
- 16
src/Services/Identity/Identity.API/Views/Account/Logout.cshtml View File

@ -1,21 +1,15 @@
@model Microsoft.eShopOnContainers.Services.Identity.API.Models.AccountViewModels.LogoutViewModel
@model LogoutViewModel
<div class="container logout-page">
<div class="page-header">
<div class="logout-page">
<div class="lead">
<h1>Logout</h1> <h1>Logout</h1>
<p>Would you like to logut of IdentityServer?</p>
</div> </div>
<div class="row">
<div class="col-sm-6">
<p>Would you like to logout of IdentityServer?</p>
<form asp-action="Logout">
<input type="hidden" name="logoutId" value="@Model.LogoutId" />
<fieldset>
<div class="form-group">
<button class="btn btn-primary">Yes</button>
</div>
</fieldset>
</form>
<form asp-action="Logout">
<input type="hidden" name="logoutId" value="@Model.LogoutId" />
<div class="form-group">
<button class="btn btn-primary">Yes</button>
</div> </div>
</div>
</div>
</form>
</div>

+ 0
- 3
src/Services/Identity/Identity.API/Views/Account/Redirecting.cshtml View File

@ -1,3 +0,0 @@
<br />
<br />
<p style="width:80%;"><center><span>Redirecting...</span></center></p>

+ 0
- 28
src/Services/Identity/Identity.API/Views/Account/Register.cshtml View File

@ -1,28 +0,0 @@
@model Microsoft.eShopOnContainers.Services.Identity.API.Models.AccountViewModels.RegisterViewModel
@{
ViewData["Title"] = "Register";
var requestQuery = ViewContext.HttpContext.Request.Query;
requestQuery.TryGetValue("ReturnUrl", out var returnUrl);
string partialView;
if (returnUrl[0].Contains("client_id=js"))
{
Layout = "_Layout-SPA";
partialView = "_RegisterPartial-SPA.cshtml";
}
else
{
partialView = "_RegisterPartial-MVC.cshtml";
}
}
<partial name=@partialView model=@Model />
@section Scripts {
@{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
}

+ 0
- 54
src/Services/Identity/Identity.API/Views/Account/_LoginPartial-MVC.cshtml View File

@ -1,54 +0,0 @@
@model Microsoft.eShopOnContainers.Services.Identity.API.Models.AccountViewModels.LoginViewModel
<div class="brand-header-block">
<ul class="container">
<li><a asp-area="" asp-controller="Account" asp-action="Register" asp-route-returnurl="@ViewData["ReturnUrl"]">REGISTER</a></li>
<li class="active" style="margin-right: 65px;">LOGIN</li>
</ul>
</div>
<div class="container account-login-container">
<div class="row">
<div class="col-md-12">
<section>
<form asp-controller="Account" asp-action="Login" asp-route-returnurl="@ViewData["ReturnUrl"]" method="post" class="form-horizontal">
<input type="hidden" asp-for="ReturnUrl" />
<h4>ARE YOU REGISTERED?</h4>
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="Email" class="control-label form-label"></label>
<input asp-for="Email" class="form-control form-input form-input-center" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Password" class="control-label form-label"></label>
<input asp-for="Password" class="form-control form-input form-input-center" />
<span asp-validation-for="Password" class="text-danger"></span>
</div>
<div class="form-group">
<div class="checkbox">
<label asp-for="RememberMe">
<input asp-for="RememberMe" />
@Html.DisplayNameFor(m => m.RememberMe)
</label>
</div>
</div>
<div class="form-group">
<button type="submit" class="btn btn-default btn-brand btn-brand-big">&nbsp;LOG IN&nbsp;</button>
</div>
<p>
<a asp-action="Register" asp-route-returnurl="@ViewData["ReturnUrl"]" class="text">Register as a new user?</a>
</p>
<p>
Note that for demo purposes you don't need to register and can login with these credentials:
</p>
<p>
User: <b>demouser@microsoft.com</b>
</p>
<p>
Password: <b>Pass@word1</b>
</p>
</form>
</section>
</div>
</div>
</div>

+ 0
- 47
src/Services/Identity/Identity.API/Views/Account/_LoginPartial-SPA.cshtml View File

@ -1,47 +0,0 @@
@model Microsoft.eShopOnContainers.Services.Identity.API.Models.AccountViewModels.LoginViewModel
<div class="container">
<div class="row">
<div class="col-md-12">
<section>
<form asp-controller="Account" asp-action="Login" asp-route-returnurl="@ViewData["ReturnUrl"]" method="post" class="form-login">
<input type="hidden" asp-for="ReturnUrl" />
<h4 class="text-left mb-4">ARE YOU REGISTERED?</h4>
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="Email" class="control-label form-label"></label>
<input asp-for="Email" class="form-control form-input w-100" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Password" class="control-label form-label"></label>
<input asp-for="Password" class="form-control form-input w-100" />
<span asp-validation-for="Password" class="text-danger"></span>
</div>
<div class="d-flex align-items-center justify-content-between">
<div class="checkbox">
<label asp-for="RememberMe">
<input asp-for="RememberMe" class="mr-1" />
@Html.DisplayNameFor(m => m.RememberMe)
</label>
</div>
<button type="submit" class="btn btn-primary">LOG IN</button>
</div>
<div class="form-login-register-link text-center mt-3">
<a class="text-link" asp-action="Register" asp-route-returnurl="@ViewData["ReturnUrl"]">Register as a new user?</a>
</div>
</form>
<div class="form-login-details">
<div>
Note that for demo purposes you don't need to register and can login with these credentials:
</div>
<div>
User: <strong>demouser@microsoft.com</strong> Password: <strong>Pass@word1</strong>
</div>
</div>
</section>
</div>
</div>
</div>

+ 0
- 100
src/Services/Identity/Identity.API/Views/Account/_RegisterPartial-MVC.cshtml View File

@ -1,100 +0,0 @@
@model Microsoft.eShopOnContainers.Services.Identity.API.Models.AccountViewModels.RegisterViewModel
<div class="brand-header-block">
<ul class="container">
<li class="active">REGISTER</li>
<li style="margin-right: 65px;"><a asp-area="" asp-controller="Account" asp-action="Login" asp-route-returnurl="@ViewData["ReturnUrl"]">LOGIN</a></li>
</ul>
</div>
<div class="container register-container">
<form asp-controller="Account" asp-action="Register" asp-route-returnurl="@ViewData["ReturnUrl"]" method="post" class="form-horizontal">
<h4 class="order-create-section-title">CREATE NEW ACCOUNT</h4>
<div asp-validation-summary="All" class="text-danger"></div>
<div class="row">
<div class="form-group col-sm-6">
<label asp-for="User.Name" class="control-label form-label">NAME</label>
<input asp-for="User.Name" class="form-control form-input" />
<span asp-validation-for="User.Name" class="text-danger" />
</div>
<div class="form-group col-sm-6">
<label asp-for="User.LastName" class="control-label form-label">LAST NAME</label>
<input asp-for="User.LastName" class="form-control form-input" />
<span asp-validation-for="User.LastName" class="text-danger" />
</div>
<div class="form-group col-sm-6">
<label asp-for="User.Street" class="control-label form-label">ADDRESS</label>
<input asp-for="User.Street" class="form-control form-input" />
<span asp-validation-for="User.Street" class="text-danger" />
</div>
<div class="form-group col-sm-6">
<label asp-for="User.City" class="control-label form-label"></label>
<input asp-for="User.City" class="form-control form-input" />
<span asp-validation-for="User.City" class="text-danger" />
</div>
<div class="form-group col-sm-6">
<label asp-for="User.State" class="control-label form-label"></label>
<input asp-for="User.State" class="form-control form-input" />
<span asp-validation-for="User.State" class="text-danger" />
</div>
<div class="form-group col-sm-6">
<label asp-for="User.Country" class="control-label form-label"></label>
<input asp-for="User.Country" class="form-control form-input" />
<span asp-validation-for="User.Country" class="text-danger" />
</div>
<div class="form-group col-sm-6">
<label asp-for="User.ZipCode" class="control-label form-label">POSTCODE</label>
<input asp-for="User.ZipCode" class="form-control form-input" />
<span asp-validation-for="User.ZipCode" class="text-danger" />
</div>
<div class="form-group col-sm-6">
<label asp-for="User.PhoneNumber" class="control-label form-label">PHONE NUMBER</label>
<input asp-for="User.PhoneNumber" class="form-control form-input" />
<span asp-validation-for="User.PhoneNumber" class="text-danger" />
</div>
<div class="form-group col-sm-6">
<label asp-for="User.CardNumber" class="control-label form-label">Card Number</label>
<input asp-for="User.CardNumber" class="form-control form-input" />
<span asp-validation-for="User.CardNumber" class="text-danger" />
</div>
<div class="form-group col-sm-6">
<label asp-for="User.CardHolderName" class="control-label form-label">Cardholder Name</label>
<input asp-for="User.CardHolderName" class="form-control form-input" />
<span asp-validation-for="User.CardHolderName" class="text-danger" />
</div>
<div class="form-group col-sm-3">
<label asp-for="User.Expiration" class="control-label form-label">Expiration Date</label>
<input asp-for="User.Expiration" placeholder="MM/YY" class="form-control form-input form-input-small" />
<span asp-validation-for="User.Expiration" class="text-danger" />
</div>
<div class="form-group col-sm-3">
<label asp-for="User.SecurityNumber" class="control-label form-label">Security Code</label>
<input asp-for="User.SecurityNumber" class="form-control form-input form-input-small" />
<span asp-validation-for="User.SecurityNumber" class="text-danger" />
</div>
</div>
<br /><br />
<div class="row">
<div class="form-group col-sm-6">
<label asp-for="Email" class="control-label form-label"></label>
<input asp-for="Email" class="form-control form-input" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
<div class="form-group col-sm-offset-6"></div>
<div class="form-group col-sm-6">
<label asp-for="Password" class="control-label form-label"></label>
<input asp-for="Password" class="form-control form-input" />
<span asp-validation-for="Password" class="text-danger"></span>
</div>
<div class="form-group col-sm-6">
<label asp-for="ConfirmPassword" class="control-label form-label"></label>
<input asp-for="ConfirmPassword" class="form-control form-input" />
<span asp-validation-for="ConfirmPassword" class="text-danger"></span>
</div>
</div>
<br /><br />
<div class="form-group">
<button type="submit" class="btn btn-default btn-brand">&nbsp;Register&nbsp;</button>
</div>
<br /><br />
</form>
</div>

+ 0
- 100
src/Services/Identity/Identity.API/Views/Account/_RegisterPartial-SPA.cshtml View File

@ -1,100 +0,0 @@
@model Microsoft.eShopOnContainers.Services.Identity.API.Models.AccountViewModels.RegisterViewModel
<div class="container">
<h1 class="mb-4 mt-5">[ New Account ]</h1>
<form asp-controller="Account" asp-action="Register" asp-route-returnurl="@ViewData["ReturnUrl"]" method="post" class="form-register">
<h4 class="order-create-section-title">Information</h4>
<div asp-validation-summary="All" class="text-danger"></div>
<div class="row">
<div class="form-group col-sm-6">
<label asp-for="User.Name" class="control-label form-label">Name</label>
<input asp-for="User.Name" class="form-control form-input" />
<span asp-validation-for="User.Name" class="text-danger" />
</div>
<div class="form-group col-sm-6">
<label asp-for="User.LastName" class="control-label form-label">Last Name</label>
<input asp-for="User.LastName" class="form-control form-input" />
<span asp-validation-for="User.LastName" class="text-danger" />
</div>
<div class="form-group col-sm-6">
<label asp-for="User.Street" class="control-label form-label">Address</label>
<input asp-for="User.Street" class="form-control form-input" />
<span asp-validation-for="User.Street" class="text-danger" />
</div>
<div class="form-group col-sm-6">
<label asp-for="User.City" class="control-label form-label"></label>
<input asp-for="User.City" class="form-control form-input" />
<span asp-validation-for="User.City" class="text-danger" />
</div>
<div class="form-group col-sm-6">
<label asp-for="User.State" class="control-label form-label"></label>
<input asp-for="User.State" class="form-control form-input" />
<span asp-validation-for="User.State" class="text-danger" />
</div>
<div class="form-group col-sm-6">
<label asp-for="User.Country" class="control-label form-label"></label>
<input asp-for="User.Country" class="form-control form-input" />
<span asp-validation-for="User.Country" class="text-danger" />
</div>
<div class="form-group col-sm-6">
<label asp-for="User.ZipCode" class="control-label form-label">Postcode</label>
<input asp-for="User.ZipCode" class="form-control form-input" />
<span asp-validation-for="User.ZipCode" class="text-danger" />
</div>
<div class="form-group col-sm-6">
<label asp-for="User.PhoneNumber" class="control-label form-label">Phone Number</label>
<input asp-for="User.PhoneNumber" class="form-control form-input" />
<span asp-validation-for="User.PhoneNumber" class="text-danger" />
</div>
</div>
<div class="mt-4">
<h4 class="order-create-section-title">Credit Card</h4>
</div>
<div class="row">
<div class="form-group col-sm-6">
<label asp-for="User.CardNumber" class="control-label form-label">Card Number</label>
<input asp-for="User.CardNumber" class="form-control form-input" />
<span asp-validation-for="User.CardNumber" class="text-danger" />
</div>
<div class="form-group col-sm-6">
<label asp-for="User.CardHolderName" class="control-label form-label">Cardholder Name</label>
<input asp-for="User.CardHolderName" class="form-control form-input" />
<span asp-validation-for="User.CardHolderName" class="text-danger" />
</div>
<div class="form-group col-sm-3">
<label asp-for="User.Expiration" class="control-label form-label">Expiration Date</label>
<input asp-for="User.Expiration" placeholder="MM/YY" class="form-control form-input form-input-small" />
<span asp-validation-for="User.Expiration" class="text-danger" />
</div>
<div class="form-group col-sm-3">
<label asp-for="User.SecurityNumber" class="control-label form-label">Security Code</label>
<input asp-for="User.SecurityNumber" class="form-control form-input form-input-small" />
<span asp-validation-for="User.SecurityNumber" class="text-danger" />
</div>
</div>
<br /><br />
<div class="row">
<div class="form-group col-sm-6">
<label asp-for="Email" class="control-label form-label"></label>
<input asp-for="Email" class="form-control form-input" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
<div class="form-group col-sm-offset-6"></div>
<div class="form-group col-sm-6">
<label asp-for="Password" class="control-label form-label"></label>
<input asp-for="Password" class="form-control form-input" />
<span asp-validation-for="Password" class="text-danger"></span>
</div>
<div class="form-group col-sm-6">
<label asp-for="ConfirmPassword" class="control-label form-label"></label>
<input asp-for="ConfirmPassword" class="form-control form-input" />
<span asp-validation-for="ConfirmPassword" class="text-danger"></span>
</div>
</div>
<br /><br />
<div class="d-flex mt-3 justify-content-end">
<button type="submit" class="btn btn-primary">Register</button>
</div>
<br /><br />
</form>
</div>

+ 2
- 2
src/Services/Identity/Identity.API/Views/Consent/Index.cshtml View File

@ -1,4 +1,4 @@
@model Microsoft.eShopOnContainers.Services.Identity.API.Models.ConsentViewModels.ConsentViewModel
@model ConsentViewModel
<div class="page-consent"> <div class="page-consent">
<div class="lead"> <div class="lead">
@ -101,4 +101,4 @@
</div> </div>
</div> </div>
</form> </form>
</div>
</div>

+ 7
- 0
src/Services/Identity/Identity.API/Views/Device/Success.cshtml View File

@ -0,0 +1,7 @@
<div class="page-device-success">
<div class="lead">
<h1>Success</h1>
<p>You have successfully authorized the device</p>
</div>
</div>

+ 23
- 0
src/Services/Identity/Identity.API/Views/Device/UserCodeCapture.cshtml View File

@ -0,0 +1,23 @@
@model string
<div class="page-device-code">
<div class="lead">
<h1>User Code</h1>
<p>Please enter the code displayed on your device.</p>
</div>
<partial name="_ValidationSummary" />
<div class="row">
<div class="col-sm-6">
<form asp-action="UserCodeCapture">
<div class="form-group">
<label for="userCode">User Code:</label>
<input class="form-control" for="userCode" name="userCode" autofocus />
</div>
<button class="btn btn-primary" name="button">Submit</button>
</form>
</div>
</div>
</div>

+ 108
- 0
src/Services/Identity/Identity.API/Views/Device/UserCodeConfirmation.cshtml View File

@ -0,0 +1,108 @@
@model DeviceAuthorizationViewModel
<div class="page-device-confirmation">
<div class="lead">
@if (Model.ClientLogoUrl != null)
{
<div class="client-logo"><img src="@Model.ClientLogoUrl"></div>
}
<h1>
@Model.ClientName
<small class="text-muted">is requesting your permission</small>
</h1>
@if (Model.ConfirmUserCode)
{
<p>Please confirm that the authorization request quotes the code: <strong>@Model.UserCode</strong>.</p>
}
<p>Uncheck the permissions you do not wish to grant.</p>
</div>
<div class="row">
<div class="col-sm-8">
<partial name="_ValidationSummary" />
</div>
</div>
<form asp-action="Callback">
<input asp-for="UserCode" type="hidden" value="@Model.UserCode" />
<div class="row">
<div class="col-sm-8">
@if (Model.IdentityScopes.Any())
{
<div class="form-group">
<div class="card">
<div class="card-header">
<span class="glyphicon glyphicon-user"></span>
Personal Information
</div>
<ul class="list-group list-group-flush">
@foreach (var scope in Model.IdentityScopes)
{
<partial name="_ScopeListItem" model="@scope" />
}
</ul>
</div>
</div>
}
@if (Model.ApiScopes.Any())
{
<div class="form-group">
<div class="card">
<div class="card-header">
<span class="glyphicon glyphicon-tasks"></span>
Application Access
</div>
<ul class="list-group list-group-flush">
@foreach (var scope in Model.ApiScopes)
{
<partial name="_ScopeListItem" model="scope" />
}
</ul>
</div>
</div>
}
<div class="form-group">
<div class="card">
<div class="card-header">
<span class="glyphicon glyphicon-tasks"></span>
Description
</div>
<div class="card-body">
<input class="form-control" placeholder="Description or name of device" asp-for="Description" autofocus>
</div>
</div>
</div>
@if (Model.AllowRememberConsent)
{
<div class="form-group">
<div class="form-check">
<input class="form-check-input" asp-for="RememberConsent">
<label class="form-check-label" asp-for="RememberConsent">
<strong>Remember My Decision</strong>
</label>
</div>
</div>
}
</div>
</div>
<div class="row">
<div class="col-sm-4">
<button name="button" value="yes" class="btn btn-primary" autofocus>Yes, Allow</button>
<button name="button" value="no" class="btn btn-secondary">No, Do Not Allow</button>
</div>
<div class="col-sm-4 col-lg-auto">
@if (Model.ClientUrl != null)
{
<a class="btn btn-outline-info" href="@Model.ClientUrl">
<span class="glyphicon glyphicon-info-sign"></span>
<strong>@Model.ClientName</strong>
</a>
}
</div>
</div>
</form>
</div>

+ 64
- 0
src/Services/Identity/Identity.API/Views/Diagnostics/Index.cshtml View File

@ -0,0 +1,64 @@
@model DiagnosticsViewModel
<div class="diagnostics-page">
<div class="lead">
<h1>Authentication Cookie</h1>
</div>
<div class="row">
<div class="col">
<div class="card">
<div class="card-header">
<h2>Claims</h2>
</div>
<div class="card-body">
<dl>
@foreach (var claim in Model.AuthenticateResult.Principal.Claims)
{
<dt>@claim.Type</dt>
<dd>@claim.Value</dd>
}
</dl>
</div>
</div>
</div>
<div class="col">
<div class="card">
<div class="card-header">
<h2>Properties</h2>
</div>
<div class="card-body">
<dl>
@foreach (var prop in Model.AuthenticateResult.Properties.Items)
{
<dt>@prop.Key</dt>
<dd>@prop.Value</dd>
}
@if (Model.Clients.Any())
{
<dt>Clients</dt>
<dd>
@{
var clients = Model.Clients.ToArray();
for(var i = 0; i < clients.Length; i++)
{
<text>@clients[i]</text>
if (i < clients.Length - 1)
{
<text>, </text>
}
}
}
</dd>
}
</dl>
</div>
</div>
</div>
</div>
</div>

+ 87
- 0
src/Services/Identity/Identity.API/Views/Grants/Index.cshtml View File

@ -0,0 +1,87 @@
@model GrantsViewModel
<div class="grants-page">
<div class="lead">
<h1>Client Application Permissions</h1>
<p>Below is the list of applications you have given permission to and the resources they have access to.</p>
</div>
@if (Model.Grants.Any() == false)
{
<div class="row">
<div class="col-sm-8">
<div class="alert alert-info">
You have not given access to any applications
</div>
</div>
</div>
}
else
{
foreach (var grant in Model.Grants)
{
<div class="card">
<div class="card-header">
<div class="row">
<div class="col-sm-8 card-title">
@if (grant.ClientLogoUrl != null)
{
<img src="@grant.ClientLogoUrl">
}
<strong>@grant.ClientName</strong>
</div>
<div class="col-sm-2">
<form asp-action="Revoke">
<input type="hidden" name="clientId" value="@grant.ClientId">
<button class="btn btn-danger">Revoke Access</button>
</form>
</div>
</div>
</div>
<ul class="list-group list-group-flush">
@if (grant.Description != null)
{
<li class="list-group-item">
<label>Description:</label> @grant.Description
</li>
}
<li class="list-group-item">
<label>Created:</label> @grant.Created.ToString("yyyy-MM-dd")
</li>
@if (grant.Expires.HasValue)
{
<li class="list-group-item">
<label>Expires:</label> @grant.Expires.Value.ToString("yyyy-MM-dd")
</li>
}
@if (grant.IdentityGrantNames.Any())
{
<li class="list-group-item">
<label>Identity Grants</label>
<ul>
@foreach (var name in grant.IdentityGrantNames)
{
<li>@name</li>
}
</ul>
</li>
}
@if (grant.ApiGrantNames.Any())
{
<li class="list-group-item">
<label>API Grants</label>
<ul>
@foreach (var name in grant.ApiGrantNames)
{
<li>@name</li>
}
</ul>
</li>
}
</ul>
</div>
}
}
</div>

+ 30
- 28
src/Services/Identity/Identity.API/Views/Home/Index.cshtml View File

@ -1,30 +1,32 @@
<div class="welcome-page container">
<div class="row page-header">
<div class="col-sm-10">
<h1>
<img class="icon" src="~/icon.jpg">
Welcome to Duende.IdentityServer
@*<small>(build {version})</small>*@
</h1>
</div>
</div>
@using System.Diagnostics
<div class="row">
<div class="col-sm-8">
<p>
IdentityServer publishes a
<a href="~/.well-known/openid-configuration">discovery document</a>
where you can find metadata and links to all the endpoints, key material, etc.
</p>
</div>
</div>
<div class="row">
<div class="col-sm-8">
<p>
Here are links to the
<a href="https://github.com/identityserver/Duende.IdentityServer">source code repository</a>,
and <a href="https://github.com/identityserver/Duende.IdentityServer.Samples">ready to use samples</a>.
</p>
</div>
</div>
@{
var version = FileVersionInfo.GetVersionInfo(typeof(Duende.IdentityServer.Hosting.IdentityServerMiddleware).Assembly.Location).ProductVersion.Split('+').First();
}
<div class="welcome-page">
<h1>
<img src="~/icon.jpg">
Welcome to IdentityServer4
<small class="text-muted">(version @version)</small>
</h1>
<ul>
<li>
IdentityServer publishes a
<a href="~/.well-known/openid-configuration">discovery document</a>
where you can find metadata and links to all the endpoints, key material, etc.
</li>
<li>
Click <a href="~/diagnostics">here</a> to see the claims for your current session.
</li>
<li>
Click <a href="~/grants">here</a> to manage your stored grants.
</li>
<li>
Here are links to the
<a href="https://github.com/identityserver/IdentityServer4">source code repository</a>,
and <a href="https://github.com/IdentityServer/IdentityServer4/tree/main/samples">ready to use samples</a>.
</li>
</ul>
</div> </div>

+ 8
- 2
src/Services/Identity/Identity.API/Views/Shared/Error.cshtml View File

@ -1,12 +1,13 @@
@model Microsoft.eShopOnContainers.Services.Identity.API.Models.ErrorViewModel
@model ErrorViewModel
@{ @{
var error = Model?.Error?.Error; var error = Model?.Error?.Error;
var errorDescription = Model?.Error?.ErrorDescription;
var request_id = Model?.Error?.RequestId; var request_id = Model?.Error?.RequestId;
} }
<div class="error-page"> <div class="error-page">
<div class="page-header">
<div class="lead">
<h1>Error</h1> <h1>Error</h1>
</div> </div>
@ -22,6 +23,11 @@
: @error : @error
</em> </em>
</strong> </strong>
if (errorDescription != null)
{
<div>@errorDescription</div>
}
} }
</div> </div>


+ 11
- 0
src/Services/Identity/Identity.API/Views/Shared/Redirect.cshtml View File

@ -0,0 +1,11 @@
@model RedirectViewModel
<div class="redirect-page">
<div class="lead">
<h1>You are now being returned to the application</h1>
<p>Once complete, you may close this tab.</p>
</div>
</div>
<meta http-equiv="refresh" content="0;url=@Model.RedirectUrl" data-url="@Model.RedirectUrl">
<script src="~/js/signin-redirect.js"></script>

+ 0
- 54
src/Services/Identity/Identity.API/Views/Shared/_Layout-SPA.cshtml View File

@ -1,54 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="Content-Security-Policy" content="script-src 'unsafe-inline'; script-src-elem 'unsafe-inline'">
<title>eShopOnContainers - Identity</title>
<link rel="icon" type="image/x-icon" href="~/favicon.ico" />
<link rel="shortcut icon" type="image/x-icon" href="~/favicon.ico" />
<environment names="Development">
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
<link rel="stylesheet" href="~/css/site-spa.css" />
</environment>
<environment names="Staging,Production">
<link rel="stylesheet" href="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/css/bootstrap.min.css"
asp-fallback-href="~/lib/bootstrap/dist/css/bootstrap.min.css"
asp-fallback-test-class="sr-only" asp-fallback-test-property="position" asp-fallback-test-value="absolute" />
<link rel="stylesheet" href="~/css/site-spa.min.css" asp-append-version="true" />
</environment>
</head>
<body>
<div class="es-header">
<div class="container">
<article class="d-flex align-content-center justify-content-between">
<section>
<a asp-controller="home" asp-action="ReturnToOriginalApplication" asp-route-returnUrl="@ViewData["ReturnUrl"]">
<img class="es-header-brand" src="~/images/logo_color.svg">
</a>
</section>
</article>
</div>
</div>
<div class="content">
@RenderBody()
</div>
<footer class="footer">
<div class="container">
<article class="d-flex w-100 h-100 justify-content-between align-items-center">
<section>
<img class="footer-brand" src="~/images/logo.svg">
</section>
<section> © e-Shoponcontainers. All rights reserved </section>
</article>
</div>
</footer>
<script src="~/lib/jquery/jquery.js"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.js"></script>
@RenderSection("scripts", required: false)
</body>
</html>

+ 32
- 40
src/Services/Identity/Identity.API/Views/Shared/_Layout.cshtml View File

@ -1,56 +1,48 @@
<!DOCTYPE html> <!DOCTYPE html>
<html>
<html lang="en">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="Content-Security-Policy" content="script-src 'unsafe-inline'; script-src-elem 'unsafe-inline'">
<title>eShopOnContainers Identity</title>
<link rel="icon" type="image/x-icon" href="~/favicon.ico" />
<link rel="shortcut icon" type="image/x-icon" href="~/favicon.ico" />
<environment names="Development">
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
<link rel="stylesheet" href="~/css/site.css" />
</environment>
<environment names="Staging,Production">
<link rel="stylesheet" href="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/css/bootstrap.min.css"
asp-fallback-href="~/lib/bootstrap/dist/css/bootstrap.min.css"
asp-fallback-test-class="sr-only" asp-fallback-test-property="position" asp-fallback-test-value="absolute" />
<link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true" />
</environment>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no" />
<title>eShopOnContainers - Identity</title>
<link rel="icon" type="image/x-icon" href="favicon.png">
<link rel="shortcut icon" type="image/x-icon" href="~/favicon.png" />
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
<link rel="stylesheet" href="~/css/site.css" />
</head> </head>
<body> <body>
<div class="navbar navbar-inverse fixed-top es-header">
<div class="es-header">
<div class="container"> <div class="container">
<div class="row">
<div class="navbar-header col-sm-6 col-xs-8">
<a asp-controller="home" asp-action="ReturnToOriginalApplication" asp-route-returnUrl="@ViewData["ReturnUrl"]">
<div class="navbar-brand"></div>
</a>
</div>
</div>
<article class="d-flex align-content-center justify-content-between">
<section>
<img class="es-header-brand" src="~/images/logo_color.svg">
</section>
</article>
</div> </div>
</div> </div>
<div>
<div class="content">
@RenderBody() @RenderBody()
<br /><br />
<footer>
<div class="container">
<div class="row">
<div class="col-sm-6">
<br><div class="brand"></div>
</div>
<div class="col-sm-6">
<img class="text hidden-xs" src="~/images/main_footer_text.PNG" width="335" height="26" alt="footer text image" />
</div>
</div>
</div>
</footer>
</div> </div>
<footer class="footer">
<div class="container">
<article class="d-flex w-100 h-100 justify-content-between align-items-center">
<section>
<img class="footer-brand" src="~/images/logo.svg">
</section>
<section> © eShopOnContainers. All rights reserved </section>
</article>
</div>
</footer>
<script src="~/lib/jquery/dist/jquery.slim.min.js"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
<script src="~/lib/jquery/jquery.js"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.js"></script>
@RenderSection("scripts", required: false) @RenderSection("scripts", required: false)
</body> </body>
</html> </html>

+ 0
- 27
src/Services/Identity/Identity.API/Views/Shared/_LoginPartial.cshtml View File

@ -1,27 +0,0 @@
@using Microsoft.AspNetCore.Identity
@using Microsoft.eShopOnContainers.Services.Identity.API.Models
@inject SignInManager<ApplicationUser> SignInManager
@inject UserManager<ApplicationUser> UserManager
@if (SignInManager.IsSignedIn(User))
{
<form asp-area="" asp-controller="Account" asp-action="Logout" method="post" id="logoutForm" class="navbar-right">
<ul class="nav navbar-nav navbar-right">
<li>
<a asp-area="" asp-controller="Manage" asp-action="Index" title="Manage">Hello @UserManager.GetUserName(User)!</a>
</li>
<li>
<button type="submit" class="btn btn-link navbar-btn navbar-link">Log off</button>
</li>
</ul>
</form>
}
else
{
<ul class="nav navbar-nav navbar-right">
<li><a asp-area="" asp-controller="Account" asp-action="Register">Register</a></li>
<li><a asp-area="" asp-controller="Account" asp-action="Login">Log in</a></li>
</ul>
}

src/Services/Identity/Identity.API/Views/Consent/_ScopeListItem.cshtml → src/Services/Identity/Identity.API/Views/Shared/_ScopeListItem.cshtml View File

@ -1,4 +1,4 @@
@model Microsoft.eShopOnContainers.Services.Identity.API.Models.ConsentViewModels.ScopeViewModel
@model ScopeViewModel
<li class="list-group-item"> <li class="list-group-item">
<label> <label>
@ -12,8 +12,8 @@
@if (Model.Required) @if (Model.Required)
{ {
<input type="hidden" <input type="hidden"
name="ScopesConsented"
value="@Model.Value" />
name="ScopesConsented"
value="@Model.Value" />
} }
<strong>@Model.DisplayName</strong> <strong>@Model.DisplayName</strong>
@if (Model.Emphasize) @if (Model.Emphasize)

+ 0
- 14
src/Services/Identity/Identity.API/Views/Shared/_ValidationScriptsPartial.cshtml View File

@ -1,14 +0,0 @@
<environment names="Development">
<script src="~/lib/jquery-validation/dist/jquery.validate.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
</environment>
<environment names="Staging,Production">
<script src="https://ajax.aspnetcdn.com/ajax/jquery.validate/1.14.0/jquery.validate.min.js"
asp-fallback-src="~/lib/jquery-validation/dist/jquery.validate.min.js"
asp-fallback-test="window.jQuery && window.jQuery.validator">
</script>
<script src="https://ajax.aspnetcdn.com/ajax/jquery.validation.unobtrusive/3.2.6/jquery.validate.unobtrusive.min.js"
asp-fallback-src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"
asp-fallback-test="window.jQuery && window.jQuery.validator && window.jQuery.validator.unobtrusive">
</script>
</environment>

+ 1
- 1
src/Services/Identity/Identity.API/Views/Shared/_ValidationSummary.cshtml View File

@ -1,4 +1,4 @@
@if (ViewContext.ModelState.IsValid == false)
@if (ViewContext.ModelState.IsValid == false)
{ {
<div class="alert alert-danger"> <div class="alert alert-danger">
<strong>Error</strong> <strong>Error</strong>


+ 2
- 1
src/Services/Identity/Identity.API/Views/_ViewImports.cshtml View File

@ -1 +1,2 @@
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@using IdentityServerHost.Quickstart.UI
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

+ 1
- 1
src/Services/Identity/Identity.API/Views/_ViewStart.cshtml View File

@ -1,3 +1,3 @@
@{
@{
Layout = "_Layout"; Layout = "_Layout";
} }

+ 1
- 0
src/Services/Identity/Identity.API/keys/is-signing-key-10C452043E09C5A22FC8D97669868B8F.json View File

@ -0,0 +1 @@
{"Version":1,"Id":"10C452043E09C5A22FC8D97669868B8F","Created":"2022-11-30T12:03:40.1630635Z","Algorithm":"RS256","IsX509Certificate":false,"Data":"CfDJ8JmnWj_BpdBDujWYr1NmSawi1hoBHaFG7R1_Hu0pSObE9SiTNg1Wm83zVA3-oCwbntipelQt_gpZ3KH-NMBYdleYt4Gzhpvf4uchFNFzvdnx1X9jpWQi9WCmUm3cVNfzNG2eIKzrVLAoiaWLuDDG9XT-u2ojWIuKRKH7zEBMGrqCDQiVHZlBuqH_qzpWUQn-qKYEkFRcm05OmMYXJA0AquXimdOl1V_BcagZOpNVzG3C5t7lZTsTrm7FpD5zMdyZxL2VJJjRy5fUZbIQIjbQdTGYJaROhj-Sc4h2MgyrMNYJ-TQZKci_5ZoN-GJfjdaB-3UHDk_uFrVk9WpAYEWw0rw-I05tMu8vVxlE2jAnEGaVWEqS0f_Bz53MkDQb2YEleekTwaLJM1JZF791NTaG7oDXHSetSeo7UngHt3D9Ls2FHnZwV-B5mvzZju_-U7inwsoKXjZeqzQfQMs2IsIgiXJJkS20PObmosEbq7LkT97qRrinGicY0pA2zIcDmLiywYzWpsP6Rjw7epDaDlPu2J9BZ93Ni-slfTkNdn2SvAfE2dsJmV7Pz5tMEE-0mBlSMbcrs8TuVQayYPm2dm-sZLCv-iQu0rrvGcJ-lLPliby69du5oFU6WKpo52X4Du9tcM-5prBLWRVt_gcq16zBY_f8ieNnq6b0C_9chaztCevTQAouiSk7ni3zjJo3RuG1wPUts2tLT4sjY6IK2Zbhsvi-negegn91cFA0MzdOaD2Ca2GWN2L2kRrDWioVz_P4ztVFIU3SrgjW6stOGNreRvOB8txUCexJuUP0rb85pOa8rxPCkk1Iw8BjjRdirhvU3bvqhml6i2_iqNTAxpx89A6LRUuOYvYiwJersfhOF9F-FfOVGNxxfm1AKKnUBDfgLWDAw1r8PBeV6huAqUSIwaN5PcQJcdsfER9FeP9_CJqDgNGaL-psm2NQDaVV2Vx4m3GqWQhKMlk90zee5XNe1wobBS0XW_GQYieWWhH5t0pyzJi0QvpFyMAz1mcGXQs-TiHoU142FlFQojIu3_Ynf2RHOOByWDM9krMKMn6ZTnLWgBXnWtMefXqYNEhlACtP8lIXBxcbI2wFeBgRmaQA2DtEEjlymZ4RTaiQEuVC6B_wtTTVVFx3CL-TM62Xs-FOWTYafzKqGsHKzVWnB3fZqjDh4DNwb_A4mkfxoyb2UI9W4P7BJmbV5tOktYudVAFLt3XL7uNhBYLDSOjL_n8bixTg-sLUyO1H5-IxYQJciZdAro4yGO7r40FTYpZfTyvlr3RqFMDhBEPu_nSx9lPKrgpei0DmqYWw_cHJ3mPR1HDUE_i8Prlyzsyf9F-pYV4HMyupetXeXxMAIULnB52SE7uYfSUZHC25Ffo_tif8zMPo2ZKiMpc42BZ0cBptkPKUCvkR-Cq3pFnqXkKADWh1LLBP09hZtOjXqkFdsIFZq5KfRE6UVIyMJ91L-OSHOnggdjScFJzPlgEteUbydKSlPEBkicIs-uXXe8Tk8KZg2CfUU2WnziP0-VgymbTomKR5xz36kJqMjP2VYp6Xy-t3joQEqPE50ntT0AYUFJb6NZt5stHQz76-WecPnA6ZSA3aJec7NlLrxwM6BMGDyBZpbkgWtWFJdGWz6m9j4RKuyZBnyOXzYZU8FDNpDu6FVEymUkYxEhUnpIl7PZWuiZGADwj9-Bpzg0To8jMJ-jgx8w3RDzb5DoM6-jh0IJ5vecn8lSfRzHcOu48CLY5mTuHFr93YKiQCK06DS8VY3kI1b8irtdvmY-IvUCyCJlpzBYh8vFrS2PdJYsRVWuGJraxMiHYP_zH5r4Q4z27p6vt0Rbm0S4Fm1I9z4Hmaa4igF1mnktuTMizyxKWTsh3DjbsB_6W_lNU62RqJAt_IGr69MIphVY6ZOcbLx2I8p0z4yWpuYJey2BmRQPQeCZ-Tb9HtHESxTBFxupYul8619OJWA7RfLl-xv43X81OZnaxE9wLVFs5uEowKoEOzl_K5nxOw4igCiWYWufaFMMrZSu-0ni9RVSwytOPiEbGV0XOcVnB_lHDz1ymvDI4u1rfOVkehwB_wFBeRJRSEymzoD-bgRox-a1qLJNhOsdm9WUdZF3MHbmmA8NpyqU5qOTTqCvY-FXdJHL_OHmq1nDVHCJvcwLqUfPFMfFAi0MZQEhRndeQWYGdRGOhmvtwbRX-Hryz2HKXber1ixUvY3-XCxjzSyZdn845UzpKBdsgwAwY-LFzlgNQGEKHzsZUl8MSeqx3iO2QwDwC5eV4gNWBC5QBw0NAloBmdnQ84sSWydPViOYW1CinUQAnZsW2Z-0Xw0FP4fbS4HvulNBMIxE_mfJsHny4YZu_463zGR0SrV2hEFSiXuwSx27K6xSyPRB26__t8w37AeFYXtavqr9rB5GXnEJRQkW-i5lDnhtA93uRjfNzVhUpcTK-ROm3h7b4UrUTaK_IMLXxoYEjOFm8-cw1ZGHu3","DataProtected":true}

+ 1
- 0
src/Services/Identity/Identity.API/keys/is-signing-key-1417AE0BAF26F393609EF30FA355D06C.json View File

@ -0,0 +1 @@
{"Version":1,"Id":"1417AE0BAF26F393609EF30FA355D06C","Created":"2022-11-30T15:12:36.3392917Z","Algorithm":"RS256","IsX509Certificate":false,"Data":"CfDJ8Etya4tS5j5DiLTWt2Gh6S_0SY7Shff7x3C-zhStPviP80hTMRjXNVXCumUQnwoKrJ48OLZhHuU7wgSxqXaAT3hk35KvO_KOhz5pV_sWLPOogHC-hpQTIzSEbXzyfEIkliqVgW3AfpLbuDTZY4Hj8hu-OM0waYEjCQkIv-PrftxX7JU4KCT1rxZBUrDJN_mKCQEGWy0LGLVsLV8dKyLMSvJsIz3HJh4sngzist-XVaPxat2eMPqVOJEWkbUf6VqwJOnssR-ZOS9b63YZPhihUcz6J0T5Y2aWIyZfZwSkuh8DiL9_wzKSRTmplaY1tzeq_-FhbkgwtLX9VDbeesx_HGL_Pxj5SG5aprfZ7DN2FBr70m7olb-bEl8jOm0w7ot0QGwxHft53Nf9piLEyQp4hsReq-PNwCirzFNkQ5aM6Kwvwitkop_Ab8gpzfwiEw-uVyPN_gpkrt099MQrdtljc9gXHKuXr4321pgcbwO6Ua-amRcF_9uYSCVBdIhwmpaQAgMmI-0uI-5Ph7H23ZV2zR1epClkiR2t5RWOtXNgLqoupXyBjDWrse3al-CsdnkofmtY0bqr2ZdNd_EKFxqn7Gf5-dNuPJEboDUdwvjUpWYIzMlq6_3q9KP012SbimkqMgz3-3jDBw_xe43DvZd2CnWFTDQSuD0m7DTf2GVL0DlasQNYQgO1T7xN7hgScsXmzhJNUx7iz2ze5BMqWV3vw6P6ny4sj_QA4KAi0zxl7sPgPo2p-65MWlbZy646ZYlm-jDD9dfORdo9nmURg3ITYNVUREtVg4IcEzPVDBMnV-NEf3T3vHD1V49yVhovA9oAHj4uTye-bbLUIhoLPCZ5GfR8AEHE4IhL0JyjuaWilp1qNEXAhziwZUELenYAf74wEzMvSSobEWL5Hq-ApMdRePBjjeGrIobS6KuE7b3HVsYwJt9pWvmVCYZ-nQCDAOIFcgH5kifkHu6NEi23Jk7a5hmgq1kVJXr5xcli9B-bHu1mO1kl0gXsqJcGpfetcw8bEtwO8F_6kdkmW3RXLa3rvrnkigPJJtn4sm_3ee1dqFYarJlXHPugwuyzfEPXNiT1HCcDhQYlrYxfKSzmlhjthGn14W2ZYtb3YVCXUFReA8V20rs4ec7VTdjuKCPgA8bk3xKevr6aETF9I65hbI0pIFQ9_cJHkl9a3BZL-X5qSH2s3_rvPupVuP6mu-Y1pUEGp4p5XjZc0CqsLm-W6MsUKmBgeCiokmUGfs5P_Riv9ZuvlHyYcwj2qmgf4MDkmhThfisook1nrPomO1lByHMRHx8rwbVQQXHPKMI32WZxfpRQiuAERonMvLBd2P2UOutqe6t7qy_gbeM0WNEcKb08UowSxGUFnEH6Vk1Dx9Vzye2tw7ej-EsT39hudhe_59nd9IYvx1o547N1cI_ASDVn3ibVQU_BWLrSK-HIqGaA52F_hthPGpMjVBO8xtLXHiyxqI-gh9iLq0vpvMeFQjyLSmFjKQifMEVaATAF9h2TJTc0yqkIVN9y77LvkUDoFvi96zGSYDyIf6rtiF_E1GnAwu-6Le16bj9npI-FcNmh-DRIvA6LepbSXs27rVMjQdK6dOPDavu5Y9IM5bg0pndKek0orZzkbaFwxTxmM08AhKlyN6PPiIqcZ7i456FQDhqMTUEeBEShsOJy4573e0GA-J1sHvzVQYiKDlzvgYD-zh79OxnJXlq22BNrWsQ6pqXfXgB5UIvhUTrmcrUtv3uw6GcAyl76ySgfs3Hb9Mpk0aeUdI-xyMuxxJvz1w1NffcuXPsxf3VosZO8vkofjVSO8lGtzarScy-abFGD9UA-S7xGvz0rqRSAEnpxe4AZ5eBfs_VmTy21RYJqxPbrYWcjrRdQjxERhND70wiuLkF1pivUQ-bohP3w0EfV0fJoSoH9HthQ9_aY2ASJnv82BX_lPRdEgYVh1RYWziVGB_6WWkch8BLrUzXezJZcfQHGuEQYzVB17RQIhCqFo7GexmHkFB40punvvS1gUZOQLiqSu8UvJ27F5KNLpkgDy8q0pDWewRGmZ5J09aMlJE6e08u_gPrA9G52SWXvdkX8CRvPn-xOLsBfb1_84QBhB1PuSKeptNLjgMufTdfUkGrL_b3fTo4AgLz7VuKrAMNYjwCTLr4e6towu-JJ08Fr3c8igbWApP6yNiVbE1W1mX66jaRfyw3jiVAP2ytEH_0kLOE3BDFkepIopwGtkQ2ultqFHxr_-oSadSpP3g4dLt1GfQ7fjzYkYAkstXxgYgGGe7Q-mwQM1dR8RnE0jFvQnTKi-Oc_pj0h91HgRK_ASQrqrgOf9JN6McbVlE3VbEKvFfOKP8ukFa6i8n7tk1H0B8bX5iy4m_b3i3MbQmQ16gkZ2ECW29-bA4vWz6-R3LZ3kf2hHAJzv8U6cG71KLVgWSBxIyRJE-cSiBPxtM1hGT1qcJvpAVWvowkcO9Anng3lrfgnGaYh","DataProtected":true}

+ 1
- 0
src/Services/Identity/Identity.API/keys/is-signing-key-22178AEDB80CD0A41DF6874FE93F794D.json View File

@ -0,0 +1 @@
{"Version":1,"Id":"22178AEDB80CD0A41DF6874FE93F794D","Created":"2022-11-29T07:52:44.7571677Z","Algorithm":"RS256","IsX509Certificate":false,"Data":"CfDJ8E4RSuU_s2BGnQ_I0J1Xz9YiYIkBDFNHHGo4A20gtVeWqoNIQNxtP1p0JacgjeZkGl9NlE2-7qD02Vs9NIZ_IfOHW71XkfBIuIp5JvnwMi-X42Qw1F6UTQKNlYNvk1Fh27a6tM-mQgxysayCdDLlJWoRJYKDPDkUO03rZVkWDD2r6QNcMNtYhjM7oxy2yiw479PIeIldPOk73cPqp_k4oMPTRWwS04dX8fpQNA_3d2PoV1sw8-Hbmspiv3PO-umUGU2OFcN243cP9aaGXDoR-RgEbCn225WuHAWTe3tf39zVDtbAWDnRTNEX_jCNBItSaFOdMerDKD28PXTnWXuoMKyZ1_8ETZk0CDMAMVktqOatYrx0JHisG8vJbHtzxIvQrZL1YqoAN_qraVLgTl99WIZv56rYSb3RbNSzqB7yNn1Y_YAobXVbqJCaqqVG6uEaQxQ4WRI78sit630anIlKP7vccOtJH_bspQlPisNVrpCWiGo0D0IzNsZXLNPkc9kUbJKGc3xgE5H-z9kbk3sOecVH_iQy4b6fJ0IrDGz9fIhu2UI0-_lUE6sQ0g-NAA9kW68vD6PVSytiag7tFVV0olRPhaiBkSRuNGKjqsPmlPtkpzVxuTxSj19YxoC200cFARZq68qjxZ7ugdHQGlqd6bKFjj6NWCfG3Sbub1HpTkPnEUIR6ic8EfUrbu18MlOh-7bnIJkIuz0Nr5zZ9tPgiJCdGPjNo47z4i-kPWiNhS9k1sJqg60dgCT3onC2Uft4F4dnQpIKb28Fli9qkTDOg9gObEr9W19tMJvEPAhWJSl6HSmIJ-TElrW7nNCdS-H7PIlGWIvP6f0CCzrlgtsfMh8TK0B-Vpv5ky9D67L03IA7VfrhHss5mEkoz2s5-yefn2Wo5uMTxy53O4GIS8j3lF0ufUIBjGqR5q6IyEQUyU79OA5A7A3gJx78Cb-OEK3GixhYwjXYOw1YcguMZo_rGsq-EnjPoVBuZfdwyEIZ847gVwDzFsh0FHZIi2vVxXZ3tNGjJI2TXxNx7nyXuBxERgCPI3C5OeK2Uy8e3ooZCqPICKS9xEnICZteEcSYkRFmvPX6O1pZMKWoiyzGe-bhuBiotTB3yGIVlkhMyqNni2hQHoR5b1CX4YaGBMT_fdtW4uhvYLhLnGR69aal2ffIhiFJMfB_ByI8sALzgsn5jW-zOAmtiEOaPs1O9FqPf1x3Wk2YuoyujgHjHi9SSUz1hAJRmsyCmcYsMdGjMd-wnk83dsdMMeNANTKJHlZP5-__XA8emO-sloHrCiHpALX-JB5RQ1BGVD-dPiTlM0MYeTLdt7y5HRKSAbsOCnnF1vzV8y8NkUgpKVLTPRsJaoNYqik4Wpoa2d2ez-gXrG9S-pAByXHyTHiVDVNyqUTDMI0s8Fm_6_QwJTkgGEV6dR6r89Rzq70FFjdKB5RygRLhRdF2Be8blyExHI3HQg8_S9fcZ3zCybjatlNuNCgNZTTelC1UzWSEql3HqeqiMWIi9Kr8GNl59OIGm2lm9KNoyvcFHlQVQ33lw4akd5Z7aYu_HcdpBhMcEr9CuN6yQPhbCDOVhsMkqzXGzpwC2ezFnALAkbj9P44vRU9p0FEKFDNn_pQimVTLQJoU5Taf1n70CxYcPgM9BU3JPL9VUb9rDS3bHA7XQoO6878k6PkLbJ6GcfIJitaRZL5TkAUJUywH-a5xADVo-1_k3ADJYhOTbwxsYU96Kuhw8FOIToBH8CtLnqJLoJ8iwVPjB9LSsHPhmR7Rsi-x7Oi5JeugLctOvBn1sq7x_hHQKPVHA5LvtQ6Y719bOgPKLybQSCebe0ordUIsPAZMq-OvLj1sPS8PvfvZBDkyUA04avHm5kgT6RJF7ARUI1vMNLijm-_dxxrzY2bj6FHT340iFDURFePLdSM-Ctu3X-gLXKqPzQ6MBBQS0pyC3GkuX7J_GhPOZ-yXvRJmvIEoDAVEvjUQc57Ud6nyS_C_ljLmIoHghJiTJxNOOjR5pucb9Vljxm_BGPZwMi6-bTbD454ESbZ1kyvOAG73PjnPoQSaEYyM3XLW3XgJ9R4dnYDk31zHpQebodqwcFsXzxV43yi7zPrJSMzaFnC3-uPQ7qH4xs9puX5RL4q4lSCUKAB82TMtCJ0xHwj4DYqsJQt9jUAz1IZbgbChq8d0EPe6y2yg38LZs50r6DD0f-VgF1fyup_1ORM1Ub6gRyWxIBZLyLFY6uLvzPhVQaBsDitK2ku-9ELv9frr0j-BPNNCBpNRQyJCbZwOcovJ3gn9w1TAJ4cXGxzJWRGVnJY0e_tJ5ZMwRrn4E6LwhKXK9N2lBU7-2RQxEU79sTRz12SV9v8Az51MlUHzjWZp8p2bttpkQKMeoIkIVu31t88Dn7MOZkwR5DQ8b1_tNV_3Nbqyc9OUUFTSrpfEJeU6VRrUilgvBq78lSov4q1tbKxp4OjhxdNv9NxfYCL11kTZVB_h","DataProtected":true}

+ 1
- 0
src/Services/Identity/Identity.API/keys/is-signing-key-870FA7120249C21C30ADE458B07918C1.json View File

@ -0,0 +1 @@
{"Version":1,"Id":"870FA7120249C21C30ADE458B07918C1","Created":"2022-11-30T09:59:58.1299062Z","Algorithm":"RS256","IsX509Certificate":false,"Data":"CfDJ8BCNHSqqa79Fqw5EoSNNrOMZDqq5p81RZsuqUc9iuvF_3nm9RHjc2lAsu-T8AjFPxhbWkSNnQAU7JGWpNh_TU1XK9-Skieb68wO2lUFauCBnoNf7R7JEpuw7zdjkInQZFOF1hBzhL0qKszqhM8-2gz2Q2V5c8Ng0C_2CzHNqCsyhTHBD8NgXfzpxdym7V8HZSS4SkJ3QpKLH-VMXRFssCM7PXEimNWBN2yky4IW5_fRgYgRkkCaXCU43ayGxLyBe_J6DzZ1fefO7ZBne2b8HaVVQaAEQDTlsFUxNqDLcAO-k6q3FoL9ZW2gm82a8ra1mSJTbEeAacNK5oE22urnovVF1GPwTVnnXMi_9eloBqyTM5xtTQPYbVlOkZl5pgRjKOY_Mc62Ix9p9hoH-gCfDHw2Q9X34JT1ymzBdYqtgTmWoY548-ZkX6v3AEpkhmsMdKMyNSdz-SJfasTqKkp4JkqGR-gXW17HOtxHmaOPg0ZRuQ4Dx_YHuQr98UQPMUktYL_kF_76FevNyfy5eyN6eawHNGwhXzn0YdyCjaRV1LXfD-4LqqJBhExUSyCtG0Pq0qjZ0ySAuKi72-0532XNRJ1gh7CxlfRdeLDID4ffs1cgmfODFv1k1JElYihqqGwXMwx-SfgpM9eo4j6jqd0kdAgj3n4ZsJKQMCDOGv84Gfhnw7dxlIL0e-D0sKKc6f7KagNFJCbt0LZ492IWzIPIGp5lXQwhrDPECjdNvBv0jhC6yMzZQZlVQXX6MYue_qzXb45TK23j8HjlgYPCip9eqH1NBA1MWkHykA6ZkdZ29eCCMuO-m8zB_Z_7YdEdzGcViK25T2q8qo_0BMzAzdrEJ3rOYOly7wzsoESn6aKr2U1b4qh-fljOwQrYqoSzuiu5l4QY1qVG4ZR3VMjV2lo3LEbMayA6Sis38LQy_ulUKGdOi4KeyNSlbL44wN48gDM86kNPkTM2kVoJc6RRZ5zptnGyXgAsuknHaF5EX48Ihxt2AFU_pVgKGnrx4XkYkxMEwbInoL19WgaKZyLAXjwtUG3FSOPdWswUqbcDNoetfY21Y9yWZsZ0rUTUa3x9rLzRP-H9NGbK_Yf8j75lpdx2sFUvJyyxZ2KEoq74bS-Zg203Q3jhz_RVJi8M3R0oRQHh2I-wUimdAHQ9JQu-M-VAtVLG6DVrY4vyhdv2JxIPyLrRTJPVhMDEqKqUcbm0VJE8MJOb3C6hdFrrzcM8AAkxGhYHh2vlJu0MAa8y7U0lHm8mcrNqsjPahAFvVMwUPkxaXFSYwJDdnBfODquWHt6reE9GwQw-ZFvigPGmY3SWMZiEBK-2Zkz9NglOKEgnojT6xcAbdaeEDadvY-Qriej8rMBZ3F-2IHYNMM4J2MkbexgloQgS6UWx7Nx1ln-pI-uHCDUmwsb5bl05Jma4_DXMsIjzinc2u-CvpEUXe1KmDddh7NYSrUv5RjaZfxgzjYCW3gSynFRGNfy57PaX7R7VV9pn3IRMKsbdWKlA957M2tDqmgkzC2M8KZAJRFmw_tCVk-swjPjyFmXUgsdLs0oX8OSAh4JX8DWvK42wHerh4gbjX_N7mYWh38e4-jMvyZ5Zdqf-2phj21z9uZla1mCsncjDDPJy7sVCrlJasgPLgtCD5j0lxp3wdAjvx2I4q6CKNcP8-MapHABe2TIgMmEf2VOJv42yrO99q49OwkopbyhMf-0-drAOo7Tw7eKHH7S174LfEKewCMmj2BVaM_I-rTgU28Be8pfbl6jTmj-9WCvuBbBiCtvhpeOApuMbJkc43As4QV031W9DEQpB520DWUdAHjvNkUMo0QDBLm7Zx4xzeO3Ei35Xw8_w9OjZBCZw3kalIbZAL9MlsyfFXczbZXMZnzkccPTwTK2SgS5lq1Ic07YB_uqY52CwYlpjbRP3ygM2mVCLTB921NhXKh9-Md4oJ9ieaDUNyNPch2MLQluKmyIH0sumgBkyn1k17IM4qN3EXRSsUiqa-FBU-PFa1gVIf_dhdnLI_GOPGEYBmZYTrk8J8HK2-wIi5shGDOB-WjBaAWbymdMmPhifRKiDKdOmPVArGptSyrM_5UvHF189Jl0ABxEWcsW6DScgw72GtcXxhDQligdcjanZHNVGEnfwFN604v68G42IC7clOVAno7cgaapHXXYSadEqVzP_Q4d-d-obG0v44yMu3WSdfhmJ8GC-8jS3b16q5_vTfyim5VFmLdG0PX1L9--QYop1Rns-64a5Dz8T8MmyrtgRXBAMfXC84R7N8I7SgRbsSo2pgMzRafCOpYWqfUlNcPjqQXSPj8oEt0iz0DBq9ui1dK0JiiOGOO9uOdswQP2r_cVlF-QvTszcyy1hn6QHCdsfqojSi70jmyhpBa5Vgba7nyP_qbJCtYiZwk7RLWT-jZ47BwZzEK73UXoAYCc8NT5HsIW8KFuManq8Mau7BLNj6WhXJOKmbP0oD88LMj3TeEvZ3","DataProtected":true}

+ 1
- 0
src/Services/Identity/Identity.API/keys/is-signing-key-B0B34AEB91E2638F61EB9A5543F3940F.json View File

@ -0,0 +1 @@
{"Version":1,"Id":"B0B34AEB91E2638F61EB9A5543F3940F","Created":"2022-11-30T11:21:15.586844Z","Algorithm":"RS256","IsX509Certificate":false,"Data":"CfDJ8Fzd_R2KZDdMrJulCrd4GFTnFtf9LeKAfSemG0-9e385u01WZOrLNU5PPCNSRvcQKMERaUvLDcAsYIsqdKPJ14QGDgSTlxggwrR50kCv3494z5uCvWOvWqYc3KKzhR-7e-mXViY4m2oYHpGaVCITKXYwCRYxO-P0mSdr6D7W-846cfdst47_rjIxxeMajL1suYSYe_sz_nz8ooKW8ZH_kl5M9cI0waB2XFviIP3f08pz_eyneKqy7tCc3ruSNiUzfUBAF7fBHDcyPrE2h4AKaXG1PT-I6JOFVj9lAgv_1ACSqTVEjJiRWfFIfft3sQ-chSv1yxy7b8GvhIsKKEn-OqXCnYr5pFW7VPagt7pf9IQdivU7I4m4zSyW0bjIWXdCL3sXQAChiDpvRCFqFfsiH4lWmI4X8Aq8Tz1_LkbicHW5-ZxeZpvgPlg6t77xqFIxFtixqdc1XxifVwJR5GwrFcbcI9VpnlBomkBSUFMslW9NxGwSztgnYatkQuiXTzdqFoOr5d4YGPEHe_Oc3h9yDyUxgBdJK30sSx1Dwki94CeQNBS7PisGbu5q60MbsyFQ2S6kBogReQdURLW326NkgOBJ27jHk3ccx24WfFuuDzquE7xetxvnX4AV0oKOudMClsOv4Wr1aMm98p4VimUQkAneFImQszK1-zSQfxeElamoYy-4h1P2V_A5siSmfKpDs1q2ja3C62lBr0YSx822O-QF4XI0id6GOZiCc_9v52B_teTlgMq6L54EjqDM-h6ERkPvoqNFL0fWz3ktOBPCQm56YrSyyaXNKG2TwL4AMLsaQ9us5SXnJ7UpiY_7ls4SVm_h3btoQcokOiO247mYo4-BRq4pCsFHNBsCReiKlJH8G8fwrujWcu_U7NdVWgmUgmcsIh9mfommuOdBdVQdP8CYZM8od1NBIIifIoj51WuZXtb9Wr4QVIddEjAQrYnexJAozF9TeBVfjhDkIIA2-b8yBGqNl5VrF4bo3b3LLm1rbq5vzfKN6pKzs2sDGple6kZn_t0Ym5fmf8iWXLzMnRyasEDuXX0-Vg9WxiWyXzHsUY-SZEfEXs_WfuzBZb9NP1JvrB_w33cUfgl9Sa6RD56O5F1YAR3WxiCiWGnlSnqpaiiyTb4NVr8N0qQl9xOz9KCSaN8H2WKgCML2DdjW5_O8Zea2EPpSf1NGB1G-Nm-9HWAn8xDAei5x8kzTIxka6Jb8GO6Rdffk7wwuYMoB4ZYuiuL1q-rqvaU5GffQWMjGz-yH-WCivVFCosvJgk9340K0bDl97CULs43LF3nQFcmuLQoW-fasNYk5ZAXYA8bjn9eYm-hWbsfA402c3iZKKZVcLAU-jPAr44Z1P9PbT-xwooGTKKnZuR55l24T3p4hDatpazlObRd27kPtSLgbXPkNKbru9N8--JUhoEeFGC8E01gCn9256wlpIO1hk9a-P87t7zJWdYkl9Uo8Poz_Hfm9Uaml55BWSghfXlB99hULUmpnPwyOVJnRhGOrnr6ys_2l6bCW-gZlWS7oevQ_BuJBcCXVuRYQPbVtH9ZZSKF-S-uczbtfzhJfH29tCNsHzAukVHFiIwHqzhLHNu1gX5COGzYdN57p7DXGkJXGJUipczm2ZAfI9g-Kgvy6MaXScuwaobPobL5AH5WG1CclEb8kg_08rnQsDfvNhmsAJE7dN1GjpA46fP2bJ09T4hfNdmW-IZbeQaDMGc_8RpoADdUY81swXUgrL56UxoCFxqifXtskbzxF5Z1ad4qRYNlqU_zMbxjSZTsSizNja4pmsx1O2NAhvN31bSoA8PWpLLz37Lq-craDpXW6-mIySm3NQWxTLx2hsk4i29-dD6AjUX2aDLEng4JLKLWKLcH_ulkujNP0oXdkXcGikLqFwUKG8_lEWV_ROlXUVlmwNf9WaohIX8caCK2CF_tyEkCxEjZIBzw5xazq7_zNbI66rXfqBHxDKqrdM3UJYBNFMk2LLyEGD4TetGu4RzBDt2r4rXatSXQh9a662LYlCXxlElP8xfH7BudBa9KgV0FQOdHQ4sm9chHiT9JPQX6bHwf_imYHmTyMV3cIN7f7bACLji-5hFf7if8ANbhY7zer9-rQWR9Wy-vYUyUaSWCWRXHQC2eXL0XklBNnv3vK1nFOcaeC5R0GGULfMnBluyClu2Z67OCsAzQqi-_aacxQVN91SNmAfUbVCrPgBTbQY_1u64fEMc8ZcL52xwT0qgCSvBPCNDEJ8YDVVd8L8eLX0hg7rV8SgCnAl5qG74BCdkmhCMLB-OiwGZxZ9s94yGdjX9jA4D70uOgn5t9OhpboX3OoRK40Xged6zCBEhUd9MilD9o-BtEPeAHloKqMrvrVDE4l9R739pjhgOfvXqGIytXD-bKlMZAY7dnJ4vu4GjVvhHfzxTV3zu_nAV8j3n41_knCe2RyCm9j74YZejnRC15ADqItTDmTre8_iS6hTOrVO9FP","DataProtected":true}

+ 1
- 0
src/Services/Identity/Identity.API/keys/is-signing-key-CFE19FEED1112F0740C7CEAAE490834F.json View File

@ -0,0 +1 @@
{"Version":1,"Id":"CFE19FEED1112F0740C7CEAAE490834F","Created":"2022-11-29T07:38:58.7490696Z","Algorithm":"RS256","IsX509Certificate":false,"Data":"CfDJ8PyEV1YRvftHv5uH-VZQ5M7m2ypM-2X7o3A9EvjXNQQsFD50DwqX3UR8e4cDEITQrBowxyPPRXo_bTgEnifJjx1kdzA3q0L-FPJqCOjh6AaRqUg_TlnhvbEgG1HYoBZTAwvhi6g3h86RFPU4juwhOWHcD1vzCgW1vfJ9VFXXq3F-FSfbpdykS5bLn5hUdg3zgNWh7zZXOIFxgNkelR7lGMfF6hquTQMbCRhtDg0-N8O8m7M0JaNsB6_H6v-Z2RlYzH0R0B0Ka2fH3uPnudPrd1RIP4BONzMorJpDFZP9TF0DY9sJhkcHEhKDZlRlwI_Z-e0z8WeW183Gg_HSV23wO4B5R8ldXRSioqWyUuQfoYplUanuNeccblBe7hxQw9fhsanHGgbiuBjuypXarZmlVlbFO2zPKl46Vj8YZTxopOFOi4ALcNlUzGgPG5lwPjDfXEyAp7_ipMIW84UVfwqnPZFE1FASpFx1BPif-huoAaXw2GF4HJ2QVqfxkr_Gn8isrzBOT3fqmeZ1n0HNOc8VxCSdScbBSKFMnxNSgPQSexHGSKnXpvXF0OL6R8oqHoi8TNfYFitGE_qED6wCjQ77qMXOUVXB18kW7nJYqph2u1BH1PykRWaWDlnh3H7fKWRhO0ejaTJWl7oSLllYzi99yL5JvudDCjI0QdfbMz7o3SrdrZbIKQcbTPPgl_cfNneP4QA6MSlJFbG3bc3q8IUPhISrZHO_s7SsoX0BiP-AHcA7M_WrkAQYLkTMsBuO3x6N-bWjtTouqzHY7-h5ZYHqvk7Mg-jlgJjFjgKRDOJFiDorbf-5nvxjl3eLV8rjX7ML9KqjD-ZUjMsDt79lpjCi6B2EFqbiGT35I67hC6a7l8gPjwLKB7Oc8h4YggwyS1eLCq3CgmfMqlIAeAqLL7nVwDPReW-pirHix92q2tSkKCuz5UV5jphmDAnRFQmICaIinoSsBtjb1RlonM-yklDO-jdR5r0ZEFb8ZCssSojhfqEi5mcK3xJsyzM_Inzg71zAHCUl5cuBhbWekBeHiqb2p5ar9guCK_h7zmga1ZZTqfWAGzxHrHkHb6XiK_wLlwBjJEoGhgHfi0UzUECddK2DVwEuTsXhNDJ8CgB2oWNhIHgnlNxL_JA6_bY8BnGtcOKnEuaKpR6rXEZit2Jcr-1-Q8YJ29tVNubphYm2ZMrDTGdAVE8ODftey6BIN_HmBELf6a7x-Cd5DPrW46iVqlOuBqmjxjZ4HHTS08HHGSD4_xj2vNaWl8rIbE4BbDq9V_qyP548KLNbGqSIed38DDcbsRMIuac8po95Z3q6NUf2JR0mCOEizdrszZOW9sGVlO-WReaTkpNN8MaKYoUy6k84IxwPPv-Tov0yMdTN-jp7H2HaTOGDvEb72swcW39huoAbXwjZdrpFQjTD9_cnNrVdclDGN_yix_i8-vQDQlQ9Ronpk2NvAdnrmEzQ6w7D7PqAe5c4YJfn_0z45ERm9zBwCMCbw8lP_Yi3nwTbqGtFqAoXTSZ8vR5_UxQLqxsJy6U6gK4Es68-hnD-YR5p3CzBXxSEgV7_qvW2gly5VCB-1yoD8dTVInogMhOzDZD71gmpJURgKXX9WN92yq-YB24E8koOXdbxTvSblIVTxwDqQ7lb81y68HmZVSRLk3zhxbkEkjI1kkB8uzlArbXljN7LnxyXHU81DDeufVK5_myoIMA5NpSwEKxAkmCoF7p642qBwo90k0q0AvJVmoy8xpCRiJyLDR8wYHFsed3T99jvvvV7GyA4SG5Tt-ST5kKFKvOOwT_WrOg-ao5cz6Br8_YL2xW5dB231R4ZxfJlmrJY9WcuTQMZrWnYOOSj_5u2I4sgcnp7nrRF1l2xnieiym5ef23a-rSMBZi_leN6BAma6N1M7tu1N8cEw7wd_6FV6hqrcKFPGNm4HE5M-pmoWvzjBTo25rh_4atG5uEmDgmhmY6qiZc1eso__Fz29uJ4792kkpdGyPAcV8igzPMdKts1KiO9ThrDRH25Knwxa1tOWwezkeId-OuZRwC4LhGuZseww5TxzQtyN8mMWbLDPCmWya56F-i0jag4i-r2Z26VEmhpSywbACetljEsaVDJF2ZdIrIFJqjYLa9zhcKbD9GZPHZt0HKilXDp8DomCQHHWsIapwCiG6WLGOhVc0-0VJ28dBqIowyCQScefePKIIk7Oa0T4UzPXcXiXYNQktHGs9VrF2ux4De-SbxDRdJy1IRSH8_NQ4SFtUelO8IG27B_8Ywbonn9yRsoojmuS-fXPuWSJMHDjPcSYwJqQyxjfPjGCLsnQWRZCNT5Ft6cfcSDRZWoCqKK2qLfqcGs-FR71HM0G6ipAuVI_2IyfalS-fXqTPCncyFsOqp4BRhhxLFu8UxTZ5-HQyKH3fX75mZCD21d9RObyWv8g2T0HtEBLEf2eR8I8zCUcOf98asCoT2CU4AtOhVwqrE2u1qNKupc5Nvw","DataProtected":true}

+ 1
- 0
src/Services/Identity/Identity.API/tempkey.jwk View File

@ -0,0 +1 @@
{"AdditionalData":{},"Alg":"RS256","Crv":null,"D":"Pf7_o1LMYwcXEWHkZ54kP4ItOYu0lhOOn4vc88XYg9XmDR8DcPxyvRLWGIuV-jSioMP59HIN5yHeDrQbG3tY2lTUynRYHdwOFVQAzQ3LpGSF4eKfMOSDU05HkWv-w2E1XWogVFAafv2F4FHeYtqWAzjjcjlYy_n0GkCcjBvIneuVqWNZQ9-eBt28cApruVfTeoE01Liz-ww4T0CQ9ujJQf9zUxhYb6WWMQ1U-A0qGK9hmh00Ymf-rxS2HuZ9o8Bps9YEvy7rCBfy6nSMB8T2I2SNCHDdlR8Oi-j_l0VQyidnfnRY0_Lio8uSKDTiUL_vBKyCREAfCEHM6TCpdixR-Q","DP":"CR-Lyp15RyQV_TmxXq2EX8mntgdnT6ROePrkNlRCSXH-AAVJQ-24OlBH0qzehzMWI9Yk72bhqZdBIC1W8SGsTxf6T3xp-MwINJwHgYftX-D9RDQCYS5o6_gFW1775Of43Zv8tsjjwwkxUKHiwH8ImbEyqv2qVhdb_yMlgFwtXCc","DQ":"ulVv9t13HB8K8jKLomSoINMaFtNGN0LS6wY93JKtwGCjcvV5lwXRiHhq0AbDOrJggg-zZVedlbXpWRokMI-hLTHc9TguYEfiJ11DvgldTB5MUfMxHau0L8ofS5YAuTSEgg4EPIrWgvVEF1ljHwLQtDSBB3CvI6UHF7rXXy9cwIk","E":"AQAB","K":null,"KeyId":"F7383A0ED7CD960EF473FD2851803938","Kid":"F7383A0ED7CD960EF473FD2851803938","Kty":"RSA","N":"p3jTfCB0YsKpqJ6CNJi0tVmFBmoyI_D7QLLsbB-TCZ3-HIXDEr_k6zKb2GJ_QP7mncdSnYpJWSv7fWPfM0bL3A6NaMLF9MDjbfD5ti9irEW1dzBvIK0YjWmfks3eq6Mb2mM6PZtNEnoCqEzjgcRkDR1vtClEzUjs1E_i7TB-Y0J_aTYpLf-eN7yA1Obu8zMVRSSVBIwG5W5jljzA2nxk2u9qeDq8Sn0qgGwbX8cyYGQoVWBOPx7zap4cNcL6dHILjnlVrHqAUW9NVXtBWlVDP1Gvnm2zhCVJT_gW1twNhswyFULGVQH1ZWI0NukEqHG6LpN8Ti7Hx-K8MEEv_vQBYw","Oth":null,"P":"1XZw-ufz_e5fWroi8naLluW5Ow1f-Ems8oG1WYUJkc1Q4vZWklLRqlEsDH48gqk0dEgXLE9tz0dDTo03tAPJmR1InFpusyLp2XPPJ4XLhWFCMDDb0xr4oayV1XDOi3U9eKERtcASo1IDo8zDEuY8NHFdJlNKpfJ7P0JSB0hEwoc","Q":"yNg4tO9PVrwQ0CaouP0jniFouHAEfZMyJ0vVM1xPVim7jeKysOJi79lHO7OWNSABB3hY8dLcMSiKxS3BaU0Q2ns9Gw-NLR6eqT0BN9Nzh-a0sa0iuPUWCPgsWdrqjIf4FBX0Ra-6q7_LxuWCncehUAtoLFWMkcOrEj03Gwz1lUU","QI":"UWdYA2qY622GL97_tPvrgk1VWSqoO2a0-TlGu0gK51T_Z1hqLwzG9QpTujoxnRU7Js_QUe_9cIEO72N0qliEm6A-MClM-5LUA9XleyAosb1cblLjAPqT9gfGbRRAbB3Aj3YwsgASGUiSpGVfaUuXn-OnuLoidZQXw7w9xcPuxMo","Use":null,"X":null,"X5t":null,"X5tS256":null,"X5u":null,"Y":null,"KeySize":2048,"HasPrivateKey":true,"CryptoProviderFactory":{"CryptoProviderCache":{},"CustomCryptoProvider":null,"CacheSignatureProviders":true,"SignatureProviderObjectPoolCacheSize":16}}

+ 1
- 0
src/Services/Identity/Identity.API/wwwroot/js/signin-redirect.js View File

@ -0,0 +1 @@
window.location.href = document.querySelector("meta[http-equiv=refresh]").getAttribute("data-url");

+ 6
- 0
src/Services/Identity/Identity.API/wwwroot/js/signout-redirect.js View File

@ -0,0 +1,6 @@
window.addEventListener("load", function () {
var a = document.querySelector("a.PostLogoutRedirectUri");
if (a) {
window.location = a.href;
}
});

+ 1
- 1
src/Services/Ordering/Ordering.API/Program.cs View File

@ -68,7 +68,7 @@ Serilog.ILogger CreateSerilogLogger(IConfiguration configuration)
.Enrich.FromLogContext() .Enrich.FromLogContext()
.WriteTo.Console() .WriteTo.Console()
.WriteTo.Seq(string.IsNullOrWhiteSpace(seqServerUrl) ? "http://seq" : seqServerUrl) .WriteTo.Seq(string.IsNullOrWhiteSpace(seqServerUrl) ? "http://seq" : seqServerUrl)
.WriteTo.Http(string.IsNullOrWhiteSpace(logstashUrl) ? "http://logstash:8080" : logstashUrl)
.WriteTo.Http(string.IsNullOrWhiteSpace(logstashUrl) ? "http://logstash:8080" : logstashUrl,null)
.ReadFrom.Configuration(configuration) .ReadFrom.Configuration(configuration)
.CreateLogger(); .CreateLogger();
} }


+ 12
- 0
src/Services/Ordering/Ordering.API/Startup.cs View File

@ -390,4 +390,16 @@ static class CustomExtensionsMethods
return services; return services;
} }
public static IServiceCollection AddCustomAuthorization(this IServiceCollection services, IConfiguration configuration)
{
services.AddAuthorization(options =>
{
options.AddPolicy("ApiScope", policy =>
{
policy.RequireAuthenticatedUser();
policy.RequireClaim("scope", "ordering");
});
});
return services;
}
} }

+ 1
- 1
src/Web/WebMVC/Controllers/AccountController.cs View File

@ -1,6 +1,6 @@
namespace Microsoft.eShopOnContainers.WebMVC.Controllers; namespace Microsoft.eShopOnContainers.WebMVC.Controllers;
[Authorize(AuthenticationSchemes = OpenIdConnectDefaults.AuthenticationScheme)]
[Authorize]
public class AccountController : Controller public class AccountController : Controller
{ {
private readonly ILogger<AccountController> _logger; private readonly ILogger<AccountController> _logger;


+ 1
- 1
src/Web/WebMVC/Controllers/CartController.cs View File

@ -1,6 +1,6 @@
namespace Microsoft.eShopOnContainers.WebMVC.Controllers; namespace Microsoft.eShopOnContainers.WebMVC.Controllers;
[Authorize(AuthenticationSchemes = OpenIdConnectDefaults.AuthenticationScheme)]
[Authorize]
public class CartController : Controller public class CartController : Controller
{ {
private readonly IBasketService _basketSvc; private readonly IBasketService _basketSvc;


Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save