update to Net7 and Duende IdentityServer 6.2pull/2044/head
@ -1,11 +1,11 @@ | |||||
<Project Sdk="Microsoft.NET.Sdk"> | <Project Sdk="Microsoft.NET.Sdk"> | ||||
<PropertyGroup> | <PropertyGroup> | ||||
<TargetFramework>net6.0</TargetFramework> | |||||
<TargetFramework>net7.0</TargetFramework> | |||||
</PropertyGroup> | </PropertyGroup> | ||||
<ItemGroup> | <ItemGroup> | ||||
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.2.0" /> | <PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.2.0" /> | ||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0" /> | |||||
<PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" /> | |||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" /> | |||||
<PackageReference Include="Microsoft.Extensions.Http" Version="7.0.0" /> | |||||
</ItemGroup> | </ItemGroup> | ||||
</Project> | </Project> |
@ -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(); | |||||
} | |||||
} | |||||
} |
@ -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?.ClientId != null) | |||||
{ | |||||
var client = await _clientStore.FindEnabledClientByIdAsync(context.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,131 +0,0 @@ | |||||
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 IClientStore _clientStore; | |||||
private readonly IResourceStore _resourceStore; | |||||
private readonly IIdentityServerInteractionService _interaction; | |||||
public ConsentController( | |||||
ILogger<ConsentController> logger, | |||||
IIdentityServerInteractionService interaction, | |||||
IClientStore clientStore, | |||||
IResourceStore resourceStore) | |||||
{ | |||||
_logger = logger; | |||||
_interaction = interaction; | |||||
_clientStore = clientStore; | |||||
_resourceStore = resourceStore; | |||||
} | |||||
/// <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); | |||||
ViewData["ReturnUrl"] = 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) | |||||
{ | |||||
// parse the return URL back to an AuthorizeRequest object | |||||
var request = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl); | |||||
ConsentResponse response = null; | |||||
// user clicked 'no' - send back the standard 'access_denied' response | |||||
if (model.Button == "no") | |||||
{ | |||||
response = ConsentResponse.Denied; | |||||
} | |||||
// 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()) | |||||
{ | |||||
response = new ConsentResponse | |||||
{ | |||||
RememberConsent = model.RememberConsent, | |||||
ScopesConsented = model.ScopesConsented | |||||
}; | |||||
} | |||||
else | |||||
{ | |||||
ModelState.AddModelError("", "You must pick at least one permission."); | |||||
} | |||||
} | |||||
else | |||||
{ | |||||
ModelState.AddModelError("", "Invalid Selection"); | |||||
} | |||||
if (response != null) | |||||
{ | |||||
// communicate outcome of consent back to identityserver | |||||
await _interaction.GrantConsentAsync(request, response); | |||||
// redirect back to authorization endpoint | |||||
return Redirect(model.ReturnUrl); | |||||
} | |||||
var vm = await BuildViewModelAsync(model.ReturnUrl, model); | |||||
if (vm != null) | |||||
{ | |||||
return View("Index", vm); | |||||
} | |||||
return View("Error"); | |||||
} | |||||
async Task<ConsentViewModel> BuildViewModelAsync(string returnUrl, ConsentInputModel model = null) | |||||
{ | |||||
var request = await _interaction.GetAuthorizationContextAsync(returnUrl); | |||||
if (request != null) | |||||
{ | |||||
var client = await _clientStore.FindEnabledClientByIdAsync(request.ClientId); | |||||
if (client != null) | |||||
{ | |||||
var resources = await _resourceStore.FindEnabledResourcesByScopeAsync(request.ScopesRequested); | |||||
if (resources != null && (resources.IdentityResources.Any() || resources.ApiResources.Any())) | |||||
{ | |||||
return new ConsentViewModel(model, returnUrl, request, client, resources); | |||||
} | |||||
else | |||||
{ | |||||
_logger.LogError("No scopes matching: {0}", request.ScopesRequested.Aggregate((x, y) => x + ", " + y)); | |||||
} | |||||
} | |||||
else | |||||
{ | |||||
_logger.LogError("Invalid client id: {0}", request.ClientId); | |||||
} | |||||
} | |||||
else | |||||
{ | |||||
_logger.LogError("No consent request matching request: {0}", returnUrl); | |||||
} | |||||
return null; | |||||
} | |||||
} | |||||
} |
@ -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,18 +1,17 @@ | |||||
namespace Microsoft.eShopOnContainers.Services.Identity.API.Data | |||||
namespace Microsoft.eShopOnContainers.Services.Identity.API.Data; | |||||
public class ApplicationDbContext : IdentityDbContext<ApplicationUser> | |||||
{ | { | ||||
public class ApplicationDbContext : IdentityDbContext<ApplicationUser> | |||||
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) | |||||
: base(options) | |||||
{ | { | ||||
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) | |||||
: base(options) | |||||
{ | |||||
} | |||||
} | |||||
protected override void OnModelCreating(ModelBuilder builder) | |||||
{ | |||||
base.OnModelCreating(builder); | |||||
// Customize the ASP.NET Identity model and override the defaults if needed. | |||||
// For example, you can rename the ASP.NET Identity table names and more. | |||||
// Add your customizations after calling base.OnModelCreating(builder); | |||||
} | |||||
protected override void OnModelCreating(ModelBuilder builder) | |||||
{ | |||||
base.OnModelCreating(builder); | |||||
// Customize the ASP.NET Identity model and override the defaults if needed. | |||||
// For example, you can rename the ASP.NET Identity table names and more. | |||||
// Add your customizations after calling base.OnModelCreating(builder); | |||||
} | } | ||||
} | } |
@ -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,73 +0,0 @@ | |||||
using IdentityServer4.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(); | |||||
} | |||||
} | |||||
} | |||||
} |
@ -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 | ||||
{ | { |
@ -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, storeOptions); | |||||
} | |||||
} | |||||
} |
@ -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, operationOptions); | |||||
} | |||||
} | |||||
} |
@ -1,911 +0,0 @@ | |||||
// <auto-generated /> | |||||
using System; | |||||
using IdentityServer4.EntityFramework.DbContexts; | |||||
using Microsoft.EntityFrameworkCore; | |||||
using Microsoft.EntityFrameworkCore.Infrastructure; | |||||
using Microsoft.EntityFrameworkCore.Metadata; | |||||
using Microsoft.EntityFrameworkCore.Migrations; | |||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion; | |||||
namespace Identity.API.Migrations.ConfigurationDb | |||||
{ | |||||
[DbContext(typeof(ConfigurationDbContext))] | |||||
[Migration("20210813072543_InitialMigration")] | |||||
partial class InitialMigration | |||||
{ | |||||
protected override void BuildTargetModel(ModelBuilder modelBuilder) | |||||
{ | |||||
#pragma warning disable 612, 618 | |||||
modelBuilder | |||||
.HasAnnotation("Relational:MaxIdentifierLength", 128) | |||||
.HasAnnotation("ProductVersion", "6.0.0") | |||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResource", b => | |||||
{ | |||||
b.Property<int>("Id") | |||||
.ValueGeneratedOnAdd() | |||||
.HasColumnType("int") | |||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); | |||||
b.Property<DateTime>("Created") | |||||
.HasColumnType("datetime2"); | |||||
b.Property<string>("Description") | |||||
.HasMaxLength(1000) | |||||
.HasColumnType("nvarchar(1000)"); | |||||
b.Property<string>("DisplayName") | |||||
.HasMaxLength(200) | |||||
.HasColumnType("nvarchar(200)"); | |||||
b.Property<bool>("Enabled") | |||||
.HasColumnType("bit"); | |||||
b.Property<DateTime?>("LastAccessed") | |||||
.HasColumnType("datetime2"); | |||||
b.Property<string>("Name") | |||||
.IsRequired() | |||||
.HasMaxLength(200) | |||||
.HasColumnType("nvarchar(200)"); | |||||
b.Property<bool>("NonEditable") | |||||
.HasColumnType("bit"); | |||||
b.Property<DateTime?>("Updated") | |||||
.HasColumnType("datetime2"); | |||||
b.HasKey("Id"); | |||||
b.HasIndex("Name") | |||||
.IsUnique(); | |||||
b.ToTable("ApiResources", (string)null); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceClaim", b => | |||||
{ | |||||
b.Property<int>("Id") | |||||
.ValueGeneratedOnAdd() | |||||
.HasColumnType("int") | |||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); | |||||
b.Property<int>("ApiResourceId") | |||||
.HasColumnType("int"); | |||||
b.Property<string>("Type") | |||||
.IsRequired() | |||||
.HasMaxLength(200) | |||||
.HasColumnType("nvarchar(200)"); | |||||
b.HasKey("Id"); | |||||
b.HasIndex("ApiResourceId"); | |||||
b.ToTable("ApiClaims", (string)null); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceProperty", b => | |||||
{ | |||||
b.Property<int>("Id") | |||||
.ValueGeneratedOnAdd() | |||||
.HasColumnType("int") | |||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); | |||||
b.Property<int>("ApiResourceId") | |||||
.HasColumnType("int"); | |||||
b.Property<string>("Key") | |||||
.IsRequired() | |||||
.HasMaxLength(250) | |||||
.HasColumnType("nvarchar(250)"); | |||||
b.Property<string>("Value") | |||||
.IsRequired() | |||||
.HasMaxLength(2000) | |||||
.HasColumnType("nvarchar(2000)"); | |||||
b.HasKey("Id"); | |||||
b.HasIndex("ApiResourceId"); | |||||
b.ToTable("ApiProperties", (string)null); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScope", b => | |||||
{ | |||||
b.Property<int>("Id") | |||||
.ValueGeneratedOnAdd() | |||||
.HasColumnType("int") | |||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); | |||||
b.Property<int>("ApiResourceId") | |||||
.HasColumnType("int"); | |||||
b.Property<string>("Description") | |||||
.HasMaxLength(1000) | |||||
.HasColumnType("nvarchar(1000)"); | |||||
b.Property<string>("DisplayName") | |||||
.HasMaxLength(200) | |||||
.HasColumnType("nvarchar(200)"); | |||||
b.Property<bool>("Emphasize") | |||||
.HasColumnType("bit"); | |||||
b.Property<string>("Name") | |||||
.IsRequired() | |||||
.HasMaxLength(200) | |||||
.HasColumnType("nvarchar(200)"); | |||||
b.Property<bool>("Required") | |||||
.HasColumnType("bit"); | |||||
b.Property<bool>("ShowInDiscoveryDocument") | |||||
.HasColumnType("bit"); | |||||
b.HasKey("Id"); | |||||
b.HasIndex("ApiResourceId"); | |||||
b.HasIndex("Name") | |||||
.IsUnique(); | |||||
b.ToTable("ApiScopes", (string)null); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScopeClaim", b => | |||||
{ | |||||
b.Property<int>("Id") | |||||
.ValueGeneratedOnAdd() | |||||
.HasColumnType("int") | |||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); | |||||
b.Property<int>("ApiScopeId") | |||||
.HasColumnType("int"); | |||||
b.Property<string>("Type") | |||||
.IsRequired() | |||||
.HasMaxLength(200) | |||||
.HasColumnType("nvarchar(200)"); | |||||
b.HasKey("Id"); | |||||
b.HasIndex("ApiScopeId"); | |||||
b.ToTable("ApiScopeClaims", (string)null); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiSecret", b => | |||||
{ | |||||
b.Property<int>("Id") | |||||
.ValueGeneratedOnAdd() | |||||
.HasColumnType("int") | |||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); | |||||
b.Property<int>("ApiResourceId") | |||||
.HasColumnType("int"); | |||||
b.Property<DateTime>("Created") | |||||
.HasColumnType("datetime2"); | |||||
b.Property<string>("Description") | |||||
.HasMaxLength(1000) | |||||
.HasColumnType("nvarchar(1000)"); | |||||
b.Property<DateTime?>("Expiration") | |||||
.HasColumnType("datetime2"); | |||||
b.Property<string>("Type") | |||||
.IsRequired() | |||||
.HasMaxLength(250) | |||||
.HasColumnType("nvarchar(250)"); | |||||
b.Property<string>("Value") | |||||
.IsRequired() | |||||
.HasMaxLength(4000) | |||||
.HasColumnType("nvarchar(4000)"); | |||||
b.HasKey("Id"); | |||||
b.HasIndex("ApiResourceId"); | |||||
b.ToTable("ApiSecrets", (string)null); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.Client", b => | |||||
{ | |||||
b.Property<int>("Id") | |||||
.ValueGeneratedOnAdd() | |||||
.HasColumnType("int") | |||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); | |||||
b.Property<int>("AbsoluteRefreshTokenLifetime") | |||||
.HasColumnType("int"); | |||||
b.Property<int>("AccessTokenLifetime") | |||||
.HasColumnType("int"); | |||||
b.Property<int>("AccessTokenType") | |||||
.HasColumnType("int"); | |||||
b.Property<bool>("AllowAccessTokensViaBrowser") | |||||
.HasColumnType("bit"); | |||||
b.Property<bool>("AllowOfflineAccess") | |||||
.HasColumnType("bit"); | |||||
b.Property<bool>("AllowPlainTextPkce") | |||||
.HasColumnType("bit"); | |||||
b.Property<bool>("AllowRememberConsent") | |||||
.HasColumnType("bit"); | |||||
b.Property<bool>("AlwaysIncludeUserClaimsInIdToken") | |||||
.HasColumnType("bit"); | |||||
b.Property<bool>("AlwaysSendClientClaims") | |||||
.HasColumnType("bit"); | |||||
b.Property<int>("AuthorizationCodeLifetime") | |||||
.HasColumnType("int"); | |||||
b.Property<bool>("BackChannelLogoutSessionRequired") | |||||
.HasColumnType("bit"); | |||||
b.Property<string>("BackChannelLogoutUri") | |||||
.HasMaxLength(2000) | |||||
.HasColumnType("nvarchar(2000)"); | |||||
b.Property<string>("ClientClaimsPrefix") | |||||
.HasMaxLength(200) | |||||
.HasColumnType("nvarchar(200)"); | |||||
b.Property<string>("ClientId") | |||||
.IsRequired() | |||||
.HasMaxLength(200) | |||||
.HasColumnType("nvarchar(200)"); | |||||
b.Property<string>("ClientName") | |||||
.HasMaxLength(200) | |||||
.HasColumnType("nvarchar(200)"); | |||||
b.Property<string>("ClientUri") | |||||
.HasMaxLength(2000) | |||||
.HasColumnType("nvarchar(2000)"); | |||||
b.Property<int?>("ConsentLifetime") | |||||
.HasColumnType("int"); | |||||
b.Property<DateTime>("Created") | |||||
.HasColumnType("datetime2"); | |||||
b.Property<string>("Description") | |||||
.HasMaxLength(1000) | |||||
.HasColumnType("nvarchar(1000)"); | |||||
b.Property<int>("DeviceCodeLifetime") | |||||
.HasColumnType("int"); | |||||
b.Property<bool>("EnableLocalLogin") | |||||
.HasColumnType("bit"); | |||||
b.Property<bool>("Enabled") | |||||
.HasColumnType("bit"); | |||||
b.Property<bool>("FrontChannelLogoutSessionRequired") | |||||
.HasColumnType("bit"); | |||||
b.Property<string>("FrontChannelLogoutUri") | |||||
.HasMaxLength(2000) | |||||
.HasColumnType("nvarchar(2000)"); | |||||
b.Property<int>("IdentityTokenLifetime") | |||||
.HasColumnType("int"); | |||||
b.Property<bool>("IncludeJwtId") | |||||
.HasColumnType("bit"); | |||||
b.Property<DateTime?>("LastAccessed") | |||||
.HasColumnType("datetime2"); | |||||
b.Property<string>("LogoUri") | |||||
.HasMaxLength(2000) | |||||
.HasColumnType("nvarchar(2000)"); | |||||
b.Property<bool>("NonEditable") | |||||
.HasColumnType("bit"); | |||||
b.Property<string>("PairWiseSubjectSalt") | |||||
.HasMaxLength(200) | |||||
.HasColumnType("nvarchar(200)"); | |||||
b.Property<string>("ProtocolType") | |||||
.IsRequired() | |||||
.HasMaxLength(200) | |||||
.HasColumnType("nvarchar(200)"); | |||||
b.Property<int>("RefreshTokenExpiration") | |||||
.HasColumnType("int"); | |||||
b.Property<int>("RefreshTokenUsage") | |||||
.HasColumnType("int"); | |||||
b.Property<bool>("RequireClientSecret") | |||||
.HasColumnType("bit"); | |||||
b.Property<bool>("RequireConsent") | |||||
.HasColumnType("bit"); | |||||
b.Property<bool>("RequirePkce") | |||||
.HasColumnType("bit"); | |||||
b.Property<int>("SlidingRefreshTokenLifetime") | |||||
.HasColumnType("int"); | |||||
b.Property<bool>("UpdateAccessTokenClaimsOnRefresh") | |||||
.HasColumnType("bit"); | |||||
b.Property<DateTime?>("Updated") | |||||
.HasColumnType("datetime2"); | |||||
b.Property<string>("UserCodeType") | |||||
.HasMaxLength(100) | |||||
.HasColumnType("nvarchar(100)"); | |||||
b.Property<int?>("UserSsoLifetime") | |||||
.HasColumnType("int"); | |||||
b.HasKey("Id"); | |||||
b.HasIndex("ClientId") | |||||
.IsUnique(); | |||||
b.ToTable("Clients", (string)null); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientClaim", b => | |||||
{ | |||||
b.Property<int>("Id") | |||||
.ValueGeneratedOnAdd() | |||||
.HasColumnType("int") | |||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); | |||||
b.Property<int>("ClientId") | |||||
.HasColumnType("int"); | |||||
b.Property<string>("Type") | |||||
.IsRequired() | |||||
.HasMaxLength(250) | |||||
.HasColumnType("nvarchar(250)"); | |||||
b.Property<string>("Value") | |||||
.IsRequired() | |||||
.HasMaxLength(250) | |||||
.HasColumnType("nvarchar(250)"); | |||||
b.HasKey("Id"); | |||||
b.HasIndex("ClientId"); | |||||
b.ToTable("ClientClaims", (string)null); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientCorsOrigin", b => | |||||
{ | |||||
b.Property<int>("Id") | |||||
.ValueGeneratedOnAdd() | |||||
.HasColumnType("int") | |||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); | |||||
b.Property<int>("ClientId") | |||||
.HasColumnType("int"); | |||||
b.Property<string>("Origin") | |||||
.IsRequired() | |||||
.HasMaxLength(150) | |||||
.HasColumnType("nvarchar(150)"); | |||||
b.HasKey("Id"); | |||||
b.HasIndex("ClientId"); | |||||
b.ToTable("ClientCorsOrigins", (string)null); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientGrantType", b => | |||||
{ | |||||
b.Property<int>("Id") | |||||
.ValueGeneratedOnAdd() | |||||
.HasColumnType("int") | |||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); | |||||
b.Property<int>("ClientId") | |||||
.HasColumnType("int"); | |||||
b.Property<string>("GrantType") | |||||
.IsRequired() | |||||
.HasMaxLength(250) | |||||
.HasColumnType("nvarchar(250)"); | |||||
b.HasKey("Id"); | |||||
b.HasIndex("ClientId"); | |||||
b.ToTable("ClientGrantTypes", (string)null); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientIdPRestriction", b => | |||||
{ | |||||
b.Property<int>("Id") | |||||
.ValueGeneratedOnAdd() | |||||
.HasColumnType("int") | |||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); | |||||
b.Property<int>("ClientId") | |||||
.HasColumnType("int"); | |||||
b.Property<string>("Provider") | |||||
.IsRequired() | |||||
.HasMaxLength(200) | |||||
.HasColumnType("nvarchar(200)"); | |||||
b.HasKey("Id"); | |||||
b.HasIndex("ClientId"); | |||||
b.ToTable("ClientIdPRestrictions", (string)null); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientPostLogoutRedirectUri", b => | |||||
{ | |||||
b.Property<int>("Id") | |||||
.ValueGeneratedOnAdd() | |||||
.HasColumnType("int") | |||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); | |||||
b.Property<int>("ClientId") | |||||
.HasColumnType("int"); | |||||
b.Property<string>("PostLogoutRedirectUri") | |||||
.IsRequired() | |||||
.HasMaxLength(2000) | |||||
.HasColumnType("nvarchar(2000)"); | |||||
b.HasKey("Id"); | |||||
b.HasIndex("ClientId"); | |||||
b.ToTable("ClientPostLogoutRedirectUris", (string)null); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientProperty", b => | |||||
{ | |||||
b.Property<int>("Id") | |||||
.ValueGeneratedOnAdd() | |||||
.HasColumnType("int") | |||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); | |||||
b.Property<int>("ClientId") | |||||
.HasColumnType("int"); | |||||
b.Property<string>("Key") | |||||
.IsRequired() | |||||
.HasMaxLength(250) | |||||
.HasColumnType("nvarchar(250)"); | |||||
b.Property<string>("Value") | |||||
.IsRequired() | |||||
.HasMaxLength(2000) | |||||
.HasColumnType("nvarchar(2000)"); | |||||
b.HasKey("Id"); | |||||
b.HasIndex("ClientId"); | |||||
b.ToTable("ClientProperties", (string)null); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientRedirectUri", b => | |||||
{ | |||||
b.Property<int>("Id") | |||||
.ValueGeneratedOnAdd() | |||||
.HasColumnType("int") | |||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); | |||||
b.Property<int>("ClientId") | |||||
.HasColumnType("int"); | |||||
b.Property<string>("RedirectUri") | |||||
.IsRequired() | |||||
.HasMaxLength(2000) | |||||
.HasColumnType("nvarchar(2000)"); | |||||
b.HasKey("Id"); | |||||
b.HasIndex("ClientId"); | |||||
b.ToTable("ClientRedirectUris", (string)null); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientScope", b => | |||||
{ | |||||
b.Property<int>("Id") | |||||
.ValueGeneratedOnAdd() | |||||
.HasColumnType("int") | |||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); | |||||
b.Property<int>("ClientId") | |||||
.HasColumnType("int"); | |||||
b.Property<string>("Scope") | |||||
.IsRequired() | |||||
.HasMaxLength(200) | |||||
.HasColumnType("nvarchar(200)"); | |||||
b.HasKey("Id"); | |||||
b.HasIndex("ClientId"); | |||||
b.ToTable("ClientScopes", (string)null); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientSecret", b => | |||||
{ | |||||
b.Property<int>("Id") | |||||
.ValueGeneratedOnAdd() | |||||
.HasColumnType("int") | |||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); | |||||
b.Property<int>("ClientId") | |||||
.HasColumnType("int"); | |||||
b.Property<DateTime>("Created") | |||||
.HasColumnType("datetime2"); | |||||
b.Property<string>("Description") | |||||
.HasMaxLength(2000) | |||||
.HasColumnType("nvarchar(2000)"); | |||||
b.Property<DateTime?>("Expiration") | |||||
.HasColumnType("datetime2"); | |||||
b.Property<string>("Type") | |||||
.IsRequired() | |||||
.HasMaxLength(250) | |||||
.HasColumnType("nvarchar(250)"); | |||||
b.Property<string>("Value") | |||||
.IsRequired() | |||||
.HasMaxLength(4000) | |||||
.HasColumnType("nvarchar(4000)"); | |||||
b.HasKey("Id"); | |||||
b.HasIndex("ClientId"); | |||||
b.ToTable("ClientSecrets", (string)null); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityClaim", b => | |||||
{ | |||||
b.Property<int>("Id") | |||||
.ValueGeneratedOnAdd() | |||||
.HasColumnType("int") | |||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); | |||||
b.Property<int>("IdentityResourceId") | |||||
.HasColumnType("int"); | |||||
b.Property<string>("Type") | |||||
.IsRequired() | |||||
.HasMaxLength(200) | |||||
.HasColumnType("nvarchar(200)"); | |||||
b.HasKey("Id"); | |||||
b.HasIndex("IdentityResourceId"); | |||||
b.ToTable("IdentityClaims", (string)null); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResource", b => | |||||
{ | |||||
b.Property<int>("Id") | |||||
.ValueGeneratedOnAdd() | |||||
.HasColumnType("int") | |||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); | |||||
b.Property<DateTime>("Created") | |||||
.HasColumnType("datetime2"); | |||||
b.Property<string>("Description") | |||||
.HasMaxLength(1000) | |||||
.HasColumnType("nvarchar(1000)"); | |||||
b.Property<string>("DisplayName") | |||||
.HasMaxLength(200) | |||||
.HasColumnType("nvarchar(200)"); | |||||
b.Property<bool>("Emphasize") | |||||
.HasColumnType("bit"); | |||||
b.Property<bool>("Enabled") | |||||
.HasColumnType("bit"); | |||||
b.Property<string>("Name") | |||||
.IsRequired() | |||||
.HasMaxLength(200) | |||||
.HasColumnType("nvarchar(200)"); | |||||
b.Property<bool>("NonEditable") | |||||
.HasColumnType("bit"); | |||||
b.Property<bool>("Required") | |||||
.HasColumnType("bit"); | |||||
b.Property<bool>("ShowInDiscoveryDocument") | |||||
.HasColumnType("bit"); | |||||
b.Property<DateTime?>("Updated") | |||||
.HasColumnType("datetime2"); | |||||
b.HasKey("Id"); | |||||
b.HasIndex("Name") | |||||
.IsUnique(); | |||||
b.ToTable("IdentityResources", (string)null); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResourceProperty", b => | |||||
{ | |||||
b.Property<int>("Id") | |||||
.ValueGeneratedOnAdd() | |||||
.HasColumnType("int") | |||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); | |||||
b.Property<int>("IdentityResourceId") | |||||
.HasColumnType("int"); | |||||
b.Property<string>("Key") | |||||
.IsRequired() | |||||
.HasMaxLength(250) | |||||
.HasColumnType("nvarchar(250)"); | |||||
b.Property<string>("Value") | |||||
.IsRequired() | |||||
.HasMaxLength(2000) | |||||
.HasColumnType("nvarchar(2000)"); | |||||
b.HasKey("Id"); | |||||
b.HasIndex("IdentityResourceId"); | |||||
b.ToTable("IdentityProperties", (string)null); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceClaim", b => | |||||
{ | |||||
b.HasOne("IdentityServer4.EntityFramework.Entities.ApiResource", "ApiResource") | |||||
.WithMany("UserClaims") | |||||
.HasForeignKey("ApiResourceId") | |||||
.OnDelete(DeleteBehavior.Cascade) | |||||
.IsRequired(); | |||||
b.Navigation("ApiResource"); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceProperty", b => | |||||
{ | |||||
b.HasOne("IdentityServer4.EntityFramework.Entities.ApiResource", "ApiResource") | |||||
.WithMany("Properties") | |||||
.HasForeignKey("ApiResourceId") | |||||
.OnDelete(DeleteBehavior.Cascade) | |||||
.IsRequired(); | |||||
b.Navigation("ApiResource"); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScope", b => | |||||
{ | |||||
b.HasOne("IdentityServer4.EntityFramework.Entities.ApiResource", "ApiResource") | |||||
.WithMany("Scopes") | |||||
.HasForeignKey("ApiResourceId") | |||||
.OnDelete(DeleteBehavior.Cascade) | |||||
.IsRequired(); | |||||
b.Navigation("ApiResource"); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScopeClaim", b => | |||||
{ | |||||
b.HasOne("IdentityServer4.EntityFramework.Entities.ApiScope", "ApiScope") | |||||
.WithMany("UserClaims") | |||||
.HasForeignKey("ApiScopeId") | |||||
.OnDelete(DeleteBehavior.Cascade) | |||||
.IsRequired(); | |||||
b.Navigation("ApiScope"); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiSecret", b => | |||||
{ | |||||
b.HasOne("IdentityServer4.EntityFramework.Entities.ApiResource", "ApiResource") | |||||
.WithMany("Secrets") | |||||
.HasForeignKey("ApiResourceId") | |||||
.OnDelete(DeleteBehavior.Cascade) | |||||
.IsRequired(); | |||||
b.Navigation("ApiResource"); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientClaim", b => | |||||
{ | |||||
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") | |||||
.WithMany("Claims") | |||||
.HasForeignKey("ClientId") | |||||
.OnDelete(DeleteBehavior.Cascade) | |||||
.IsRequired(); | |||||
b.Navigation("Client"); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientCorsOrigin", b => | |||||
{ | |||||
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") | |||||
.WithMany("AllowedCorsOrigins") | |||||
.HasForeignKey("ClientId") | |||||
.OnDelete(DeleteBehavior.Cascade) | |||||
.IsRequired(); | |||||
b.Navigation("Client"); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientGrantType", b => | |||||
{ | |||||
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") | |||||
.WithMany("AllowedGrantTypes") | |||||
.HasForeignKey("ClientId") | |||||
.OnDelete(DeleteBehavior.Cascade) | |||||
.IsRequired(); | |||||
b.Navigation("Client"); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientIdPRestriction", b => | |||||
{ | |||||
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") | |||||
.WithMany("IdentityProviderRestrictions") | |||||
.HasForeignKey("ClientId") | |||||
.OnDelete(DeleteBehavior.Cascade) | |||||
.IsRequired(); | |||||
b.Navigation("Client"); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientPostLogoutRedirectUri", b => | |||||
{ | |||||
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") | |||||
.WithMany("PostLogoutRedirectUris") | |||||
.HasForeignKey("ClientId") | |||||
.OnDelete(DeleteBehavior.Cascade) | |||||
.IsRequired(); | |||||
b.Navigation("Client"); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientProperty", b => | |||||
{ | |||||
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") | |||||
.WithMany("Properties") | |||||
.HasForeignKey("ClientId") | |||||
.OnDelete(DeleteBehavior.Cascade) | |||||
.IsRequired(); | |||||
b.Navigation("Client"); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientRedirectUri", b => | |||||
{ | |||||
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") | |||||
.WithMany("RedirectUris") | |||||
.HasForeignKey("ClientId") | |||||
.OnDelete(DeleteBehavior.Cascade) | |||||
.IsRequired(); | |||||
b.Navigation("Client"); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientScope", b => | |||||
{ | |||||
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") | |||||
.WithMany("AllowedScopes") | |||||
.HasForeignKey("ClientId") | |||||
.OnDelete(DeleteBehavior.Cascade) | |||||
.IsRequired(); | |||||
b.Navigation("Client"); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientSecret", b => | |||||
{ | |||||
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") | |||||
.WithMany("ClientSecrets") | |||||
.HasForeignKey("ClientId") | |||||
.OnDelete(DeleteBehavior.Cascade) | |||||
.IsRequired(); | |||||
b.Navigation("Client"); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityClaim", b => | |||||
{ | |||||
b.HasOne("IdentityServer4.EntityFramework.Entities.IdentityResource", "IdentityResource") | |||||
.WithMany("UserClaims") | |||||
.HasForeignKey("IdentityResourceId") | |||||
.OnDelete(DeleteBehavior.Cascade) | |||||
.IsRequired(); | |||||
b.Navigation("IdentityResource"); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResourceProperty", b => | |||||
{ | |||||
b.HasOne("IdentityServer4.EntityFramework.Entities.IdentityResource", "IdentityResource") | |||||
.WithMany("Properties") | |||||
.HasForeignKey("IdentityResourceId") | |||||
.OnDelete(DeleteBehavior.Cascade) | |||||
.IsRequired(); | |||||
b.Navigation("IdentityResource"); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResource", b => | |||||
{ | |||||
b.Navigation("Properties"); | |||||
b.Navigation("Scopes"); | |||||
b.Navigation("Secrets"); | |||||
b.Navigation("UserClaims"); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScope", b => | |||||
{ | |||||
b.Navigation("UserClaims"); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.Client", b => | |||||
{ | |||||
b.Navigation("AllowedCorsOrigins"); | |||||
b.Navigation("AllowedGrantTypes"); | |||||
b.Navigation("AllowedScopes"); | |||||
b.Navigation("Claims"); | |||||
b.Navigation("ClientSecrets"); | |||||
b.Navigation("IdentityProviderRestrictions"); | |||||
b.Navigation("PostLogoutRedirectUris"); | |||||
b.Navigation("Properties"); | |||||
b.Navigation("RedirectUris"); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResource", b => | |||||
{ | |||||
b.Navigation("Properties"); | |||||
b.Navigation("UserClaims"); | |||||
}); | |||||
#pragma warning restore 612, 618 | |||||
} | |||||
} | |||||
} |
@ -1,607 +0,0 @@ | |||||
using System; | |||||
using Microsoft.EntityFrameworkCore.Migrations; | |||||
namespace Identity.API.Migrations.ConfigurationDb | |||||
{ | |||||
public partial class InitialMigration : 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), | |||||
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: "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), | |||||
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), | |||||
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), | |||||
Created = table.Column<DateTime>(type: "datetime2", nullable: false), | |||||
Updated = table.Column<DateTime>(type: "datetime2", nullable: true), | |||||
LastAccessed = table.Column<DateTime>(type: "datetime2", 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), | |||||
NonEditable = table.Column<bool>(type: "bit", nullable: false) | |||||
}, | |||||
constraints: table => | |||||
{ | |||||
table.PrimaryKey("PK_Clients", 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: "ApiClaims", | |||||
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_ApiClaims", x => x.Id); | |||||
table.ForeignKey( | |||||
name: "FK_ApiClaims_ApiResources_ApiResourceId", | |||||
column: x => x.ApiResourceId, | |||||
principalTable: "ApiResources", | |||||
principalColumn: "Id", | |||||
onDelete: ReferentialAction.Cascade); | |||||
}); | |||||
migrationBuilder.CreateTable( | |||||
name: "ApiProperties", | |||||
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_ApiProperties", x => x.Id); | |||||
table.ForeignKey( | |||||
name: "FK_ApiProperties_ApiResources_ApiResourceId", | |||||
column: x => x.ApiResourceId, | |||||
principalTable: "ApiResources", | |||||
principalColumn: "Id", | |||||
onDelete: ReferentialAction.Cascade); | |||||
}); | |||||
migrationBuilder.CreateTable( | |||||
name: "ApiScopes", | |||||
columns: table => new | |||||
{ | |||||
Id = table.Column<int>(type: "int", nullable: false) | |||||
.Annotation("SqlServer:Identity", "1, 1"), | |||||
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), | |||||
ApiResourceId = table.Column<int>(type: "int", nullable: false) | |||||
}, | |||||
constraints: table => | |||||
{ | |||||
table.PrimaryKey("PK_ApiScopes", x => x.Id); | |||||
table.ForeignKey( | |||||
name: "FK_ApiScopes_ApiResources_ApiResourceId", | |||||
column: x => x.ApiResourceId, | |||||
principalTable: "ApiResources", | |||||
principalColumn: "Id", | |||||
onDelete: ReferentialAction.Cascade); | |||||
}); | |||||
migrationBuilder.CreateTable( | |||||
name: "ApiSecrets", | |||||
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_ApiSecrets", x => x.Id); | |||||
table.ForeignKey( | |||||
name: "FK_ApiSecrets_ApiResources_ApiResourceId", | |||||
column: x => x.ApiResourceId, | |||||
principalTable: "ApiResources", | |||||
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(2000)", maxLength: 2000, 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(2000)", maxLength: 2000, 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: "IdentityClaims", | |||||
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_IdentityClaims", x => x.Id); | |||||
table.ForeignKey( | |||||
name: "FK_IdentityClaims_IdentityResources_IdentityResourceId", | |||||
column: x => x.IdentityResourceId, | |||||
principalTable: "IdentityResources", | |||||
principalColumn: "Id", | |||||
onDelete: ReferentialAction.Cascade); | |||||
}); | |||||
migrationBuilder.CreateTable( | |||||
name: "IdentityProperties", | |||||
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_IdentityProperties", x => x.Id); | |||||
table.ForeignKey( | |||||
name: "FK_IdentityProperties_IdentityResources_IdentityResourceId", | |||||
column: x => x.IdentityResourceId, | |||||
principalTable: "IdentityResources", | |||||
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"), | |||||
ApiScopeId = 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_ApiScopeId", | |||||
column: x => x.ApiScopeId, | |||||
principalTable: "ApiScopes", | |||||
principalColumn: "Id", | |||||
onDelete: ReferentialAction.Cascade); | |||||
}); | |||||
migrationBuilder.CreateIndex( | |||||
name: "IX_ApiClaims_ApiResourceId", | |||||
table: "ApiClaims", | |||||
column: "ApiResourceId"); | |||||
migrationBuilder.CreateIndex( | |||||
name: "IX_ApiProperties_ApiResourceId", | |||||
table: "ApiProperties", | |||||
column: "ApiResourceId"); | |||||
migrationBuilder.CreateIndex( | |||||
name: "IX_ApiResources_Name", | |||||
table: "ApiResources", | |||||
column: "Name", | |||||
unique: true); | |||||
migrationBuilder.CreateIndex( | |||||
name: "IX_ApiScopeClaims_ApiScopeId", | |||||
table: "ApiScopeClaims", | |||||
column: "ApiScopeId"); | |||||
migrationBuilder.CreateIndex( | |||||
name: "IX_ApiScopes_ApiResourceId", | |||||
table: "ApiScopes", | |||||
column: "ApiResourceId"); | |||||
migrationBuilder.CreateIndex( | |||||
name: "IX_ApiScopes_Name", | |||||
table: "ApiScopes", | |||||
column: "Name", | |||||
unique: true); | |||||
migrationBuilder.CreateIndex( | |||||
name: "IX_ApiSecrets_ApiResourceId", | |||||
table: "ApiSecrets", | |||||
column: "ApiResourceId"); | |||||
migrationBuilder.CreateIndex( | |||||
name: "IX_ClientClaims_ClientId", | |||||
table: "ClientClaims", | |||||
column: "ClientId"); | |||||
migrationBuilder.CreateIndex( | |||||
name: "IX_ClientCorsOrigins_ClientId", | |||||
table: "ClientCorsOrigins", | |||||
column: "ClientId"); | |||||
migrationBuilder.CreateIndex( | |||||
name: "IX_ClientGrantTypes_ClientId", | |||||
table: "ClientGrantTypes", | |||||
column: "ClientId"); | |||||
migrationBuilder.CreateIndex( | |||||
name: "IX_ClientIdPRestrictions_ClientId", | |||||
table: "ClientIdPRestrictions", | |||||
column: "ClientId"); | |||||
migrationBuilder.CreateIndex( | |||||
name: "IX_ClientPostLogoutRedirectUris_ClientId", | |||||
table: "ClientPostLogoutRedirectUris", | |||||
column: "ClientId"); | |||||
migrationBuilder.CreateIndex( | |||||
name: "IX_ClientProperties_ClientId", | |||||
table: "ClientProperties", | |||||
column: "ClientId"); | |||||
migrationBuilder.CreateIndex( | |||||
name: "IX_ClientRedirectUris_ClientId", | |||||
table: "ClientRedirectUris", | |||||
column: "ClientId"); | |||||
migrationBuilder.CreateIndex( | |||||
name: "IX_Clients_ClientId", | |||||
table: "Clients", | |||||
column: "ClientId", | |||||
unique: true); | |||||
migrationBuilder.CreateIndex( | |||||
name: "IX_ClientScopes_ClientId", | |||||
table: "ClientScopes", | |||||
column: "ClientId"); | |||||
migrationBuilder.CreateIndex( | |||||
name: "IX_ClientSecrets_ClientId", | |||||
table: "ClientSecrets", | |||||
column: "ClientId"); | |||||
migrationBuilder.CreateIndex( | |||||
name: "IX_IdentityClaims_IdentityResourceId", | |||||
table: "IdentityClaims", | |||||
column: "IdentityResourceId"); | |||||
migrationBuilder.CreateIndex( | |||||
name: "IX_IdentityProperties_IdentityResourceId", | |||||
table: "IdentityProperties", | |||||
column: "IdentityResourceId"); | |||||
migrationBuilder.CreateIndex( | |||||
name: "IX_IdentityResources_Name", | |||||
table: "IdentityResources", | |||||
column: "Name", | |||||
unique: true); | |||||
} | |||||
protected override void Down(MigrationBuilder migrationBuilder) | |||||
{ | |||||
migrationBuilder.DropTable( | |||||
name: "ApiClaims"); | |||||
migrationBuilder.DropTable( | |||||
name: "ApiProperties"); | |||||
migrationBuilder.DropTable( | |||||
name: "ApiScopeClaims"); | |||||
migrationBuilder.DropTable( | |||||
name: "ApiSecrets"); | |||||
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: "IdentityClaims"); | |||||
migrationBuilder.DropTable( | |||||
name: "IdentityProperties"); | |||||
migrationBuilder.DropTable( | |||||
name: "ApiScopes"); | |||||
migrationBuilder.DropTable( | |||||
name: "Clients"); | |||||
migrationBuilder.DropTable( | |||||
name: "IdentityResources"); | |||||
migrationBuilder.DropTable( | |||||
name: "ApiResources"); | |||||
} | |||||
} | |||||
} |
@ -1,909 +0,0 @@ | |||||
// <auto-generated /> | |||||
using System; | |||||
using IdentityServer4.EntityFramework.DbContexts; | |||||
using Microsoft.EntityFrameworkCore; | |||||
using Microsoft.EntityFrameworkCore.Infrastructure; | |||||
using Microsoft.EntityFrameworkCore.Metadata; | |||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion; | |||||
namespace Identity.API.Migrations.ConfigurationDb | |||||
{ | |||||
[DbContext(typeof(ConfigurationDbContext))] | |||||
partial class ConfigurationDbContextModelSnapshot : ModelSnapshot | |||||
{ | |||||
protected override void BuildModel(ModelBuilder modelBuilder) | |||||
{ | |||||
#pragma warning disable 612, 618 | |||||
modelBuilder | |||||
.HasAnnotation("Relational:MaxIdentifierLength", 128) | |||||
.HasAnnotation("ProductVersion", "6.0.0") | |||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResource", b => | |||||
{ | |||||
b.Property<int>("Id") | |||||
.ValueGeneratedOnAdd() | |||||
.HasColumnType("int") | |||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); | |||||
b.Property<DateTime>("Created") | |||||
.HasColumnType("datetime2"); | |||||
b.Property<string>("Description") | |||||
.HasMaxLength(1000) | |||||
.HasColumnType("nvarchar(1000)"); | |||||
b.Property<string>("DisplayName") | |||||
.HasMaxLength(200) | |||||
.HasColumnType("nvarchar(200)"); | |||||
b.Property<bool>("Enabled") | |||||
.HasColumnType("bit"); | |||||
b.Property<DateTime?>("LastAccessed") | |||||
.HasColumnType("datetime2"); | |||||
b.Property<string>("Name") | |||||
.IsRequired() | |||||
.HasMaxLength(200) | |||||
.HasColumnType("nvarchar(200)"); | |||||
b.Property<bool>("NonEditable") | |||||
.HasColumnType("bit"); | |||||
b.Property<DateTime?>("Updated") | |||||
.HasColumnType("datetime2"); | |||||
b.HasKey("Id"); | |||||
b.HasIndex("Name") | |||||
.IsUnique(); | |||||
b.ToTable("ApiResources", (string)null); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceClaim", b => | |||||
{ | |||||
b.Property<int>("Id") | |||||
.ValueGeneratedOnAdd() | |||||
.HasColumnType("int") | |||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); | |||||
b.Property<int>("ApiResourceId") | |||||
.HasColumnType("int"); | |||||
b.Property<string>("Type") | |||||
.IsRequired() | |||||
.HasMaxLength(200) | |||||
.HasColumnType("nvarchar(200)"); | |||||
b.HasKey("Id"); | |||||
b.HasIndex("ApiResourceId"); | |||||
b.ToTable("ApiClaims", (string)null); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceProperty", b => | |||||
{ | |||||
b.Property<int>("Id") | |||||
.ValueGeneratedOnAdd() | |||||
.HasColumnType("int") | |||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); | |||||
b.Property<int>("ApiResourceId") | |||||
.HasColumnType("int"); | |||||
b.Property<string>("Key") | |||||
.IsRequired() | |||||
.HasMaxLength(250) | |||||
.HasColumnType("nvarchar(250)"); | |||||
b.Property<string>("Value") | |||||
.IsRequired() | |||||
.HasMaxLength(2000) | |||||
.HasColumnType("nvarchar(2000)"); | |||||
b.HasKey("Id"); | |||||
b.HasIndex("ApiResourceId"); | |||||
b.ToTable("ApiProperties", (string)null); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScope", b => | |||||
{ | |||||
b.Property<int>("Id") | |||||
.ValueGeneratedOnAdd() | |||||
.HasColumnType("int") | |||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); | |||||
b.Property<int>("ApiResourceId") | |||||
.HasColumnType("int"); | |||||
b.Property<string>("Description") | |||||
.HasMaxLength(1000) | |||||
.HasColumnType("nvarchar(1000)"); | |||||
b.Property<string>("DisplayName") | |||||
.HasMaxLength(200) | |||||
.HasColumnType("nvarchar(200)"); | |||||
b.Property<bool>("Emphasize") | |||||
.HasColumnType("bit"); | |||||
b.Property<string>("Name") | |||||
.IsRequired() | |||||
.HasMaxLength(200) | |||||
.HasColumnType("nvarchar(200)"); | |||||
b.Property<bool>("Required") | |||||
.HasColumnType("bit"); | |||||
b.Property<bool>("ShowInDiscoveryDocument") | |||||
.HasColumnType("bit"); | |||||
b.HasKey("Id"); | |||||
b.HasIndex("ApiResourceId"); | |||||
b.HasIndex("Name") | |||||
.IsUnique(); | |||||
b.ToTable("ApiScopes", (string)null); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScopeClaim", b => | |||||
{ | |||||
b.Property<int>("Id") | |||||
.ValueGeneratedOnAdd() | |||||
.HasColumnType("int") | |||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); | |||||
b.Property<int>("ApiScopeId") | |||||
.HasColumnType("int"); | |||||
b.Property<string>("Type") | |||||
.IsRequired() | |||||
.HasMaxLength(200) | |||||
.HasColumnType("nvarchar(200)"); | |||||
b.HasKey("Id"); | |||||
b.HasIndex("ApiScopeId"); | |||||
b.ToTable("ApiScopeClaims", (string)null); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiSecret", b => | |||||
{ | |||||
b.Property<int>("Id") | |||||
.ValueGeneratedOnAdd() | |||||
.HasColumnType("int") | |||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); | |||||
b.Property<int>("ApiResourceId") | |||||
.HasColumnType("int"); | |||||
b.Property<DateTime>("Created") | |||||
.HasColumnType("datetime2"); | |||||
b.Property<string>("Description") | |||||
.HasMaxLength(1000) | |||||
.HasColumnType("nvarchar(1000)"); | |||||
b.Property<DateTime?>("Expiration") | |||||
.HasColumnType("datetime2"); | |||||
b.Property<string>("Type") | |||||
.IsRequired() | |||||
.HasMaxLength(250) | |||||
.HasColumnType("nvarchar(250)"); | |||||
b.Property<string>("Value") | |||||
.IsRequired() | |||||
.HasMaxLength(4000) | |||||
.HasColumnType("nvarchar(4000)"); | |||||
b.HasKey("Id"); | |||||
b.HasIndex("ApiResourceId"); | |||||
b.ToTable("ApiSecrets", (string)null); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.Client", b => | |||||
{ | |||||
b.Property<int>("Id") | |||||
.ValueGeneratedOnAdd() | |||||
.HasColumnType("int") | |||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); | |||||
b.Property<int>("AbsoluteRefreshTokenLifetime") | |||||
.HasColumnType("int"); | |||||
b.Property<int>("AccessTokenLifetime") | |||||
.HasColumnType("int"); | |||||
b.Property<int>("AccessTokenType") | |||||
.HasColumnType("int"); | |||||
b.Property<bool>("AllowAccessTokensViaBrowser") | |||||
.HasColumnType("bit"); | |||||
b.Property<bool>("AllowOfflineAccess") | |||||
.HasColumnType("bit"); | |||||
b.Property<bool>("AllowPlainTextPkce") | |||||
.HasColumnType("bit"); | |||||
b.Property<bool>("AllowRememberConsent") | |||||
.HasColumnType("bit"); | |||||
b.Property<bool>("AlwaysIncludeUserClaimsInIdToken") | |||||
.HasColumnType("bit"); | |||||
b.Property<bool>("AlwaysSendClientClaims") | |||||
.HasColumnType("bit"); | |||||
b.Property<int>("AuthorizationCodeLifetime") | |||||
.HasColumnType("int"); | |||||
b.Property<bool>("BackChannelLogoutSessionRequired") | |||||
.HasColumnType("bit"); | |||||
b.Property<string>("BackChannelLogoutUri") | |||||
.HasMaxLength(2000) | |||||
.HasColumnType("nvarchar(2000)"); | |||||
b.Property<string>("ClientClaimsPrefix") | |||||
.HasMaxLength(200) | |||||
.HasColumnType("nvarchar(200)"); | |||||
b.Property<string>("ClientId") | |||||
.IsRequired() | |||||
.HasMaxLength(200) | |||||
.HasColumnType("nvarchar(200)"); | |||||
b.Property<string>("ClientName") | |||||
.HasMaxLength(200) | |||||
.HasColumnType("nvarchar(200)"); | |||||
b.Property<string>("ClientUri") | |||||
.HasMaxLength(2000) | |||||
.HasColumnType("nvarchar(2000)"); | |||||
b.Property<int?>("ConsentLifetime") | |||||
.HasColumnType("int"); | |||||
b.Property<DateTime>("Created") | |||||
.HasColumnType("datetime2"); | |||||
b.Property<string>("Description") | |||||
.HasMaxLength(1000) | |||||
.HasColumnType("nvarchar(1000)"); | |||||
b.Property<int>("DeviceCodeLifetime") | |||||
.HasColumnType("int"); | |||||
b.Property<bool>("EnableLocalLogin") | |||||
.HasColumnType("bit"); | |||||
b.Property<bool>("Enabled") | |||||
.HasColumnType("bit"); | |||||
b.Property<bool>("FrontChannelLogoutSessionRequired") | |||||
.HasColumnType("bit"); | |||||
b.Property<string>("FrontChannelLogoutUri") | |||||
.HasMaxLength(2000) | |||||
.HasColumnType("nvarchar(2000)"); | |||||
b.Property<int>("IdentityTokenLifetime") | |||||
.HasColumnType("int"); | |||||
b.Property<bool>("IncludeJwtId") | |||||
.HasColumnType("bit"); | |||||
b.Property<DateTime?>("LastAccessed") | |||||
.HasColumnType("datetime2"); | |||||
b.Property<string>("LogoUri") | |||||
.HasMaxLength(2000) | |||||
.HasColumnType("nvarchar(2000)"); | |||||
b.Property<bool>("NonEditable") | |||||
.HasColumnType("bit"); | |||||
b.Property<string>("PairWiseSubjectSalt") | |||||
.HasMaxLength(200) | |||||
.HasColumnType("nvarchar(200)"); | |||||
b.Property<string>("ProtocolType") | |||||
.IsRequired() | |||||
.HasMaxLength(200) | |||||
.HasColumnType("nvarchar(200)"); | |||||
b.Property<int>("RefreshTokenExpiration") | |||||
.HasColumnType("int"); | |||||
b.Property<int>("RefreshTokenUsage") | |||||
.HasColumnType("int"); | |||||
b.Property<bool>("RequireClientSecret") | |||||
.HasColumnType("bit"); | |||||
b.Property<bool>("RequireConsent") | |||||
.HasColumnType("bit"); | |||||
b.Property<bool>("RequirePkce") | |||||
.HasColumnType("bit"); | |||||
b.Property<int>("SlidingRefreshTokenLifetime") | |||||
.HasColumnType("int"); | |||||
b.Property<bool>("UpdateAccessTokenClaimsOnRefresh") | |||||
.HasColumnType("bit"); | |||||
b.Property<DateTime?>("Updated") | |||||
.HasColumnType("datetime2"); | |||||
b.Property<string>("UserCodeType") | |||||
.HasMaxLength(100) | |||||
.HasColumnType("nvarchar(100)"); | |||||
b.Property<int?>("UserSsoLifetime") | |||||
.HasColumnType("int"); | |||||
b.HasKey("Id"); | |||||
b.HasIndex("ClientId") | |||||
.IsUnique(); | |||||
b.ToTable("Clients", (string)null); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientClaim", b => | |||||
{ | |||||
b.Property<int>("Id") | |||||
.ValueGeneratedOnAdd() | |||||
.HasColumnType("int") | |||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); | |||||
b.Property<int>("ClientId") | |||||
.HasColumnType("int"); | |||||
b.Property<string>("Type") | |||||
.IsRequired() | |||||
.HasMaxLength(250) | |||||
.HasColumnType("nvarchar(250)"); | |||||
b.Property<string>("Value") | |||||
.IsRequired() | |||||
.HasMaxLength(250) | |||||
.HasColumnType("nvarchar(250)"); | |||||
b.HasKey("Id"); | |||||
b.HasIndex("ClientId"); | |||||
b.ToTable("ClientClaims", (string)null); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientCorsOrigin", b => | |||||
{ | |||||
b.Property<int>("Id") | |||||
.ValueGeneratedOnAdd() | |||||
.HasColumnType("int") | |||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); | |||||
b.Property<int>("ClientId") | |||||
.HasColumnType("int"); | |||||
b.Property<string>("Origin") | |||||
.IsRequired() | |||||
.HasMaxLength(150) | |||||
.HasColumnType("nvarchar(150)"); | |||||
b.HasKey("Id"); | |||||
b.HasIndex("ClientId"); | |||||
b.ToTable("ClientCorsOrigins", (string)null); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientGrantType", b => | |||||
{ | |||||
b.Property<int>("Id") | |||||
.ValueGeneratedOnAdd() | |||||
.HasColumnType("int") | |||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); | |||||
b.Property<int>("ClientId") | |||||
.HasColumnType("int"); | |||||
b.Property<string>("GrantType") | |||||
.IsRequired() | |||||
.HasMaxLength(250) | |||||
.HasColumnType("nvarchar(250)"); | |||||
b.HasKey("Id"); | |||||
b.HasIndex("ClientId"); | |||||
b.ToTable("ClientGrantTypes", (string)null); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientIdPRestriction", b => | |||||
{ | |||||
b.Property<int>("Id") | |||||
.ValueGeneratedOnAdd() | |||||
.HasColumnType("int") | |||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); | |||||
b.Property<int>("ClientId") | |||||
.HasColumnType("int"); | |||||
b.Property<string>("Provider") | |||||
.IsRequired() | |||||
.HasMaxLength(200) | |||||
.HasColumnType("nvarchar(200)"); | |||||
b.HasKey("Id"); | |||||
b.HasIndex("ClientId"); | |||||
b.ToTable("ClientIdPRestrictions", (string)null); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientPostLogoutRedirectUri", b => | |||||
{ | |||||
b.Property<int>("Id") | |||||
.ValueGeneratedOnAdd() | |||||
.HasColumnType("int") | |||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); | |||||
b.Property<int>("ClientId") | |||||
.HasColumnType("int"); | |||||
b.Property<string>("PostLogoutRedirectUri") | |||||
.IsRequired() | |||||
.HasMaxLength(2000) | |||||
.HasColumnType("nvarchar(2000)"); | |||||
b.HasKey("Id"); | |||||
b.HasIndex("ClientId"); | |||||
b.ToTable("ClientPostLogoutRedirectUris", (string)null); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientProperty", b => | |||||
{ | |||||
b.Property<int>("Id") | |||||
.ValueGeneratedOnAdd() | |||||
.HasColumnType("int") | |||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); | |||||
b.Property<int>("ClientId") | |||||
.HasColumnType("int"); | |||||
b.Property<string>("Key") | |||||
.IsRequired() | |||||
.HasMaxLength(250) | |||||
.HasColumnType("nvarchar(250)"); | |||||
b.Property<string>("Value") | |||||
.IsRequired() | |||||
.HasMaxLength(2000) | |||||
.HasColumnType("nvarchar(2000)"); | |||||
b.HasKey("Id"); | |||||
b.HasIndex("ClientId"); | |||||
b.ToTable("ClientProperties", (string)null); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientRedirectUri", b => | |||||
{ | |||||
b.Property<int>("Id") | |||||
.ValueGeneratedOnAdd() | |||||
.HasColumnType("int") | |||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); | |||||
b.Property<int>("ClientId") | |||||
.HasColumnType("int"); | |||||
b.Property<string>("RedirectUri") | |||||
.IsRequired() | |||||
.HasMaxLength(2000) | |||||
.HasColumnType("nvarchar(2000)"); | |||||
b.HasKey("Id"); | |||||
b.HasIndex("ClientId"); | |||||
b.ToTable("ClientRedirectUris", (string)null); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientScope", b => | |||||
{ | |||||
b.Property<int>("Id") | |||||
.ValueGeneratedOnAdd() | |||||
.HasColumnType("int") | |||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); | |||||
b.Property<int>("ClientId") | |||||
.HasColumnType("int"); | |||||
b.Property<string>("Scope") | |||||
.IsRequired() | |||||
.HasMaxLength(200) | |||||
.HasColumnType("nvarchar(200)"); | |||||
b.HasKey("Id"); | |||||
b.HasIndex("ClientId"); | |||||
b.ToTable("ClientScopes", (string)null); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientSecret", b => | |||||
{ | |||||
b.Property<int>("Id") | |||||
.ValueGeneratedOnAdd() | |||||
.HasColumnType("int") | |||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); | |||||
b.Property<int>("ClientId") | |||||
.HasColumnType("int"); | |||||
b.Property<DateTime>("Created") | |||||
.HasColumnType("datetime2"); | |||||
b.Property<string>("Description") | |||||
.HasMaxLength(2000) | |||||
.HasColumnType("nvarchar(2000)"); | |||||
b.Property<DateTime?>("Expiration") | |||||
.HasColumnType("datetime2"); | |||||
b.Property<string>("Type") | |||||
.IsRequired() | |||||
.HasMaxLength(250) | |||||
.HasColumnType("nvarchar(250)"); | |||||
b.Property<string>("Value") | |||||
.IsRequired() | |||||
.HasMaxLength(4000) | |||||
.HasColumnType("nvarchar(4000)"); | |||||
b.HasKey("Id"); | |||||
b.HasIndex("ClientId"); | |||||
b.ToTable("ClientSecrets", (string)null); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityClaim", b => | |||||
{ | |||||
b.Property<int>("Id") | |||||
.ValueGeneratedOnAdd() | |||||
.HasColumnType("int") | |||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); | |||||
b.Property<int>("IdentityResourceId") | |||||
.HasColumnType("int"); | |||||
b.Property<string>("Type") | |||||
.IsRequired() | |||||
.HasMaxLength(200) | |||||
.HasColumnType("nvarchar(200)"); | |||||
b.HasKey("Id"); | |||||
b.HasIndex("IdentityResourceId"); | |||||
b.ToTable("IdentityClaims", (string)null); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResource", b => | |||||
{ | |||||
b.Property<int>("Id") | |||||
.ValueGeneratedOnAdd() | |||||
.HasColumnType("int") | |||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); | |||||
b.Property<DateTime>("Created") | |||||
.HasColumnType("datetime2"); | |||||
b.Property<string>("Description") | |||||
.HasMaxLength(1000) | |||||
.HasColumnType("nvarchar(1000)"); | |||||
b.Property<string>("DisplayName") | |||||
.HasMaxLength(200) | |||||
.HasColumnType("nvarchar(200)"); | |||||
b.Property<bool>("Emphasize") | |||||
.HasColumnType("bit"); | |||||
b.Property<bool>("Enabled") | |||||
.HasColumnType("bit"); | |||||
b.Property<string>("Name") | |||||
.IsRequired() | |||||
.HasMaxLength(200) | |||||
.HasColumnType("nvarchar(200)"); | |||||
b.Property<bool>("NonEditable") | |||||
.HasColumnType("bit"); | |||||
b.Property<bool>("Required") | |||||
.HasColumnType("bit"); | |||||
b.Property<bool>("ShowInDiscoveryDocument") | |||||
.HasColumnType("bit"); | |||||
b.Property<DateTime?>("Updated") | |||||
.HasColumnType("datetime2"); | |||||
b.HasKey("Id"); | |||||
b.HasIndex("Name") | |||||
.IsUnique(); | |||||
b.ToTable("IdentityResources", (string)null); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResourceProperty", b => | |||||
{ | |||||
b.Property<int>("Id") | |||||
.ValueGeneratedOnAdd() | |||||
.HasColumnType("int") | |||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); | |||||
b.Property<int>("IdentityResourceId") | |||||
.HasColumnType("int"); | |||||
b.Property<string>("Key") | |||||
.IsRequired() | |||||
.HasMaxLength(250) | |||||
.HasColumnType("nvarchar(250)"); | |||||
b.Property<string>("Value") | |||||
.IsRequired() | |||||
.HasMaxLength(2000) | |||||
.HasColumnType("nvarchar(2000)"); | |||||
b.HasKey("Id"); | |||||
b.HasIndex("IdentityResourceId"); | |||||
b.ToTable("IdentityProperties", (string)null); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceClaim", b => | |||||
{ | |||||
b.HasOne("IdentityServer4.EntityFramework.Entities.ApiResource", "ApiResource") | |||||
.WithMany("UserClaims") | |||||
.HasForeignKey("ApiResourceId") | |||||
.OnDelete(DeleteBehavior.Cascade) | |||||
.IsRequired(); | |||||
b.Navigation("ApiResource"); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceProperty", b => | |||||
{ | |||||
b.HasOne("IdentityServer4.EntityFramework.Entities.ApiResource", "ApiResource") | |||||
.WithMany("Properties") | |||||
.HasForeignKey("ApiResourceId") | |||||
.OnDelete(DeleteBehavior.Cascade) | |||||
.IsRequired(); | |||||
b.Navigation("ApiResource"); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScope", b => | |||||
{ | |||||
b.HasOne("IdentityServer4.EntityFramework.Entities.ApiResource", "ApiResource") | |||||
.WithMany("Scopes") | |||||
.HasForeignKey("ApiResourceId") | |||||
.OnDelete(DeleteBehavior.Cascade) | |||||
.IsRequired(); | |||||
b.Navigation("ApiResource"); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScopeClaim", b => | |||||
{ | |||||
b.HasOne("IdentityServer4.EntityFramework.Entities.ApiScope", "ApiScope") | |||||
.WithMany("UserClaims") | |||||
.HasForeignKey("ApiScopeId") | |||||
.OnDelete(DeleteBehavior.Cascade) | |||||
.IsRequired(); | |||||
b.Navigation("ApiScope"); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiSecret", b => | |||||
{ | |||||
b.HasOne("IdentityServer4.EntityFramework.Entities.ApiResource", "ApiResource") | |||||
.WithMany("Secrets") | |||||
.HasForeignKey("ApiResourceId") | |||||
.OnDelete(DeleteBehavior.Cascade) | |||||
.IsRequired(); | |||||
b.Navigation("ApiResource"); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientClaim", b => | |||||
{ | |||||
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") | |||||
.WithMany("Claims") | |||||
.HasForeignKey("ClientId") | |||||
.OnDelete(DeleteBehavior.Cascade) | |||||
.IsRequired(); | |||||
b.Navigation("Client"); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientCorsOrigin", b => | |||||
{ | |||||
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") | |||||
.WithMany("AllowedCorsOrigins") | |||||
.HasForeignKey("ClientId") | |||||
.OnDelete(DeleteBehavior.Cascade) | |||||
.IsRequired(); | |||||
b.Navigation("Client"); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientGrantType", b => | |||||
{ | |||||
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") | |||||
.WithMany("AllowedGrantTypes") | |||||
.HasForeignKey("ClientId") | |||||
.OnDelete(DeleteBehavior.Cascade) | |||||
.IsRequired(); | |||||
b.Navigation("Client"); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientIdPRestriction", b => | |||||
{ | |||||
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") | |||||
.WithMany("IdentityProviderRestrictions") | |||||
.HasForeignKey("ClientId") | |||||
.OnDelete(DeleteBehavior.Cascade) | |||||
.IsRequired(); | |||||
b.Navigation("Client"); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientPostLogoutRedirectUri", b => | |||||
{ | |||||
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") | |||||
.WithMany("PostLogoutRedirectUris") | |||||
.HasForeignKey("ClientId") | |||||
.OnDelete(DeleteBehavior.Cascade) | |||||
.IsRequired(); | |||||
b.Navigation("Client"); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientProperty", b => | |||||
{ | |||||
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") | |||||
.WithMany("Properties") | |||||
.HasForeignKey("ClientId") | |||||
.OnDelete(DeleteBehavior.Cascade) | |||||
.IsRequired(); | |||||
b.Navigation("Client"); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientRedirectUri", b => | |||||
{ | |||||
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") | |||||
.WithMany("RedirectUris") | |||||
.HasForeignKey("ClientId") | |||||
.OnDelete(DeleteBehavior.Cascade) | |||||
.IsRequired(); | |||||
b.Navigation("Client"); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientScope", b => | |||||
{ | |||||
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") | |||||
.WithMany("AllowedScopes") | |||||
.HasForeignKey("ClientId") | |||||
.OnDelete(DeleteBehavior.Cascade) | |||||
.IsRequired(); | |||||
b.Navigation("Client"); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientSecret", b => | |||||
{ | |||||
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") | |||||
.WithMany("ClientSecrets") | |||||
.HasForeignKey("ClientId") | |||||
.OnDelete(DeleteBehavior.Cascade) | |||||
.IsRequired(); | |||||
b.Navigation("Client"); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityClaim", b => | |||||
{ | |||||
b.HasOne("IdentityServer4.EntityFramework.Entities.IdentityResource", "IdentityResource") | |||||
.WithMany("UserClaims") | |||||
.HasForeignKey("IdentityResourceId") | |||||
.OnDelete(DeleteBehavior.Cascade) | |||||
.IsRequired(); | |||||
b.Navigation("IdentityResource"); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResourceProperty", b => | |||||
{ | |||||
b.HasOne("IdentityServer4.EntityFramework.Entities.IdentityResource", "IdentityResource") | |||||
.WithMany("Properties") | |||||
.HasForeignKey("IdentityResourceId") | |||||
.OnDelete(DeleteBehavior.Cascade) | |||||
.IsRequired(); | |||||
b.Navigation("IdentityResource"); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResource", b => | |||||
{ | |||||
b.Navigation("Properties"); | |||||
b.Navigation("Scopes"); | |||||
b.Navigation("Secrets"); | |||||
b.Navigation("UserClaims"); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScope", b => | |||||
{ | |||||
b.Navigation("UserClaims"); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.Client", b => | |||||
{ | |||||
b.Navigation("AllowedCorsOrigins"); | |||||
b.Navigation("AllowedGrantTypes"); | |||||
b.Navigation("AllowedScopes"); | |||||
b.Navigation("Claims"); | |||||
b.Navigation("ClientSecrets"); | |||||
b.Navigation("IdentityProviderRestrictions"); | |||||
b.Navigation("PostLogoutRedirectUris"); | |||||
b.Navigation("Properties"); | |||||
b.Navigation("RedirectUris"); | |||||
}); | |||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResource", b => | |||||
{ | |||||
b.Navigation("Properties"); | |||||
b.Navigation("UserClaims"); | |||||
}); | |||||
#pragma warning restore 612, 618 | |||||
} | |||||
} | |||||
} |
@ -1,108 +0,0 @@ | |||||
// <auto-generated /> | |||||
using System; | |||||
using IdentityServer4.EntityFramework.DbContexts; | |||||
using Microsoft.EntityFrameworkCore; | |||||
using Microsoft.EntityFrameworkCore.Infrastructure; | |||||
using Microsoft.EntityFrameworkCore.Metadata; | |||||
using Microsoft.EntityFrameworkCore.Migrations; | |||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion; | |||||
namespace Identity.API.Migrations.PersistedGrantDb | |||||
{ | |||||
[DbContext(typeof(PersistedGrantDbContext))] | |||||
[Migration("20210813072513_InitialMigration")] | |||||
partial class InitialMigration | |||||
{ | |||||
protected override void BuildTargetModel(ModelBuilder modelBuilder) | |||||
{ | |||||
#pragma warning disable 612, 618 | |||||
modelBuilder | |||||
.HasAnnotation("Relational:MaxIdentifierLength", 128) | |||||
.HasAnnotation("ProductVersion", "6.0.0") | |||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); | |||||
modelBuilder.Entity("IdentityServer4.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>("DeviceCode") | |||||
.IsRequired() | |||||
.HasMaxLength(200) | |||||
.HasColumnType("nvarchar(200)"); | |||||
b.Property<DateTime?>("Expiration") | |||||
.IsRequired() | |||||
.HasColumnType("datetime2"); | |||||
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("IdentityServer4.EntityFramework.Entities.PersistedGrant", b => | |||||
{ | |||||
b.Property<string>("Key") | |||||
.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<DateTime?>("Expiration") | |||||
.HasColumnType("datetime2"); | |||||
b.Property<string>("SubjectId") | |||||
.HasMaxLength(200) | |||||
.HasColumnType("nvarchar(200)"); | |||||
b.Property<string>("Type") | |||||
.IsRequired() | |||||
.HasMaxLength(50) | |||||
.HasColumnType("nvarchar(50)"); | |||||
b.HasKey("Key"); | |||||
b.HasIndex("Expiration"); | |||||
b.HasIndex("SubjectId", "ClientId", "Type"); | |||||
b.ToTable("PersistedGrants", (string)null); | |||||
}); | |||||
#pragma warning restore 612, 618 | |||||
} | |||||
} | |||||
} |
@ -1,75 +0,0 @@ | |||||
using System; | |||||
using Microsoft.EntityFrameworkCore.Migrations; | |||||
namespace Identity.API.Migrations.PersistedGrantDb | |||||
{ | |||||
public partial class InitialMigration : 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), | |||||
ClientId = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: false), | |||||
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: "PersistedGrants", | |||||
columns: table => new | |||||
{ | |||||
Key = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: false), | |||||
Type = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: false), | |||||
SubjectId = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: true), | |||||
ClientId = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: false), | |||||
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false), | |||||
Expiration = 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.Key); | |||||
}); | |||||
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_PersistedGrants_Expiration", | |||||
table: "PersistedGrants", | |||||
column: "Expiration"); | |||||
migrationBuilder.CreateIndex( | |||||
name: "IX_PersistedGrants_SubjectId_ClientId_Type", | |||||
table: "PersistedGrants", | |||||
columns: new[] { "SubjectId", "ClientId", "Type" }); | |||||
} | |||||
protected override void Down(MigrationBuilder migrationBuilder) | |||||
{ | |||||
migrationBuilder.DropTable( | |||||
name: "DeviceCodes"); | |||||
migrationBuilder.DropTable( | |||||
name: "PersistedGrants"); | |||||
} | |||||
} | |||||
} |
@ -1,106 +0,0 @@ | |||||
// <auto-generated /> | |||||
using System; | |||||
using IdentityServer4.EntityFramework.DbContexts; | |||||
using Microsoft.EntityFrameworkCore; | |||||
using Microsoft.EntityFrameworkCore.Infrastructure; | |||||
using Microsoft.EntityFrameworkCore.Metadata; | |||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion; | |||||
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("Relational:MaxIdentifierLength", 128) | |||||
.HasAnnotation("ProductVersion", "6.0.0") | |||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); | |||||
modelBuilder.Entity("IdentityServer4.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>("DeviceCode") | |||||
.IsRequired() | |||||
.HasMaxLength(200) | |||||
.HasColumnType("nvarchar(200)"); | |||||
b.Property<DateTime?>("Expiration") | |||||
.IsRequired() | |||||
.HasColumnType("datetime2"); | |||||
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("IdentityServer4.EntityFramework.Entities.PersistedGrant", b => | |||||
{ | |||||
b.Property<string>("Key") | |||||
.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<DateTime?>("Expiration") | |||||
.HasColumnType("datetime2"); | |||||
b.Property<string>("SubjectId") | |||||
.HasMaxLength(200) | |||||
.HasColumnType("nvarchar(200)"); | |||||
b.Property<string>("Type") | |||||
.IsRequired() | |||||
.HasMaxLength(50) | |||||
.HasColumnType("nvarchar(50)"); | |||||
b.HasKey("Key"); | |||||
b.HasIndex("Expiration"); | |||||
b.HasIndex("SubjectId", "ClientId", "Type"); | |||||
b.ToTable("PersistedGrants", (string)null); | |||||
}); | |||||
#pragma warning restore 612, 618 | |||||
} | |||||
} | |||||
} |
@ -1,10 +0,0 @@ | |||||
namespace Microsoft.eShopOnContainers.Services.Identity.API.Models.AccountViewModels | |||||
{ | |||||
public record ConsentInputModel | |||||
{ | |||||
public string Button { get; init; } | |||||
public IEnumerable<string> ScopesConsented { get; init; } | |||||
public bool RememberConsent { get; init; } | |||||
public string ReturnUrl { get; init; } | |||||
} | |||||
} |
@ -1,61 +0,0 @@ | |||||
using IdentityServer4.Models; | |||||
namespace Microsoft.eShopOnContainers.Services.Identity.API.Models.AccountViewModels | |||||
{ | |||||
public record ConsentViewModel : ConsentInputModel | |||||
{ | |||||
public ConsentViewModel(ConsentInputModel model, string returnUrl, AuthorizationRequest request, Client client, Resources resources) | |||||
{ | |||||
RememberConsent = model?.RememberConsent ?? true; | |||||
ScopesConsented = model?.ScopesConsented ?? Enumerable.Empty<string>(); | |||||
ReturnUrl = returnUrl; | |||||
ClientName = client.ClientName; | |||||
ClientUrl = client.ClientUri; | |||||
ClientLogoUrl = client.LogoUri; | |||||
AllowRememberConsent = client.AllowRememberConsent; | |||||
IdentityScopes = resources.IdentityResources.Select(x => new ScopeViewModel(x, ScopesConsented.Contains(x.Name) || model == null)).ToArray(); | |||||
ResourceScopes = resources.ApiResources.SelectMany(x => x.Scopes).Select(x => new ScopeViewModel(x, ScopesConsented.Contains(x.Name) || model == null)).ToArray(); | |||||
} | |||||
public string ClientName { get; init; } | |||||
public string ClientUrl { get; init; } | |||||
public string ClientLogoUrl { get; init; } | |||||
public bool AllowRememberConsent { get; init; } | |||||
public IEnumerable<ScopeViewModel> IdentityScopes { get; init; } | |||||
public IEnumerable<ScopeViewModel> ResourceScopes { get; init; } | |||||
} | |||||
public record ScopeViewModel | |||||
{ | |||||
public ScopeViewModel(Scope scope, bool check) | |||||
{ | |||||
Name = scope.Name; | |||||
DisplayName = scope.DisplayName; | |||||
Description = scope.Description; | |||||
Emphasize = scope.Emphasize; | |||||
Required = scope.Required; | |||||
Checked = check || scope.Required; | |||||
} | |||||
public ScopeViewModel(IdentityResource identity, bool check) | |||||
{ | |||||
Name = identity.Name; | |||||
DisplayName = identity.DisplayName; | |||||
Description = identity.Description; | |||||
Emphasize = identity.Emphasize; | |||||
Required = identity.Required; | |||||
Checked = check || identity.Required; | |||||
} | |||||
public string Name { get; init; } | |||||
public string DisplayName { get; init; } | |||||
public string Description { get; init; } | |||||
public bool Emphasize { get; init; } | |||||
public bool Required { get; init; } | |||||
public bool Checked { get; init; } | |||||
} | |||||
} |
@ -0,0 +1,7 @@ | |||||
namespace Microsoft.eShopOnContainers.Services.Identity.API.Models.AccountViewModels | |||||
{ | |||||
public class RedirectViewModel | |||||
{ | |||||
public string RedirectUrl { get; set; } | |||||
} | |||||
} |
@ -0,0 +1,13 @@ | |||||
using System.Collections.Generic; | |||||
namespace Microsoft.eShopOnContainers.Services.Identity.API.Models.ConsentViewModels | |||||
{ | |||||
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,12 @@ | |||||
namespace Microsoft.eShopOnContainers.Services.Identity.API.Models.ConsentViewModels | |||||
{ | |||||
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,15 @@ | |||||
using System.Collections.Generic; | |||||
namespace Microsoft.eShopOnContainers.Services.Identity.API.Models.ConsentViewModels | |||||
{ | |||||
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 @@ | |||||
using Duende.IdentityServer.Models; | |||||
namespace Microsoft.eShopOnContainers.Services.Identity.API.Models.ConsentViewModels | |||||
{ | |||||
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,12 @@ | |||||
namespace Microsoft.eShopOnContainers.Services.Identity.API.Models.ConsentViewModels | |||||
{ | |||||
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,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 = "null"; | |||||
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)); | |||||
} | |||||
} | |||||
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>(); | |||||
} |
@ -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); | |||||
} | |||||
} | |||||
} | |||||
} |
@ -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/24", | |||||
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/24", | |||||
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 |