update to Net7 and Duende IdentityServer 6.2pull/2044/head
@ -1,11 +1,11 @@ | |||
<Project Sdk="Microsoft.NET.Sdk"> | |||
<PropertyGroup> | |||
<TargetFramework>net6.0</TargetFramework> | |||
<TargetFramework>net7.0</TargetFramework> | |||
</PropertyGroup> | |||
<ItemGroup> | |||
<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> | |||
</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 Microsoft.EntityFrameworkCore.Migrations; | |||
namespace Identity.API.Migrations | |||
namespace Microsoft.eShopOnContainers.Services.Identity.API.Data.Migrations | |||
{ | |||
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 |