implement Duende.IdentityServer 6.2.0
This commit is contained in:
parent
c10fff488c
commit
b90d2d1cd4
@ -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",
|
||||||
|
@ -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
|
||||||
{
|
{
|
||||||
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Binary file not shown.
@ -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"]}/" },
|
PostLogoutRedirectUris = { $"{configuration["SpaClient"]}/" },
|
||||||
AllowedCorsOrigins = { $"{clientsUrl["Spa"]}" },
|
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" },
|
RedirectUris = { $"{configuration["BasketApiClient"]}/swagger/oauth2-redirect.html" },
|
||||||
PostLogoutRedirectUris = { $"{clientsUrl["BasketApi"]}/swagger/" },
|
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" },
|
RedirectUris = { $"{configuration["OrderingApiClient"]}/swagger/oauth2-redirect.html" },
|
||||||
PostLogoutRedirectUris = { $"{clientsUrl["OrderingApi"]}/swagger/" },
|
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" },
|
RedirectUris = { $"{configuration["MobileShoppingAggClient"]}/swagger/oauth2-redirect.html" },
|
||||||
PostLogoutRedirectUris = { $"{clientsUrl["MobileShoppingAgg"]}/swagger/" },
|
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" },
|
RedirectUris = { $"{configuration["WebShoppingAggClient"]}/swagger/oauth2-redirect.html" },
|
||||||
PostLogoutRedirectUris = { $"{clientsUrl["WebShoppingAgg"]}/swagger/" },
|
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" },
|
RedirectUris = { $"{configuration["WebhooksApiClient"]}/swagger/oauth2-redirect.html" },
|
||||||
PostLogoutRedirectUris = { $"{clientsUrl["WebhooksApi"]}/swagger/" },
|
PostLogoutRedirectUris = { $"{configuration["WebhooksApiClient"]}/swagger/" },
|
||||||
|
|
||||||
AllowedScopes =
|
AllowedScopes =
|
||||||
{
|
{
|
||||||
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,5 +1,5 @@
|
|||||||
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)
|
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
|
||||||
@ -15,4 +15,3 @@
|
|||||||
// Add your customizations after calling base.OnModelCreating(builder);
|
// Add your customizations after calling base.OnModelCreating(builder);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@ -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); ;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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 =>
|
@ -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
|
||||||
{
|
{
|
@ -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 =>
|
@ -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 });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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;
|
||||||
global using Duende.IdentityServer.EntityFramework.Mappers;
|
global using Duende.IdentityServer.Configuration;
|
||||||
global using Duende.IdentityServer.EntityFramework.Options;
|
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;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -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" />
|
||||||
@ -56,17 +53,24 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<EmbeddedResource Include="Certificate\idsrv3test.pfx" />
|
<None Include="Views\Account\AccessDenied.cshtml" />
|
||||||
</ItemGroup>
|
<None Include="Views\Account\LoggedOut.cshtml" />
|
||||||
|
<None Include="Views\Account\Login.cshtml" />
|
||||||
<ItemGroup>
|
<None Include="Views\Account\Logout.cshtml" />
|
||||||
<None Update="Setup\*">
|
<None Include="Views\Consent\Index.cshtml" />
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<None Include="Views\Device\Success.cshtml" />
|
||||||
</None>
|
<None Include="Views\Device\UserCodeCapture.cshtml" />
|
||||||
</ItemGroup>
|
<None Include="Views\Device\UserCodeConfirmation.cshtml" />
|
||||||
|
<None Include="Views\Diagnostics\Index.cshtml" />
|
||||||
<ItemGroup>
|
<None Include="Views\Grants\Index.cshtml" />
|
||||||
<Folder Include="Extensions\" />
|
<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>
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,71 +1,75 @@
|
|||||||
string Namespace = typeof(Startup).Namespace;
|
var appName = "Identity.API";
|
||||||
string AppName = Namespace.Substring(Namespace.LastIndexOf('.', Namespace.LastIndexOf('.') - 1) + 1);
|
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);
|
app.Logger.LogInformation("Seeding database ({ApplicationName})...", appName);
|
||||||
var host = BuildWebHost(configuration, args);
|
|
||||||
|
|
||||||
Log.Information("Applying migrations ({ApplicationContext})...", AppName);
|
// Apply database migration automatically. Note that this approach is not
|
||||||
host.MigrateDbContext<PersistedGrantDbContext>((_, __) => { })
|
// recommended for production scenarios. Consider generating SQL scripts from
|
||||||
.MigrateDbContext<ApplicationDbContext>((context, services) =>
|
// migrations instead.
|
||||||
|
using (var scope = app.Services.CreateScope())
|
||||||
{
|
{
|
||||||
var env = services.GetService<IWebHostEnvironment>();
|
await SeedData.EnsureSeedData(scope, app.Configuration, app.Logger);
|
||||||
var logger = services.GetService<ILogger<ApplicationDbContextSeed>>();
|
}
|
||||||
var settings = services.GetService<IOptions<AppSettings>>();
|
|
||||||
|
|
||||||
new ApplicationDbContextSeed()
|
app.Logger.LogInformation("Starting web host ({ApplicationName})...", appName);
|
||||||
.SeedAsync(context, env, logger, settings)
|
app.Run();
|
||||||
.Wait();
|
|
||||||
})
|
|
||||||
.MigrateDbContext<ConfigurationDbContext>((context, services) =>
|
|
||||||
{
|
|
||||||
new ConfigurationDbContextSeed()
|
|
||||||
.SeedAsync(context, configuration)
|
|
||||||
.Wait();
|
|
||||||
});
|
|
||||||
|
|
||||||
Log.Information("Starting web host ({ApplicationContext})...", AppName);
|
|
||||||
host.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();
|
Serilog.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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
IConfiguration GetConfiguration()
|
IConfiguration GetConfiguration()
|
||||||
|
117
src/Services/Identity/Identity.API/ProgramExtensions.cs
Normal file
117
src/Services/Identity/Identity.API/ProgramExtensions.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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";
|
||||||
|
}
|
@ -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 } });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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; }
|
||||||
|
}
|
@ -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; }
|
||||||
|
}
|
@ -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; }
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
@ -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; }
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
@ -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; }
|
||||||
|
}
|
@ -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
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -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; }
|
||||||
|
}
|
@ -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";
|
||||||
|
}
|
@ -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; }
|
||||||
|
}
|
@ -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; }
|
||||||
|
}
|
@ -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; }
|
||||||
|
}
|
@ -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; }
|
||||||
|
}
|
@ -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; }
|
||||||
|
}
|
@ -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
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -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
src/Services/Identity/Identity.API/Quickstart/Extensions.cs
Normal file
22
src/Services/Identity/Identity.API/Quickstart/Extensions.cs
Normal 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 });
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -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; }
|
||||||
|
}
|
@ -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; }
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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
src/Services/Identity/Identity.API/SeedData.cs
Normal file
124
src/Services/Identity/Identity.API/SeedData.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
|
|
Binary file not shown.
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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>
|
@ -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>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,28 +1,58 @@
|
|||||||
@model Microsoft.eShopOnContainers.Services.Identity.API.Models.AccountViewModels.LoginViewModel
|
@model LoginViewModel
|
||||||
|
|
||||||
@{
|
<div class="login-page">
|
||||||
ViewData["Title"] = "Log in";
|
|
||||||
|
|
||||||
var requestQuery = ViewContext.HttpContext.Request.Query;
|
<partial name="_ValidationSummary" />
|
||||||
|
|
||||||
requestQuery.TryGetValue("ReturnUrl", out var returnUrl);
|
<div class="container">
|
||||||
|
<div class="row top-buffer">
|
||||||
|
|
||||||
string partialView;
|
@if (Model.EnableLocalLogin)
|
||||||
|
|
||||||
if (returnUrl[0].Contains("client_id=js"))
|
|
||||||
{
|
{
|
||||||
Layout = "_Layout-SPA";
|
<div class="col"></div>
|
||||||
partialView = "_LoginPartial-SPA.cshtml";
|
<div class="col-sm-6">
|
||||||
}
|
<div class="card">
|
||||||
else
|
<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)
|
||||||
{
|
{
|
||||||
partialView = "_LoginPartial-MVC.cshtml";
|
<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>
|
||||||
}
|
}
|
||||||
|
|
||||||
<partial name=@partialView model=@Model />
|
</div>
|
||||||
|
</div>
|
||||||
@section Scripts {
|
</div>
|
||||||
@{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
|
|
||||||
}
|
|
@ -1,21 +1,15 @@
|
|||||||
@model Microsoft.eShopOnContainers.Services.Identity.API.Models.AccountViewModels.LogoutViewModel
|
@model LogoutViewModel
|
||||||
|
|
||||||
<div class="container logout-page">
|
<div class="logout-page">
|
||||||
<div class="page-header">
|
<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">
|
<form asp-action="Logout">
|
||||||
<input type="hidden" name="logoutId" value="@Model.LogoutId" />
|
<input type="hidden" name="logoutId" value="@Model.LogoutId" />
|
||||||
<fieldset>
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<button class="btn btn-primary">Yes</button>
|
<button class="btn btn-primary">Yes</button>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
@ -1,3 +0,0 @@
|
|||||||
<br />
|
|
||||||
<br />
|
|
||||||
<p style="width:80%;"><center><span>Redirecting...</span></center></p>
|
|
@ -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"); }
|
|
||||||
}
|
|
@ -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"> LOG IN </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>
|
|
@ -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>
|
|
@ -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"> Register </button>
|
|
||||||
</div>
|
|
||||||
<br /><br />
|
|
||||||
</form>
|
|
||||||
</div>
|
|
@ -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>
|
|
@ -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">
|
||||||
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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
src/Services/Identity/Identity.API/Views/Grants/Index.cshtml
Normal file
87
src/Services/Identity/Identity.API/Views/Grants/Index.cshtml
Normal 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>
|
@ -1,30 +1,32 @@
|
|||||||
<div class="welcome-page container">
|
@using System.Diagnostics
|
||||||
<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>
|
|
||||||
|
|
||||||
<div class="row">
|
@{
|
||||||
<div class="col-sm-8">
|
var version = FileVersionInfo.GetVersionInfo(typeof(Duende.IdentityServer.Hosting.IdentityServerMiddleware).Assembly.Location).ProductVersion.Split('+').First();
|
||||||
<p>
|
}
|
||||||
|
|
||||||
|
<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
|
IdentityServer publishes a
|
||||||
<a href="~/.well-known/openid-configuration">discovery document</a>
|
<a href="~/.well-known/openid-configuration">discovery document</a>
|
||||||
where you can find metadata and links to all the endpoints, key material, etc.
|
where you can find metadata and links to all the endpoints, key material, etc.
|
||||||
</p>
|
</li>
|
||||||
</div>
|
<li>
|
||||||
</div>
|
Click <a href="~/diagnostics">here</a> to see the claims for your current session.
|
||||||
<div class="row">
|
</li>
|
||||||
<div class="col-sm-8">
|
<li>
|
||||||
<p>
|
Click <a href="~/grants">here</a> to manage your stored grants.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
Here are links to the
|
Here are links to the
|
||||||
<a href="https://github.com/identityserver/Duende.IdentityServer">source code repository</a>,
|
<a href="https://github.com/identityserver/IdentityServer4">source code repository</a>,
|
||||||
and <a href="https://github.com/identityserver/Duende.IdentityServer.Samples">ready to use samples</a>.
|
and <a href="https://github.com/IdentityServer/IdentityServer4/tree/main/samples">ready to use samples</a>.
|
||||||
</p>
|
</li>
|
||||||
</div>
|
</ul>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -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>
|
||||||
|
|
||||||
|
@ -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>
|
@ -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>
|
|
@ -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="X-UA-Compatible" content="IE=edge">
|
||||||
<meta http-equiv="Content-Security-Policy" content="script-src 'unsafe-inline'; script-src-elem 'unsafe-inline'">
|
<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.ico" />
|
<title>eShopOnContainers - Identity</title>
|
||||||
<link rel="shortcut icon" type="image/x-icon" href="~/favicon.ico" />
|
|
||||||
<environment names="Development">
|
<link rel="icon" type="image/x-icon" href="favicon.png">
|
||||||
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
|
<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" />
|
<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>
|
|
||||||
</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">
|
<article class="d-flex align-content-center justify-content-between">
|
||||||
<div class="navbar-header col-sm-6 col-xs-8">
|
<section>
|
||||||
<a asp-controller="home" asp-action="ReturnToOriginalApplication" asp-route-returnUrl="@ViewData["ReturnUrl"]">
|
<img class="es-header-brand" src="~/images/logo_color.svg">
|
||||||
<div class="navbar-brand"></div>
|
</section>
|
||||||
</a>
|
</article>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div class="content">
|
||||||
@RenderBody()
|
@RenderBody()
|
||||||
<br /><br />
|
</div>
|
||||||
<footer>
|
|
||||||
|
<footer class="footer">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row">
|
<article class="d-flex w-100 h-100 justify-content-between align-items-center">
|
||||||
<div class="col-sm-6">
|
<section>
|
||||||
<br><div class="brand"></div>
|
<img class="footer-brand" src="~/images/logo.svg">
|
||||||
</div>
|
</section>
|
||||||
<div class="col-sm-6">
|
<section> © eShopOnContainers. All rights reserved </section>
|
||||||
<img class="text hidden-xs" src="~/images/main_footer_text.PNG" width="335" height="26" alt="footer text image" />
|
</article>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
|
||||||
|
|
||||||
|
<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>
|
||||||
|
@ -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>
|
|
||||||
}
|
|
@ -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>
|
@ -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,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>
|
||||||
|
@ -1 +1,2 @@
|
|||||||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
@using IdentityServerHost.Quickstart.UI
|
||||||
|
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
@{
|
@{
|
||||||
Layout = "_Layout";
|
Layout = "_Layout";
|
||||||
}
|
}
|
||||||
|
@ -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}
|
@ -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}
|
@ -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}
|
@ -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}
|
@ -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}
|
@ -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
src/Services/Identity/Identity.API/tempkey.jwk
Normal file
1
src/Services/Identity/Identity.API/tempkey.jwk
Normal 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}}
|
@ -0,0 +1 @@
|
|||||||
|
window.location.href = document.querySelector("meta[http-equiv=refresh]").getAttribute("data-url");
|
@ -0,0 +1,6 @@
|
|||||||
|
window.addEventListener("load", function () {
|
||||||
|
var a = document.querySelector("a.PostLogoutRedirectUri");
|
||||||
|
if (a) {
|
||||||
|
window.location = a.href;
|
||||||
|
}
|
||||||
|
});
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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,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,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 have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user