@ -0,0 +1,3 @@ | |||||
{ | |||||
"directory": "wwwroot/lib" | |||||
} |
@ -0,0 +1,12 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace eShopOnContainers.Identity.Configuration | |||||
{ | |||||
public class ClientCallBackUrls | |||||
{ | |||||
public string Spa { get; set; } | |||||
} | |||||
} |
@ -0,0 +1,68 @@ | |||||
using IdentityServer4.Models; | |||||
using Microsoft.Extensions.Options; | |||||
using System.Collections.Generic; | |||||
namespace eShopOnContainers.Identity.Configuration | |||||
{ | |||||
public class Config | |||||
{ | |||||
private readonly IOptions<ClientCallBackUrls> _settings; | |||||
public Config(IOptions<ClientCallBackUrls> settings) | |||||
{ | |||||
_settings = settings; | |||||
} | |||||
// scopes define the resources in your system | |||||
public static IEnumerable<Scope> GetScopes() | |||||
{ | |||||
return new List<Scope> | |||||
{ | |||||
//Authentication OpenId uses this scopes; | |||||
StandardScopes.OpenId, | |||||
StandardScopes.Profile, | |||||
//Each api we want to securice; | |||||
new Scope | |||||
{ | |||||
Name = "orders", | |||||
Description = "Orders Service" | |||||
}, | |||||
new Scope | |||||
{ | |||||
Name = "basket", | |||||
Description = "Basket Service" | |||||
} | |||||
}; | |||||
} | |||||
// client want to access resources (aka scopes) | |||||
public static IEnumerable<Client> GetClients() | |||||
{ | |||||
return new List<Client> | |||||
{ | |||||
// JavaScript Client | |||||
new Client | |||||
{ | |||||
ClientId = "js", | |||||
ClientName = "eShop SPA OpenId Client", | |||||
AllowedGrantTypes = GrantTypes.Implicit, | |||||
AllowAccessTokensViaBrowser = true, | |||||
RedirectUris = { "http://localhost:5003/callback.html" }, | |||||
PostLogoutRedirectUris = { "http://localhost:5003/index.html" }, | |||||
AllowedCorsOrigins = { "http://localhost:5003" }, | |||||
AllowedScopes = | |||||
{ | |||||
StandardScopes.OpenId.Name, | |||||
StandardScopes.Profile.Name, | |||||
"orders", | |||||
"basket" | |||||
} | |||||
} | |||||
}; | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,333 @@ | |||||
// 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 IdentityModel; | |||||
using IdentityServer4.Quickstart.UI.Models; | |||||
using IdentityServer4.Services; | |||||
using IdentityServer4.Services.InMemory; | |||||
using Microsoft.AspNetCore.Http.Authentication; | |||||
using Microsoft.AspNetCore.Mvc; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Security.Claims; | |||||
using System.Text.Encodings.Web; | |||||
using System.Threading.Tasks; | |||||
using IdentityServer4.Models; | |||||
using IdentityServer4.Stores; | |||||
using eShopOnContainers.Identity.Services; | |||||
using eShopOnContainers.Identity.Models; | |||||
using Microsoft.Extensions.Logging; | |||||
namespace IdentityServer4.Quickstart.UI.Controllers | |||||
{ | |||||
/// <summary> | |||||
/// This sample controller implements a typical login/logout/provision workflow for local and external 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 _logger; | |||||
public AccountController( | |||||
//InMemoryUserLoginService loginService, | |||||
ILoginService<ApplicationUser> loginService, | |||||
IIdentityServerInteractionService interaction, | |||||
IClientStore clientStore, | |||||
ILoggerFactory loggerFactory) | |||||
{ | |||||
_loginService = loginService; | |||||
_interaction = interaction; | |||||
_clientStore = clientStore; | |||||
_logger = loggerFactory.CreateLogger<AccountController>(); | |||||
} | |||||
/// <summary> | |||||
/// Show login page | |||||
/// </summary> | |||||
[HttpGet] | |||||
public async Task<IActionResult> Login(string returnUrl) | |||||
{ | |||||
var context = await _interaction.GetAuthorizationContextAsync(returnUrl); | |||||
if (context?.IdP != null) | |||||
{ | |||||
// if IdP is passed, then bypass showing the login screen | |||||
return ExternalLogin(context.IdP, returnUrl); | |||||
} | |||||
var vm = await BuildLoginViewModelAsync(returnUrl, context); | |||||
if (vm.EnableLocalLogin == false && vm.ExternalProviders.Count() == 1) | |||||
{ | |||||
// only one option for logging in | |||||
return ExternalLogin(vm.ExternalProviders.First().AuthenticationScheme, returnUrl); | |||||
} | |||||
return View(vm); | |||||
} | |||||
/// <summary> | |||||
/// Handle postback from username/password login | |||||
/// </summary> | |||||
[HttpPost] | |||||
[ValidateAntiForgeryToken] | |||||
public async Task<IActionResult> Login(LoginInputModel model) | |||||
{ | |||||
if (ModelState.IsValid) | |||||
{ | |||||
var user = await _loginService.FindByUsername(model.Username); | |||||
// validate username/password against in-memory store | |||||
if (await _loginService.ValidateCredentials(user, model.Password)) | |||||
{ | |||||
// issue authentication cookie with subject ID and username | |||||
//var user = _loginService.FindByUsername(model.Username); | |||||
AuthenticationProperties props = null; | |||||
// only set explicit expiration here if persistent. | |||||
// otherwise we reply upon expiration configured in cookie middleware. | |||||
if (model.RememberLogin) | |||||
{ | |||||
props = new AuthenticationProperties | |||||
{ | |||||
IsPersistent = true, | |||||
ExpiresUtc = DateTimeOffset.UtcNow.AddMonths(1) | |||||
}; | |||||
}; | |||||
//await HttpContext.Authentication.SignInAsync(, user.UserName, props); | |||||
await _loginService.SignIn(user); | |||||
// 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); | |||||
return View(vm); | |||||
} | |||||
async Task<LoginViewModel> BuildLoginViewModelAsync(string returnUrl, AuthorizationRequest context) | |||||
{ | |||||
var providers = HttpContext.Authentication.GetAuthenticationSchemes() | |||||
.Where(x => x.DisplayName != null) | |||||
.Select(x => new ExternalProvider | |||||
{ | |||||
DisplayName = x.DisplayName, | |||||
AuthenticationScheme = x.AuthenticationScheme | |||||
}); | |||||
var allowLocal = true; | |||||
if (context?.ClientId != null) | |||||
{ | |||||
var client = await _clientStore.FindEnabledClientByIdAsync(context.ClientId); | |||||
if (client != null) | |||||
{ | |||||
allowLocal = client.EnableLocalLogin; | |||||
if (client.IdentityProviderRestrictions != null && client.IdentityProviderRestrictions.Any()) | |||||
{ | |||||
providers = providers.Where(provider => client.IdentityProviderRestrictions.Contains(provider.AuthenticationScheme)); | |||||
} | |||||
} | |||||
} | |||||
return new LoginViewModel | |||||
{ | |||||
EnableLocalLogin = allowLocal, | |||||
ReturnUrl = returnUrl, | |||||
Username = context?.LoginHint, | |||||
ExternalProviders = providers.ToArray() | |||||
}; | |||||
} | |||||
async Task<LoginViewModel> BuildLoginViewModelAsync(LoginInputModel model) | |||||
{ | |||||
var context = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl); | |||||
var vm = await BuildLoginViewModelAsync(model.ReturnUrl, context); | |||||
vm.Username = model.Username; | |||||
vm.RememberLogin = model.RememberLogin; | |||||
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 }); | |||||
} | |||||
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.Authentication.SignOutAsync(idp, new AuthenticationProperties { RedirectUri = url }); | |||||
} | |||||
catch(NotSupportedException) | |||||
{ | |||||
} | |||||
} | |||||
// delete authentication cookie | |||||
await HttpContext.Authentication.SignOutAsync(); | |||||
// 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); | |||||
var vm = new LoggedOutViewModel | |||||
{ | |||||
PostLogoutRedirectUri = logout?.PostLogoutRedirectUri, | |||||
ClientName = logout?.ClientId, | |||||
SignOutIframeUrl = logout?.SignOutIFrameUrl | |||||
}; | |||||
return View("LoggedOut", vm); | |||||
} | |||||
/// <summary> | |||||
/// initiate roundtrip to external authentication provider | |||||
/// </summary> | |||||
[HttpGet] | |||||
public IActionResult ExternalLogin(string provider, string returnUrl) | |||||
{ | |||||
if (returnUrl != null) | |||||
{ | |||||
returnUrl = UrlEncoder.Default.Encode(returnUrl); | |||||
} | |||||
returnUrl = "/account/externallogincallback?returnUrl=" + returnUrl; | |||||
// start challenge and roundtrip the return URL | |||||
var props = new AuthenticationProperties | |||||
{ | |||||
RedirectUri = returnUrl, | |||||
Items = { { "scheme", provider } } | |||||
}; | |||||
return new ChallengeResult(provider, props); | |||||
} | |||||
/// <summary> | |||||
/// Post processing of external authentication | |||||
/// </summary> | |||||
[HttpGet] | |||||
public async Task<IActionResult> ExternalLoginCallback(string returnUrl) | |||||
{ | |||||
// read external identity from the temporary cookie | |||||
var info = await HttpContext.Authentication.GetAuthenticateInfoAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme); | |||||
//var tempUser = info?.Principal; | |||||
//if (tempUser == null) | |||||
//{ | |||||
// throw new Exception("External authentication error"); | |||||
//} | |||||
//// retrieve claims of the external user | |||||
//var claims = tempUser.Claims.ToList(); | |||||
//// try to determine the unique id of the external user - 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 = claims.FirstOrDefault(x => x.Type == JwtClaimTypes.Subject); | |||||
//if (userIdClaim == null) | |||||
//{ | |||||
// userIdClaim = claims.FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier); | |||||
//} | |||||
//if (userIdClaim == null) | |||||
//{ | |||||
// throw new Exception("Unknown userid"); | |||||
//} | |||||
//// remove the user id claim from the claims collection and move to the userId property | |||||
//// also set the name of the external authentication provider | |||||
//claims.Remove(userIdClaim); | |||||
//var provider = info.Properties.Items["scheme"]; | |||||
//var userId = userIdClaim.Value; | |||||
//// check if the external user is already provisioned | |||||
//var user = _loginService.FindByExternalProvider(provider, userId); | |||||
//if (user == null) | |||||
//{ | |||||
// // this sample simply auto-provisions new external user | |||||
// // another common approach is to start a registrations workflow first | |||||
// user = _loginService.AutoProvisionUser(provider, userId, claims); | |||||
//} | |||||
//var additionalClaims = new List<Claim>(); | |||||
//// if the external system sent a session id claim, copy it over | |||||
//var sid = claims.FirstOrDefault(x => x.Type == JwtClaimTypes.SessionId); | |||||
//if (sid != null) | |||||
//{ | |||||
// additionalClaims.Add(new Claim(JwtClaimTypes.SessionId, sid.Value)); | |||||
//} | |||||
//// issue authentication cookie for user | |||||
//await HttpContext.Authentication.SignInAsync(user.Subject, user.Username, provider, additionalClaims.ToArray()); | |||||
//// delete temporary cookie used during external authentication | |||||
//await HttpContext.Authentication.SignOutAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme); | |||||
//// validate return URL and redirect back to authorization endpoint | |||||
//if (_interaction.IsValidReturnUrl(returnUrl)) | |||||
//{ | |||||
// return Redirect(returnUrl); | |||||
//} | |||||
return Redirect("~/"); | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,142 @@ | |||||
// 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 IdentityServer4.Services; | |||||
using Microsoft.AspNetCore.Mvc; | |||||
using Microsoft.Extensions.Logging; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
using IdentityServer4.Models; | |||||
using IdentityServer4.Stores; | |||||
using IdentityServer4.Quickstart.UI.Models; | |||||
namespace IdentityServer4.Quickstart.UI.Controllers | |||||
{ | |||||
/// <summary> | |||||
/// This controller implements the consent logic | |||||
/// </summary> | |||||
public class ConsentController : Controller | |||||
{ | |||||
private readonly ILogger<ConsentController> _logger; | |||||
private readonly IClientStore _clientStore; | |||||
private readonly IScopeStore _scopeStore; | |||||
private readonly IIdentityServerInteractionService _interaction; | |||||
public ConsentController( | |||||
ILogger<ConsentController> logger, | |||||
IIdentityServerInteractionService interaction, | |||||
IClientStore clientStore, | |||||
IScopeStore scopeStore) | |||||
{ | |||||
_logger = logger; | |||||
_interaction = interaction; | |||||
_clientStore = clientStore; | |||||
_scopeStore = scopeStore; | |||||
} | |||||
/// <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) | |||||
{ | |||||
// 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" && model != null) | |||||
{ | |||||
// 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 scopes = await _scopeStore.FindEnabledScopesAsync(request.ScopesRequested); | |||||
if (scopes != null && scopes.Any()) | |||||
{ | |||||
return new ConsentViewModel(model, returnUrl, request, client, scopes); | |||||
} | |||||
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; | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,43 @@ | |||||
// 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 IdentityServer4.Quickstart.UI.Models; | |||||
using IdentityServer4.Services; | |||||
using Microsoft.AspNetCore.Mvc; | |||||
using System.Threading.Tasks; | |||||
namespace IdentityServer4.Quickstart.UI.Controllers | |||||
{ | |||||
public class HomeController : Controller | |||||
{ | |||||
private readonly IIdentityServerInteractionService _interaction; | |||||
public HomeController(IIdentityServerInteractionService interaction) | |||||
{ | |||||
_interaction = interaction; | |||||
} | |||||
public IActionResult Index() | |||||
{ | |||||
return View(); | |||||
} | |||||
/// <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); | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,360 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
using Microsoft.AspNetCore.Authorization; | |||||
using Microsoft.AspNetCore.Identity; | |||||
using Microsoft.AspNetCore.Mvc; | |||||
using Microsoft.Extensions.Logging; | |||||
using eShopOnContainers.Identity.Models; | |||||
using eShopOnContainers.Identity.Models.ManageViewModels; | |||||
using eShopOnContainers.Identity.Services; | |||||
namespace eShopOnContainers.Identity.Controllers | |||||
{ | |||||
[Authorize] | |||||
public class ManageController : Controller | |||||
{ | |||||
private readonly UserManager<ApplicationUser> _userManager; | |||||
private readonly SignInManager<ApplicationUser> _signInManager; | |||||
private readonly IEmailSender _emailSender; | |||||
private readonly ISmsSender _smsSender; | |||||
private readonly ILogger _logger; | |||||
public ManageController( | |||||
UserManager<ApplicationUser> userManager, | |||||
SignInManager<ApplicationUser> signInManager, | |||||
IEmailSender emailSender, | |||||
ISmsSender smsSender, | |||||
ILoggerFactory loggerFactory) | |||||
{ | |||||
_userManager = userManager; | |||||
_signInManager = signInManager; | |||||
_emailSender = emailSender; | |||||
_smsSender = smsSender; | |||||
_logger = loggerFactory.CreateLogger<ManageController>(); | |||||
} | |||||
// | |||||
// GET: /Manage/Index | |||||
[HttpGet] | |||||
public async Task<IActionResult> Index(ManageMessageId? message = null) | |||||
{ | |||||
ViewData["StatusMessage"] = | |||||
message == ManageMessageId.ChangePasswordSuccess ? "Your password has been changed." | |||||
: message == ManageMessageId.SetPasswordSuccess ? "Your password has been set." | |||||
: message == ManageMessageId.SetTwoFactorSuccess ? "Your two-factor authentication provider has been set." | |||||
: message == ManageMessageId.Error ? "An error has occurred." | |||||
: message == ManageMessageId.AddPhoneSuccess ? "Your phone number was added." | |||||
: message == ManageMessageId.RemovePhoneSuccess ? "Your phone number was removed." | |||||
: ""; | |||||
var user = await GetCurrentUserAsync(); | |||||
if (user == null) | |||||
{ | |||||
return View("Error"); | |||||
} | |||||
var model = new IndexViewModel | |||||
{ | |||||
HasPassword = await _userManager.HasPasswordAsync(user), | |||||
PhoneNumber = await _userManager.GetPhoneNumberAsync(user), | |||||
TwoFactor = await _userManager.GetTwoFactorEnabledAsync(user), | |||||
Logins = await _userManager.GetLoginsAsync(user), | |||||
BrowserRemembered = await _signInManager.IsTwoFactorClientRememberedAsync(user) | |||||
}; | |||||
return View(model); | |||||
} | |||||
// | |||||
// POST: /Manage/RemoveLogin | |||||
[HttpPost] | |||||
[ValidateAntiForgeryToken] | |||||
public async Task<IActionResult> RemoveLogin(RemoveLoginViewModel account) | |||||
{ | |||||
ManageMessageId? message = ManageMessageId.Error; | |||||
var user = await GetCurrentUserAsync(); | |||||
if (user != null) | |||||
{ | |||||
var result = await _userManager.RemoveLoginAsync(user, account.LoginProvider, account.ProviderKey); | |||||
if (result.Succeeded) | |||||
{ | |||||
await _signInManager.SignInAsync(user, isPersistent: false); | |||||
message = ManageMessageId.RemoveLoginSuccess; | |||||
} | |||||
} | |||||
return RedirectToAction(nameof(ManageLogins), new { Message = message }); | |||||
} | |||||
// | |||||
// GET: /Manage/AddPhoneNumber | |||||
public IActionResult AddPhoneNumber() | |||||
{ | |||||
return View(); | |||||
} | |||||
// | |||||
// POST: /Manage/AddPhoneNumber | |||||
[HttpPost] | |||||
[ValidateAntiForgeryToken] | |||||
public async Task<IActionResult> AddPhoneNumber(AddPhoneNumberViewModel model) | |||||
{ | |||||
if (!ModelState.IsValid) | |||||
{ | |||||
return View(model); | |||||
} | |||||
// Generate the token and send it | |||||
var user = await GetCurrentUserAsync(); | |||||
if (user == null) | |||||
{ | |||||
return View("Error"); | |||||
} | |||||
var code = await _userManager.GenerateChangePhoneNumberTokenAsync(user, model.PhoneNumber); | |||||
await _smsSender.SendSmsAsync(model.PhoneNumber, "Your security code is: " + code); | |||||
return RedirectToAction(nameof(VerifyPhoneNumber), new { PhoneNumber = model.PhoneNumber }); | |||||
} | |||||
// | |||||
// POST: /Manage/EnableTwoFactorAuthentication | |||||
[HttpPost] | |||||
[ValidateAntiForgeryToken] | |||||
public async Task<IActionResult> EnableTwoFactorAuthentication() | |||||
{ | |||||
var user = await GetCurrentUserAsync(); | |||||
if (user != null) | |||||
{ | |||||
await _userManager.SetTwoFactorEnabledAsync(user, true); | |||||
await _signInManager.SignInAsync(user, isPersistent: false); | |||||
_logger.LogInformation(1, "User enabled two-factor authentication."); | |||||
} | |||||
return RedirectToAction(nameof(Index), "Manage"); | |||||
} | |||||
// | |||||
// POST: /Manage/DisableTwoFactorAuthentication | |||||
[HttpPost] | |||||
[ValidateAntiForgeryToken] | |||||
public async Task<IActionResult> DisableTwoFactorAuthentication() | |||||
{ | |||||
var user = await GetCurrentUserAsync(); | |||||
if (user != null) | |||||
{ | |||||
await _userManager.SetTwoFactorEnabledAsync(user, false); | |||||
await _signInManager.SignInAsync(user, isPersistent: false); | |||||
_logger.LogInformation(2, "User disabled two-factor authentication."); | |||||
} | |||||
return RedirectToAction(nameof(Index), "Manage"); | |||||
} | |||||
// | |||||
// GET: /Manage/VerifyPhoneNumber | |||||
[HttpGet] | |||||
public async Task<IActionResult> VerifyPhoneNumber(string phoneNumber) | |||||
{ | |||||
var user = await GetCurrentUserAsync(); | |||||
if (user == null) | |||||
{ | |||||
return View("Error"); | |||||
} | |||||
var code = await _userManager.GenerateChangePhoneNumberTokenAsync(user, phoneNumber); | |||||
// Send an SMS to verify the phone number | |||||
return phoneNumber == null ? View("Error") : View(new VerifyPhoneNumberViewModel { PhoneNumber = phoneNumber }); | |||||
} | |||||
// | |||||
// POST: /Manage/VerifyPhoneNumber | |||||
[HttpPost] | |||||
[ValidateAntiForgeryToken] | |||||
public async Task<IActionResult> VerifyPhoneNumber(VerifyPhoneNumberViewModel model) | |||||
{ | |||||
if (!ModelState.IsValid) | |||||
{ | |||||
return View(model); | |||||
} | |||||
var user = await GetCurrentUserAsync(); | |||||
if (user != null) | |||||
{ | |||||
var result = await _userManager.ChangePhoneNumberAsync(user, model.PhoneNumber, model.Code); | |||||
if (result.Succeeded) | |||||
{ | |||||
await _signInManager.SignInAsync(user, isPersistent: false); | |||||
return RedirectToAction(nameof(Index), new { Message = ManageMessageId.AddPhoneSuccess }); | |||||
} | |||||
} | |||||
// If we got this far, something failed, redisplay the form | |||||
ModelState.AddModelError(string.Empty, "Failed to verify phone number"); | |||||
return View(model); | |||||
} | |||||
// | |||||
// POST: /Manage/RemovePhoneNumber | |||||
[HttpPost] | |||||
[ValidateAntiForgeryToken] | |||||
public async Task<IActionResult> RemovePhoneNumber() | |||||
{ | |||||
var user = await GetCurrentUserAsync(); | |||||
if (user != null) | |||||
{ | |||||
var result = await _userManager.SetPhoneNumberAsync(user, null); | |||||
if (result.Succeeded) | |||||
{ | |||||
await _signInManager.SignInAsync(user, isPersistent: false); | |||||
return RedirectToAction(nameof(Index), new { Message = ManageMessageId.RemovePhoneSuccess }); | |||||
} | |||||
} | |||||
return RedirectToAction(nameof(Index), new { Message = ManageMessageId.Error }); | |||||
} | |||||
// | |||||
// GET: /Manage/ChangePassword | |||||
[HttpGet] | |||||
public IActionResult ChangePassword() | |||||
{ | |||||
return View(); | |||||
} | |||||
// | |||||
// POST: /Manage/ChangePassword | |||||
[HttpPost] | |||||
[ValidateAntiForgeryToken] | |||||
public async Task<IActionResult> ChangePassword(ChangePasswordViewModel model) | |||||
{ | |||||
if (!ModelState.IsValid) | |||||
{ | |||||
return View(model); | |||||
} | |||||
var user = await GetCurrentUserAsync(); | |||||
if (user != null) | |||||
{ | |||||
var result = await _userManager.ChangePasswordAsync(user, model.OldPassword, model.NewPassword); | |||||
if (result.Succeeded) | |||||
{ | |||||
await _signInManager.SignInAsync(user, isPersistent: false); | |||||
_logger.LogInformation(3, "User changed their password successfully."); | |||||
return RedirectToAction(nameof(Index), new { Message = ManageMessageId.ChangePasswordSuccess }); | |||||
} | |||||
AddErrors(result); | |||||
return View(model); | |||||
} | |||||
return RedirectToAction(nameof(Index), new { Message = ManageMessageId.Error }); | |||||
} | |||||
// | |||||
// GET: /Manage/SetPassword | |||||
[HttpGet] | |||||
public IActionResult SetPassword() | |||||
{ | |||||
return View(); | |||||
} | |||||
// | |||||
// POST: /Manage/SetPassword | |||||
[HttpPost] | |||||
[ValidateAntiForgeryToken] | |||||
public async Task<IActionResult> SetPassword(SetPasswordViewModel model) | |||||
{ | |||||
if (!ModelState.IsValid) | |||||
{ | |||||
return View(model); | |||||
} | |||||
var user = await GetCurrentUserAsync(); | |||||
if (user != null) | |||||
{ | |||||
var result = await _userManager.AddPasswordAsync(user, model.NewPassword); | |||||
if (result.Succeeded) | |||||
{ | |||||
await _signInManager.SignInAsync(user, isPersistent: false); | |||||
return RedirectToAction(nameof(Index), new { Message = ManageMessageId.SetPasswordSuccess }); | |||||
} | |||||
AddErrors(result); | |||||
return View(model); | |||||
} | |||||
return RedirectToAction(nameof(Index), new { Message = ManageMessageId.Error }); | |||||
} | |||||
//GET: /Manage/ManageLogins | |||||
[HttpGet] | |||||
public async Task<IActionResult> ManageLogins(ManageMessageId? message = null) | |||||
{ | |||||
ViewData["StatusMessage"] = | |||||
message == ManageMessageId.RemoveLoginSuccess ? "The external login was removed." | |||||
: message == ManageMessageId.AddLoginSuccess ? "The external login was added." | |||||
: message == ManageMessageId.Error ? "An error has occurred." | |||||
: ""; | |||||
var user = await GetCurrentUserAsync(); | |||||
if (user == null) | |||||
{ | |||||
return View("Error"); | |||||
} | |||||
var userLogins = await _userManager.GetLoginsAsync(user); | |||||
var otherLogins = _signInManager.GetExternalAuthenticationSchemes().Where(auth => userLogins.All(ul => auth.AuthenticationScheme != ul.LoginProvider)).ToList(); | |||||
ViewData["ShowRemoveButton"] = user.PasswordHash != null || userLogins.Count > 1; | |||||
return View(new ManageLoginsViewModel | |||||
{ | |||||
CurrentLogins = userLogins, | |||||
OtherLogins = otherLogins | |||||
}); | |||||
} | |||||
// | |||||
// POST: /Manage/LinkLogin | |||||
[HttpPost] | |||||
[ValidateAntiForgeryToken] | |||||
public IActionResult LinkLogin(string provider) | |||||
{ | |||||
// Request a redirect to the external login provider to link a login for the current user | |||||
var redirectUrl = Url.Action("LinkLoginCallback", "Manage"); | |||||
var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl, _userManager.GetUserId(User)); | |||||
return Challenge(properties, provider); | |||||
} | |||||
// | |||||
// GET: /Manage/LinkLoginCallback | |||||
[HttpGet] | |||||
public async Task<ActionResult> LinkLoginCallback() | |||||
{ | |||||
var user = await GetCurrentUserAsync(); | |||||
if (user == null) | |||||
{ | |||||
return View("Error"); | |||||
} | |||||
var info = await _signInManager.GetExternalLoginInfoAsync(await _userManager.GetUserIdAsync(user)); | |||||
if (info == null) | |||||
{ | |||||
return RedirectToAction(nameof(ManageLogins), new { Message = ManageMessageId.Error }); | |||||
} | |||||
var result = await _userManager.AddLoginAsync(user, info); | |||||
var message = result.Succeeded ? ManageMessageId.AddLoginSuccess : ManageMessageId.Error; | |||||
return RedirectToAction(nameof(ManageLogins), new { Message = message }); | |||||
} | |||||
#region Helpers | |||||
private void AddErrors(IdentityResult result) | |||||
{ | |||||
foreach (var error in result.Errors) | |||||
{ | |||||
ModelState.AddModelError(string.Empty, error.Description); | |||||
} | |||||
} | |||||
public enum ManageMessageId | |||||
{ | |||||
AddPhoneSuccess, | |||||
AddLoginSuccess, | |||||
ChangePasswordSuccess, | |||||
SetTwoFactorSuccess, | |||||
SetPasswordSuccess, | |||||
RemoveLoginSuccess, | |||||
RemovePhoneSuccess, | |||||
Error | |||||
} | |||||
private Task<ApplicationUser> GetCurrentUserAsync() | |||||
{ | |||||
return _userManager.GetUserAsync(HttpContext.User); | |||||
} | |||||
#endregion | |||||
} | |||||
} |
@ -0,0 +1,26 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
using Microsoft.AspNetCore.Identity.EntityFrameworkCore; | |||||
using Microsoft.EntityFrameworkCore; | |||||
using eShopOnContainers.Identity.Models; | |||||
namespace eShopOnContainers.Identity.Data | |||||
{ | |||||
public class ApplicationDbContext : IdentityDbContext<ApplicationUser> | |||||
{ | |||||
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); | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,7 @@ | |||||
FROM microsoft/aspnetcore:1.0.1 | |||||
ENTRYPOINT ["dotnet", "eShopOnContainers.Identity.dll"] | |||||
ARG source=. | |||||
WORKDIR /app | |||||
ENV ASPNETCORE_URLS http://*:80 | |||||
EXPOSE 80 | |||||
COPY $source . |
@ -0,0 +1,15 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.ComponentModel.DataAnnotations; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace eShopOnContainers.Identity.Models.AccountViewModels | |||||
{ | |||||
public class ExternalLoginConfirmationViewModel | |||||
{ | |||||
[Required] | |||||
[EmailAddress] | |||||
public string Email { get; set; } | |||||
} | |||||
} |
@ -0,0 +1,15 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.ComponentModel.DataAnnotations; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace eShopOnContainers.Identity.Models.AccountViewModels | |||||
{ | |||||
public class ForgotPasswordViewModel | |||||
{ | |||||
[Required] | |||||
[EmailAddress] | |||||
public string Email { get; set; } | |||||
} | |||||
} |
@ -0,0 +1,22 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.ComponentModel.DataAnnotations; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace eShopOnContainers.Identity.Models.AccountViewModels | |||||
{ | |||||
public class LoginViewModel | |||||
{ | |||||
[Required] | |||||
[EmailAddress] | |||||
public string Email { get; set; } | |||||
[Required] | |||||
[DataType(DataType.Password)] | |||||
public string Password { get; set; } | |||||
[Display(Name = "Remember me?")] | |||||
public bool RememberMe { get; set; } | |||||
} | |||||
} |
@ -0,0 +1,27 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.ComponentModel.DataAnnotations; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace eShopOnContainers.Identity.Models.AccountViewModels | |||||
{ | |||||
public class RegisterViewModel | |||||
{ | |||||
[Required] | |||||
[EmailAddress] | |||||
[Display(Name = "Email")] | |||||
public string Email { get; set; } | |||||
[Required] | |||||
[StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] | |||||
[DataType(DataType.Password)] | |||||
[Display(Name = "Password")] | |||||
public string Password { get; set; } | |||||
[DataType(DataType.Password)] | |||||
[Display(Name = "Confirm password")] | |||||
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] | |||||
public string ConfirmPassword { get; set; } | |||||
} | |||||
} |
@ -0,0 +1,27 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.ComponentModel.DataAnnotations; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace eShopOnContainers.Identity.Models.AccountViewModels | |||||
{ | |||||
public class ResetPasswordViewModel | |||||
{ | |||||
[Required] | |||||
[EmailAddress] | |||||
public string Email { get; set; } | |||||
[Required] | |||||
[StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] | |||||
[DataType(DataType.Password)] | |||||
public string Password { get; set; } | |||||
[DataType(DataType.Password)] | |||||
[Display(Name = "Confirm password")] | |||||
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] | |||||
public string ConfirmPassword { get; set; } | |||||
public string Code { get; set; } | |||||
} | |||||
} |
@ -0,0 +1,19 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
using Microsoft.AspNetCore.Mvc.Rendering; | |||||
namespace eShopOnContainers.Identity.Models.AccountViewModels | |||||
{ | |||||
public class SendCodeViewModel | |||||
{ | |||||
public string SelectedProvider { get; set; } | |||||
public ICollection<SelectListItem> Providers { get; set; } | |||||
public string ReturnUrl { get; set; } | |||||
public bool RememberMe { get; set; } | |||||
} | |||||
} |
@ -0,0 +1,25 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.ComponentModel.DataAnnotations; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace eShopOnContainers.Identity.Models.AccountViewModels | |||||
{ | |||||
public class VerifyCodeViewModel | |||||
{ | |||||
[Required] | |||||
public string Provider { get; set; } | |||||
[Required] | |||||
public string Code { get; set; } | |||||
public string ReturnUrl { get; set; } | |||||
[Display(Name = "Remember this browser?")] | |||||
public bool RememberBrowser { get; set; } | |||||
[Display(Name = "Remember me?")] | |||||
public bool RememberMe { get; set; } | |||||
} | |||||
} |
@ -0,0 +1,30 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
using Microsoft.AspNetCore.Identity.EntityFrameworkCore; | |||||
using System.ComponentModel.DataAnnotations; | |||||
namespace eShopOnContainers.Identity.Models | |||||
{ | |||||
// Add profile data for application users by adding properties to the ApplicationUser class | |||||
public class ApplicationUser : IdentityUser | |||||
{ | |||||
public string CardNumber { get; set; } | |||||
public string SecurityNumber { get; set; } | |||||
public string Expiration { get; set; } | |||||
public string CardHolderName { get; set; } | |||||
public int CardType { get; set; } | |||||
public string Street { get; set; } | |||||
public string City { get; set; } | |||||
public string State { get; set; } | |||||
public string StateCode { get; set; } | |||||
public string Country { get; set; } | |||||
public string CountryCode { get; set; } | |||||
public string ZipCode { get; set; } | |||||
public double Latitude { get; set; } | |||||
public double Longitude { get; set; } | |||||
public string Name { get; set; } | |||||
public string LastName { get; set; } | |||||
} | |||||
} |
@ -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. | |||||
using System.Collections.Generic; | |||||
namespace IdentityServer4.Quickstart.UI.Models | |||||
{ | |||||
public class ConsentInputModel | |||||
{ | |||||
public string Button { get; set; } | |||||
public IEnumerable<string> ScopesConsented { get; set; } | |||||
public bool RememberConsent { get; set; } | |||||
public string ReturnUrl { get; set; } | |||||
} | |||||
} |
@ -0,0 +1,57 @@ | |||||
// 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.Collections.Generic; | |||||
using System.Linq; | |||||
using IdentityServer4.Models; | |||||
namespace IdentityServer4.Quickstart.UI.Models | |||||
{ | |||||
public class ConsentViewModel : ConsentInputModel | |||||
{ | |||||
public ConsentViewModel(ConsentInputModel model, string returnUrl, AuthorizationRequest request, Client client, IEnumerable<Scope> scopes) | |||||
{ | |||||
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 = scopes.Where(x => x.Type == ScopeType.Identity).Select(x => new ScopeViewModel(x, ScopesConsented.Contains(x.Name) || model == null)).ToArray(); | |||||
ResourceScopes = scopes.Where(x => x.Type == ScopeType.Resource).Select(x => new ScopeViewModel(x, ScopesConsented.Contains(x.Name) || model == null)).ToArray(); | |||||
} | |||||
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> ResourceScopes { get; set; } | |||||
} | |||||
public class 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 string Name { 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,13 @@ | |||||
// 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 IdentityServer4.Models; | |||||
namespace IdentityServer4.Quickstart.UI.Models | |||||
{ | |||||
public class ErrorViewModel | |||||
{ | |||||
public ErrorMessage Error { get; set; } | |||||
} | |||||
} |
@ -0,0 +1,13 @@ | |||||
// 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 IdentityServer4.Quickstart.UI.Models | |||||
{ | |||||
public class LoggedOutViewModel | |||||
{ | |||||
public string PostLogoutRedirectUri { get; set; } | |||||
public string ClientName { get; set; } | |||||
public string SignOutIframeUrl { 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. | |||||
using System.ComponentModel.DataAnnotations; | |||||
namespace IdentityServer4.Quickstart.UI.Models | |||||
{ | |||||
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,20 @@ | |||||
// 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.Collections.Generic; | |||||
namespace IdentityServer4.Quickstart.UI.Models | |||||
{ | |||||
public class LoginViewModel : LoginInputModel | |||||
{ | |||||
public bool EnableLocalLogin { get; set; } | |||||
public IEnumerable<ExternalProvider> ExternalProviders { get; set; } | |||||
} | |||||
public class ExternalProvider | |||||
{ | |||||
public string DisplayName { get; set; } | |||||
public string AuthenticationScheme { 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 IdentityServer4.Quickstart.UI.Models | |||||
{ | |||||
public class LogoutViewModel | |||||
{ | |||||
public string LogoutId { get; set; } | |||||
} | |||||
} |
@ -0,0 +1,16 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.ComponentModel.DataAnnotations; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace eShopOnContainers.Identity.Models.ManageViewModels | |||||
{ | |||||
public class AddPhoneNumberViewModel | |||||
{ | |||||
[Required] | |||||
[Phone] | |||||
[Display(Name = "Phone number")] | |||||
public string PhoneNumber { get; set; } | |||||
} | |||||
} |
@ -0,0 +1,27 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.ComponentModel.DataAnnotations; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace eShopOnContainers.Identity.Models.ManageViewModels | |||||
{ | |||||
public class ChangePasswordViewModel | |||||
{ | |||||
[Required] | |||||
[DataType(DataType.Password)] | |||||
[Display(Name = "Current password")] | |||||
public string OldPassword { get; set; } | |||||
[Required] | |||||
[StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] | |||||
[DataType(DataType.Password)] | |||||
[Display(Name = "New password")] | |||||
public string NewPassword { get; set; } | |||||
[DataType(DataType.Password)] | |||||
[Display(Name = "Confirm new password")] | |||||
[Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")] | |||||
public string ConfirmPassword { get; set; } | |||||
} | |||||
} |
@ -0,0 +1,15 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
using Microsoft.AspNetCore.Mvc.Rendering; | |||||
namespace eShopOnContainers.Identity.Models.ManageViewModels | |||||
{ | |||||
public class ConfigureTwoFactorViewModel | |||||
{ | |||||
public string SelectedProvider { get; set; } | |||||
public ICollection<SelectListItem> Providers { get; set; } | |||||
} | |||||
} |
@ -0,0 +1,12 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace eShopOnContainers.Identity.Models.ManageViewModels | |||||
{ | |||||
public class FactorViewModel | |||||
{ | |||||
public string Purpose { get; set; } | |||||
} | |||||
} |
@ -0,0 +1,21 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
using Microsoft.AspNetCore.Identity; | |||||
namespace eShopOnContainers.Identity.Models.ManageViewModels | |||||
{ | |||||
public class IndexViewModel | |||||
{ | |||||
public bool HasPassword { get; set; } | |||||
public IList<UserLoginInfo> Logins { get; set; } | |||||
public string PhoneNumber { get; set; } | |||||
public bool TwoFactor { get; set; } | |||||
public bool BrowserRemembered { get; set; } | |||||
} | |||||
} |
@ -0,0 +1,16 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
using Microsoft.AspNetCore.Http.Authentication; | |||||
using Microsoft.AspNetCore.Identity; | |||||
namespace eShopOnContainers.Identity.Models.ManageViewModels | |||||
{ | |||||
public class ManageLoginsViewModel | |||||
{ | |||||
public IList<UserLoginInfo> CurrentLogins { get; set; } | |||||
public IList<AuthenticationDescription> OtherLogins { get; set; } | |||||
} | |||||
} |
@ -0,0 +1,14 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.ComponentModel.DataAnnotations; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace eShopOnContainers.Identity.Models.ManageViewModels | |||||
{ | |||||
public class RemoveLoginViewModel | |||||
{ | |||||
public string LoginProvider { get; set; } | |||||
public string ProviderKey { get; set; } | |||||
} | |||||
} |
@ -0,0 +1,22 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.ComponentModel.DataAnnotations; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace eShopOnContainers.Identity.Models.ManageViewModels | |||||
{ | |||||
public class SetPasswordViewModel | |||||
{ | |||||
[Required] | |||||
[StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] | |||||
[DataType(DataType.Password)] | |||||
[Display(Name = "New password")] | |||||
public string NewPassword { get; set; } | |||||
[DataType(DataType.Password)] | |||||
[Display(Name = "Confirm new password")] | |||||
[Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")] | |||||
public string ConfirmPassword { get; set; } | |||||
} | |||||
} |
@ -0,0 +1,19 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.ComponentModel.DataAnnotations; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace eShopOnContainers.Identity.Models.ManageViewModels | |||||
{ | |||||
public class VerifyPhoneNumberViewModel | |||||
{ | |||||
[Required] | |||||
public string Code { get; set; } | |||||
[Required] | |||||
[Phone] | |||||
[Display(Name = "Phone number")] | |||||
public string PhoneNumber { get; set; } | |||||
} | |||||
} |
@ -0,0 +1,25 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.IO; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
using Microsoft.AspNetCore.Hosting; | |||||
namespace eShopOnContainers.Identity | |||||
{ | |||||
public class Program | |||||
{ | |||||
public static void Main(string[] args) | |||||
{ | |||||
var host = new WebHostBuilder() | |||||
.UseKestrel() | |||||
//.UseUrls("http://localhost:5000") | |||||
.UseContentRoot(Directory.GetCurrentDirectory()) | |||||
.UseIISIntegration() | |||||
.UseStartup<Startup>() | |||||
.Build(); | |||||
host.Run(); | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,28 @@ | |||||
{ | |||||
"iisSettings": { | |||||
"windowsAuthentication": false, | |||||
"anonymousAuthentication": true, | |||||
"iisExpress": { | |||||
"applicationUrl": "http://localhost:5000", | |||||
"sslPort": 0 | |||||
} | |||||
}, | |||||
"profiles": { | |||||
"IIS Express": { | |||||
"commandName": "IISExpress", | |||||
"launchBrowser": true, | |||||
"launchUrl": "http://localhost:5000", | |||||
"environmentVariables": { | |||||
"ASPNETCORE_ENVIRONMENT": "Development" | |||||
} | |||||
}, | |||||
"eShopOnContainers.Identity": { | |||||
"commandName": "Project", | |||||
"launchBrowser": true, | |||||
"launchUrl": "http://localhost:5000", | |||||
"environmentVariables": { | |||||
"ASPNETCORE_ENVIRONMENT": "Development" | |||||
} | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,34 @@ | |||||
using eShopOnContainers.Identity.Models; | |||||
using Microsoft.AspNetCore.Identity; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace eShopOnContainers.Identity.Services | |||||
{ | |||||
public class EFLoginService : ILoginService<ApplicationUser> | |||||
{ | |||||
UserManager<ApplicationUser> _userManager; | |||||
SignInManager<ApplicationUser> _signInManager; | |||||
public EFLoginService(UserManager<ApplicationUser> userManager, SignInManager<ApplicationUser> signInManager) { | |||||
_userManager = userManager; | |||||
_signInManager = signInManager; | |||||
} | |||||
public async Task<ApplicationUser> FindByUsername(string user) | |||||
{ | |||||
return await _userManager.FindByNameAsync(user); | |||||
} | |||||
public async Task<bool> ValidateCredentials(ApplicationUser user, string password) | |||||
{ | |||||
return await _userManager.CheckPasswordAsync(user, password); | |||||
} | |||||
public Task SignIn(ApplicationUser user) { | |||||
return _signInManager.SignInAsync(user, true); | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,12 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace eShopOnContainers.Identity.Services | |||||
{ | |||||
public interface IEmailSender | |||||
{ | |||||
Task SendEmailAsync(string email, string subject, string message); | |||||
} | |||||
} |
@ -0,0 +1,14 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace eShopOnContainers.Identity.Services | |||||
{ | |||||
public interface ILoginService<T> | |||||
{ | |||||
Task<bool> ValidateCredentials(T user, string password); | |||||
Task<T> FindByUsername(string user); | |||||
Task SignIn(T user); | |||||
} | |||||
} |
@ -0,0 +1,12 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace eShopOnContainers.Identity.Services | |||||
{ | |||||
public interface ISmsSender | |||||
{ | |||||
Task SendSmsAsync(string number, string message); | |||||
} | |||||
} |
@ -0,0 +1,25 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace eShopOnContainers.Identity.Services | |||||
{ | |||||
// This class is used by the application to send Email and SMS | |||||
// when you turn on two-factor authentication in ASP.NET Identity. | |||||
// For more details see this link http://go.microsoft.com/fwlink/?LinkID=532713 | |||||
public class AuthMessageSender : IEmailSender, ISmsSender | |||||
{ | |||||
public Task SendEmailAsync(string email, string subject, string message) | |||||
{ | |||||
// Plug in your email service here to send an email. | |||||
return Task.FromResult(0); | |||||
} | |||||
public Task SendSmsAsync(string number, string message) | |||||
{ | |||||
// Plug in your SMS service here to send a text message. | |||||
return Task.FromResult(0); | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,112 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
using Microsoft.AspNetCore.Builder; | |||||
using Microsoft.AspNetCore.Hosting; | |||||
using Microsoft.AspNetCore.Identity.EntityFrameworkCore; | |||||
using Microsoft.EntityFrameworkCore; | |||||
using Microsoft.Extensions.Configuration; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using Microsoft.Extensions.Logging; | |||||
using eShopOnContainers.Identity.Data; | |||||
using eShopOnContainers.Identity.Models; | |||||
using eShopOnContainers.Identity.Services; | |||||
using eShopOnContainers.Identity.Configuration; | |||||
namespace eShopOnContainers.Identity | |||||
{ | |||||
public class Startup | |||||
{ | |||||
public Startup(IHostingEnvironment env) | |||||
{ | |||||
var builder = new ConfigurationBuilder() | |||||
.SetBasePath(env.ContentRootPath) | |||||
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) | |||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true); | |||||
if (env.IsDevelopment()) | |||||
{ | |||||
// For more details on using the user secret store see http://go.microsoft.com/fwlink/?LinkID=532709 | |||||
builder.AddUserSecrets(); | |||||
} | |||||
builder.AddEnvironmentVariables(); | |||||
Configuration = builder.Build(); | |||||
} | |||||
public IConfigurationRoot Configuration { get; } | |||||
// This method gets called by the runtime. Use this method to add services to the container. | |||||
public void ConfigureServices(IServiceCollection services) | |||||
{ | |||||
// Add framework services. | |||||
services.AddDbContext<ApplicationDbContext>(options => | |||||
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))); | |||||
services.AddIdentity<ApplicationUser, IdentityRole>() | |||||
.AddEntityFrameworkStores<ApplicationDbContext>() | |||||
.AddDefaultTokenProviders(); | |||||
services.AddMvc(); | |||||
services.AddTransient<IEmailSender, AuthMessageSender>(); | |||||
services.AddTransient<ISmsSender, AuthMessageSender>(); | |||||
services.AddTransient<ILoginService<ApplicationUser>, EFLoginService>(); | |||||
// Adds IdentityServer | |||||
services.AddIdentityServer() | |||||
.AddTemporarySigningCredential() | |||||
.AddInMemoryScopes(Config.GetScopes()) | |||||
.AddInMemoryClients(Config.GetClients()) | |||||
.AddAspNetIdentity<ApplicationUser>(); | |||||
//Configuration Settings: | |||||
services.AddOptions(); | |||||
services.Configure<ClientCallBackUrls>(Configuration.GetSection("ClientCallBackUrls")); | |||||
} | |||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. | |||||
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) | |||||
{ | |||||
loggerFactory.AddConsole(Configuration.GetSection("Logging")); | |||||
loggerFactory.AddDebug(); | |||||
if (env.IsDevelopment()) | |||||
{ | |||||
app.UseDeveloperExceptionPage(); | |||||
app.UseDatabaseErrorPage(); | |||||
app.UseBrowserLink(); | |||||
} | |||||
else | |||||
{ | |||||
app.UseExceptionHandler("/Home/Error"); | |||||
} | |||||
app.UseStaticFiles(); | |||||
app.UseIdentity(); | |||||
// Adds IdentityServer | |||||
app.UseIdentityServer(); | |||||
app.UseMvc(routes => | |||||
{ | |||||
routes.MapRoute( | |||||
name: "default", | |||||
template: "{controller=Home}/{action=Index}/{id?}"); | |||||
}); | |||||
try | |||||
{ | |||||
var context = (ApplicationDbContext)app | |||||
.ApplicationServices.GetService(typeof(ApplicationDbContext)); | |||||
using (context) | |||||
{ | |||||
context.Database.Migrate(); | |||||
} | |||||
} | |||||
catch (Exception) { } | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,10 @@ | |||||
@{ | |||||
ViewData["Title"] = "Confirm Email"; | |||||
} | |||||
<h2>@ViewData["Title"].</h2> | |||||
<div> | |||||
<p> | |||||
Thank you for confirming your email. Please <a asp-controller="Account" asp-action="Login">Click here to Log in</a>. | |||||
</p> | |||||
</div> |
@ -0,0 +1,35 @@ | |||||
@model ExternalLoginConfirmationViewModel | |||||
@{ | |||||
ViewData["Title"] = "Register"; | |||||
} | |||||
<h2>@ViewData["Title"].</h2> | |||||
<h3>Associate your @ViewData["LoginProvider"] account.</h3> | |||||
<form asp-controller="Account" asp-action="ExternalLoginConfirmation" asp-route-returnurl="@ViewData["ReturnUrl"]" method="post" class="form-horizontal"> | |||||
<h4>Association Form</h4> | |||||
<hr /> | |||||
<div asp-validation-summary="All" class="text-danger"></div> | |||||
<p class="text-info"> | |||||
You've successfully authenticated with <strong>@ViewData["LoginProvider"]</strong>. | |||||
Please enter an email address for this site below and click the Register button to finish | |||||
logging in. | |||||
</p> | |||||
<div class="form-group"> | |||||
<label asp-for="Email" class="col-md-2 control-label"></label> | |||||
<div class="col-md-10"> | |||||
<input asp-for="Email" class="form-control" /> | |||||
<span asp-validation-for="Email" class="text-danger"></span> | |||||
</div> | |||||
</div> | |||||
<div class="form-group"> | |||||
<div class="col-md-offset-2 col-md-10"> | |||||
<button type="submit" class="btn btn-default">Register</button> | |||||
</div> | |||||
</div> | |||||
</form> | |||||
@section Scripts { | |||||
@{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); } | |||||
} |
@ -0,0 +1,8 @@ | |||||
@{ | |||||
ViewData["Title"] = "Login Failure"; | |||||
} | |||||
<header> | |||||
<h2>@ViewData["Title"].</h2> | |||||
<p class="text-danger">Unsuccessful login with service.</p> | |||||
</header> |
@ -0,0 +1,31 @@ | |||||
@model ForgotPasswordViewModel | |||||
@{ | |||||
ViewData["Title"] = "Forgot your password?"; | |||||
} | |||||
<h2>@ViewData["Title"]</h2> | |||||
<p> | |||||
For more information on how to enable reset password please see this <a href="http://go.microsoft.com/fwlink/?LinkID=532713">article</a>. | |||||
</p> | |||||
@*<form asp-controller="Account" asp-action="ForgotPassword" method="post" class="form-horizontal"> | |||||
<h4>Enter your email.</h4> | |||||
<hr /> | |||||
<div asp-validation-summary="All" class="text-danger"></div> | |||||
<div class="form-group"> | |||||
<label asp-for="Email" class="col-md-2 control-label"></label> | |||||
<div class="col-md-10"> | |||||
<input asp-for="Email" class="form-control" /> | |||||
<span asp-validation-for="Email" class="text-danger"></span> | |||||
</div> | |||||
</div> | |||||
<div class="form-group"> | |||||
<div class="col-md-offset-2 col-md-10"> | |||||
<button type="submit" class="btn btn-default">Submit</button> | |||||
</div> | |||||
</div> | |||||
</form>*@ | |||||
@section Scripts { | |||||
@{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); } | |||||
} |
@ -0,0 +1,8 @@ | |||||
@{ | |||||
ViewData["Title"] = "Forgot Password Confirmation"; | |||||
} | |||||
<h2>@ViewData["Title"].</h2> | |||||
<p> | |||||
Please check your email to reset your password. | |||||
</p> |
@ -0,0 +1,8 @@ | |||||
@{ | |||||
ViewData["Title"] = "Locked out"; | |||||
} | |||||
<header> | |||||
<h1 class="text-danger">Locked out.</h1> | |||||
<p class="text-danger">This account has been locked out, please try again later.</p> | |||||
</header> |
@ -0,0 +1,21 @@ | |||||
@model IdentityServer4.Quickstart.UI.Models.LoggedOutViewModel | |||||
<div class="page-header"> | |||||
<h1> | |||||
Logout | |||||
<small>You are now logged out</small> | |||||
</h1> | |||||
@if (Model.PostLogoutRedirectUri != null) | |||||
{ | |||||
<div> | |||||
Click <a href="@Model.PostLogoutRedirectUri">here</a> to return to the | |||||
<span>@Model.ClientName</span> application. | |||||
</div> | |||||
} | |||||
@if (Model.SignOutIframeUrl != null) | |||||
{ | |||||
<iframe style="display:none" width="0" height="0" class="signout" src="@Model.SignOutIframeUrl"></iframe> | |||||
} | |||||
</div> |
@ -0,0 +1,83 @@ | |||||
@model IdentityServer4.Quickstart.UI.Models.LoginViewModel | |||||
<div class="login-page"> | |||||
<div class="page-header"> | |||||
<h1>Login</h1> | |||||
</div> | |||||
@Html.Partial("_ValidationSummary") | |||||
<div class="row"> | |||||
@if (Model.EnableLocalLogin) | |||||
{ | |||||
<div class="col-sm-6"> | |||||
<div class="panel panel-default"> | |||||
<div class="panel-heading"> | |||||
<h3 class="panel-title">Local Login</h3> | |||||
</div> | |||||
<div class="panel-body"> | |||||
<form asp-route="Login"> | |||||
<input type="hidden" asp-for="ReturnUrl" /> | |||||
<fieldset> | |||||
<div class="form-group"> | |||||
<label asp-for="Username"></label> | |||||
<input class="form-control" placeholder="Username" asp-for="Username" autofocus> | |||||
</div> | |||||
<div class="form-group"> | |||||
<label asp-for="Password"></label> | |||||
<input type="password" class="form-control" placeholder="Password" asp-for="Password" autocomplete="off"> | |||||
</div> | |||||
<div class="form-group login-remember"> | |||||
<label asp-for="RememberLogin"> | |||||
<input asp-for="RememberLogin"> | |||||
<strong>Remember My Login</strong> | |||||
</label> | |||||
</div> | |||||
<div class="form-group"> | |||||
<button class="btn btn-primary">Login</button> | |||||
</div> | |||||
</fieldset> | |||||
</form> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
} | |||||
@if (Model.ExternalProviders.Any()) | |||||
{ | |||||
<div class="col-md-6 col-sm-6 external-providers"> | |||||
<div class="panel panel-default"> | |||||
<div class="panel-heading"> | |||||
<h3 class="panel-title">External Login</h3> | |||||
</div> | |||||
<div class="panel-body"> | |||||
<ul class="list-inline"> | |||||
@foreach (var provider in Model.ExternalProviders) | |||||
{ | |||||
<li> | |||||
<a class="btn btn-default" | |||||
asp-action="ExternalLogin" | |||||
asp-route-provider="@provider.AuthenticationScheme" | |||||
asp-route-returnUrl="@Model.ReturnUrl"> | |||||
@provider.DisplayName | |||||
</a> | |||||
</li> | |||||
} | |||||
</ul> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
} | |||||
@if (!Model.EnableLocalLogin && !Model.ExternalProviders.Any()) | |||||
{ | |||||
<div class="alert alert-warning"> | |||||
<strong>Invalid login request</strong> | |||||
There are no login schemes configured for this client. | |||||
</div> | |||||
} | |||||
</div> | |||||
</div> |
@ -0,0 +1,21 @@ | |||||
@model IdentityServer4.Quickstart.UI.Models.LogoutViewModel | |||||
<div class="logout-page"> | |||||
<div class="page-header"> | |||||
<h1>Logout</h1> | |||||
</div> | |||||
<div class="row"> | |||||
<div class="col-sm-6"> | |||||
<p>Would you like to logout of IdentityServer?</p> | |||||
<form asp-action="Logout"> | |||||
<input type="hidden" name="logoutId" value="@Model.LogoutId" /> | |||||
<fieldset> | |||||
<div class="form-group"> | |||||
<button class="btn btn-primary">Yes</button> | |||||
</div> | |||||
</fieldset> | |||||
</form> | |||||
</div> | |||||
</div> | |||||
</div> |
@ -0,0 +1,42 @@ | |||||
@model RegisterViewModel | |||||
@{ | |||||
ViewData["Title"] = "Register"; | |||||
} | |||||
<h2>@ViewData["Title"].</h2> | |||||
<form asp-controller="Account" asp-action="Register" asp-route-returnurl="@ViewData["ReturnUrl"]" method="post" class="form-horizontal"> | |||||
<h4>Create a new account.</h4> | |||||
<hr /> | |||||
<div asp-validation-summary="All" class="text-danger"></div> | |||||
<div class="form-group"> | |||||
<label asp-for="Email" class="col-md-2 control-label"></label> | |||||
<div class="col-md-10"> | |||||
<input asp-for="Email" class="form-control" /> | |||||
<span asp-validation-for="Email" class="text-danger"></span> | |||||
</div> | |||||
</div> | |||||
<div class="form-group"> | |||||
<label asp-for="Password" class="col-md-2 control-label"></label> | |||||
<div class="col-md-10"> | |||||
<input asp-for="Password" class="form-control" /> | |||||
<span asp-validation-for="Password" class="text-danger"></span> | |||||
</div> | |||||
</div> | |||||
<div class="form-group"> | |||||
<label asp-for="ConfirmPassword" class="col-md-2 control-label"></label> | |||||
<div class="col-md-10"> | |||||
<input asp-for="ConfirmPassword" class="form-control" /> | |||||
<span asp-validation-for="ConfirmPassword" class="text-danger"></span> | |||||
</div> | |||||
</div> | |||||
<div class="form-group"> | |||||
<div class="col-md-offset-2 col-md-10"> | |||||
<button type="submit" class="btn btn-default">Register</button> | |||||
</div> | |||||
</div> | |||||
</form> | |||||
@section Scripts { | |||||
@{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); } | |||||
} |
@ -0,0 +1,43 @@ | |||||
@model ResetPasswordViewModel | |||||
@{ | |||||
ViewData["Title"] = "Reset password"; | |||||
} | |||||
<h2>@ViewData["Title"].</h2> | |||||
<form asp-controller="Account" asp-action="ResetPassword" method="post" class="form-horizontal"> | |||||
<h4>Reset your password.</h4> | |||||
<hr /> | |||||
<div asp-validation-summary="All" class="text-danger"></div> | |||||
<input asp-for="Code" type="hidden" /> | |||||
<div class="form-group"> | |||||
<label asp-for="Email" class="col-md-2 control-label"></label> | |||||
<div class="col-md-10"> | |||||
<input asp-for="Email" class="form-control" /> | |||||
<span asp-validation-for="Email" class="text-danger"></span> | |||||
</div> | |||||
</div> | |||||
<div class="form-group"> | |||||
<label asp-for="Password" class="col-md-2 control-label"></label> | |||||
<div class="col-md-10"> | |||||
<input asp-for="Password" class="form-control" /> | |||||
<span asp-validation-for="Password" class="text-danger"></span> | |||||
</div> | |||||
</div> | |||||
<div class="form-group"> | |||||
<label asp-for="ConfirmPassword" class="col-md-2 control-label"></label> | |||||
<div class="col-md-10"> | |||||
<input asp-for="ConfirmPassword" class="form-control" /> | |||||
<span asp-validation-for="ConfirmPassword" class="text-danger"></span> | |||||
</div> | |||||
</div> | |||||
<div class="form-group"> | |||||
<div class="col-md-offset-2 col-md-10"> | |||||
<button type="submit" class="btn btn-default">Reset</button> | |||||
</div> | |||||
</div> | |||||
</form> | |||||
@section Scripts { | |||||
@{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); } | |||||
} |
@ -0,0 +1,8 @@ | |||||
@{ | |||||
ViewData["Title"] = "Reset password confirmation"; | |||||
} | |||||
<h1>@ViewData["Title"].</h1> | |||||
<p> | |||||
Your password has been reset. Please <a asp-controller="Account" asp-action="Login">Click here to log in</a>. | |||||
</p> |
@ -0,0 +1,21 @@ | |||||
@model SendCodeViewModel | |||||
@{ | |||||
ViewData["Title"] = "Send Verification Code"; | |||||
} | |||||
<h2>@ViewData["Title"].</h2> | |||||
<form asp-controller="Account" asp-action="SendCode" asp-route-returnurl="@Model.ReturnUrl" method="post" class="form-horizontal"> | |||||
<input asp-for="RememberMe" type="hidden" /> | |||||
<div class="row"> | |||||
<div class="col-md-8"> | |||||
Select Two-Factor Authentication Provider: | |||||
<select asp-for="SelectedProvider" asp-items="Model.Providers"></select> | |||||
<button type="submit" class="btn btn-default">Submit</button> | |||||
</div> | |||||
</div> | |||||
</form> | |||||
@section Scripts { | |||||
@{await Html.RenderPartialAsync("_ValidationScriptsPartial"); } | |||||
} |
@ -0,0 +1,38 @@ | |||||
@model VerifyCodeViewModel | |||||
@{ | |||||
ViewData["Title"] = "Verify"; | |||||
} | |||||
<h2>@ViewData["Title"].</h2> | |||||
<form asp-controller="Account" asp-action="VerifyCode" asp-route-returnurl="@ViewData["ReturnUrl"]" method="post" class="form-horizontal"> | |||||
<div asp-validation-summary="All" class="text-danger"></div> | |||||
<input asp-for="Provider" type="hidden" /> | |||||
<input asp-for="RememberMe" type="hidden" /> | |||||
<h4>@ViewData["Status"]</h4> | |||||
<hr /> | |||||
<div class="form-group"> | |||||
<label asp-for="Code" class="col-md-2 control-label"></label> | |||||
<div class="col-md-10"> | |||||
<input asp-for="Code" class="form-control" /> | |||||
<span asp-validation-for="Code" class="text-danger"></span> | |||||
</div> | |||||
</div> | |||||
<div class="form-group"> | |||||
<div class="col-md-offset-2 col-md-10"> | |||||
<div class="checkbox"> | |||||
<input asp-for="RememberBrowser" /> | |||||
<label asp-for="RememberBrowser"></label> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
<div class="form-group"> | |||||
<div class="col-md-offset-2 col-md-10"> | |||||
<button type="submit" class="btn btn-default">Submit</button> | |||||
</div> | |||||
</div> | |||||
</form> | |||||
@section Scripts { | |||||
@{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); } | |||||
} |
@ -0,0 +1,82 @@ | |||||
@model IdentityServer4.Quickstart.UI.Models.ConsentViewModel | |||||
<div class="page-consent"> | |||||
<div class="row page-header"> | |||||
<div class="col-sm-10"> | |||||
@if (Model.ClientLogoUrl != null) | |||||
{ | |||||
<div class="client-logo"><img src="@Model.ClientLogoUrl"></div> | |||||
} | |||||
<h1> | |||||
@Model.ClientName | |||||
<small>is requesting your permission</small> | |||||
</h1> | |||||
</div> | |||||
</div> | |||||
<div class="row"> | |||||
<div class="col-sm-8"> | |||||
@Html.Partial("_ValidationSummary") | |||||
<form asp-action="Index" class="consent-form"> | |||||
<input type="hidden" asp-for="ReturnUrl" /> | |||||
<div>Uncheck the permissions you do not wish to grant.</div> | |||||
@if (Model.IdentityScopes.Any()) | |||||
{ | |||||
<div class="panel panel-default consent-buttons"> | |||||
<div class="panel-heading"> | |||||
<span class="glyphicon glyphicon-user"></span> | |||||
Personal Information | |||||
</div> | |||||
<ul class="list-group"> | |||||
@foreach (var scope in Model.IdentityScopes) | |||||
{ | |||||
@Html.Partial("_ScopeListItem", scope) | |||||
} | |||||
</ul> | |||||
</div> | |||||
} | |||||
@if (Model.ResourceScopes.Any()) | |||||
{ | |||||
<div class="panel panel-default"> | |||||
<div class="panel-heading"> | |||||
<span class="glyphicon glyphicon-tasks"></span> | |||||
Application Access | |||||
</div> | |||||
<ul class="list-group"> | |||||
@foreach (var scope in Model.ResourceScopes) | |||||
{ | |||||
@Html.Partial("_ScopeListItem", scope) | |||||
} | |||||
</ul> | |||||
</div> | |||||
} | |||||
@if (Model.AllowRememberConsent) | |||||
{ | |||||
<div class="consent-remember"> | |||||
<label> | |||||
<input class="consent-scopecheck" asp-for="RememberConsent" /> | |||||
<strong>Remember My Decision</strong> | |||||
</label> | |||||
</div> | |||||
} | |||||
<div class="consent-buttons"> | |||||
<button name="button" value="yes" class="btn btn-primary" autofocus>Yes, Allow</button> | |||||
<button name="button" value="no" class="btn">No, Do Not Allow</button> | |||||
@if (Model.ClientUrl != null) | |||||
{ | |||||
<a class="pull-right btn btn-default" target="_blank" href="@Model.ClientUrl"> | |||||
<span class="glyphicon glyphicon-info-sign"></span> | |||||
<strong>@Model.ClientName</strong> | |||||
</a> | |||||
} | |||||
</div> | |||||
</form> | |||||
</div> | |||||
</div> | |||||
</div> |
@ -0,0 +1,34 @@ | |||||
@model IdentityServer4.Quickstart.UI.Models.ScopeViewModel | |||||
<li class="list-group-item"> | |||||
<label> | |||||
<input class="consent-scopecheck" | |||||
type="checkbox" | |||||
name="ScopesConsented" | |||||
id="scopes_@Model.Name" | |||||
value="@Model.Name" | |||||
checked="@Model.Checked" | |||||
disabled="@Model.Required" /> | |||||
@if (Model.Required) | |||||
{ | |||||
<input type="hidden" | |||||
name="ScopesConsented" | |||||
value="@Model.Name" /> | |||||
} | |||||
<strong>@Model.DisplayName</strong> | |||||
@if (Model.Emphasize) | |||||
{ | |||||
<span class="glyphicon glyphicon-exclamation-sign"></span> | |||||
} | |||||
</label> | |||||
@if (Model.Required) | |||||
{ | |||||
<span><em>(required)</em></span> | |||||
} | |||||
@if (Model.Description != null) | |||||
{ | |||||
<div class="consent-description"> | |||||
<label for="scopes_@Model.Name">@Model.Description</label> | |||||
</div> | |||||
} | |||||
</li> |
@ -0,0 +1,7 @@ | |||||
@{ | |||||
ViewData["Title"] = "About"; | |||||
} | |||||
<h2>@ViewData["Title"].</h2> | |||||
<h3>@ViewData["Message"]</h3> | |||||
<p>Use this area to provide additional information.</p> |
@ -0,0 +1,17 @@ | |||||
@{ | |||||
ViewData["Title"] = "Contact"; | |||||
} | |||||
<h2>@ViewData["Title"].</h2> | |||||
<h3>@ViewData["Message"]</h3> | |||||
<address> | |||||
One Microsoft Way<br /> | |||||
Redmond, WA 98052-6399<br /> | |||||
<abbr title="Phone">P:</abbr> | |||||
425.555.0100 | |||||
</address> | |||||
<address> | |||||
<strong>Support:</strong> <a href="mailto:Support@example.com">Support@example.com</a><br /> | |||||
<strong>Marketing:</strong> <a href="mailto:Marketing@example.com">Marketing@example.com</a> | |||||
</address> |
@ -0,0 +1,30 @@ | |||||
<div class="welcome-page"> | |||||
<div class="row page-header"> | |||||
<div class="col-sm-10"> | |||||
<h1> | |||||
<img class="icon" src="~/icon.jpg"> | |||||
Welcome to IdentityServer4 | |||||
@*<small>(build {version})</small>*@ | |||||
</h1> | |||||
</div> | |||||
</div> | |||||
<div class="row"> | |||||
<div class="col-sm-8"> | |||||
<p> | |||||
IdentityServer publishes a | |||||
<a href="~/.well-known/openid-configuration">discovery document</a> | |||||
where you can find metadata and links to all the endpoints, key material, etc. | |||||
</p> | |||||
</div> | |||||
</div> | |||||
<div class="row"> | |||||
<div class="col-sm-8"> | |||||
<p> | |||||
Here are links to the | |||||
<a href="https://github.com/identityserver/IdentityServer4">source code repository</a>, | |||||
and <a href="https://github.com/identityserver/IdentityServer4.Samples">ready to use samples</a>. | |||||
</p> | |||||
</div> | |||||
</div> | |||||
</div> |
@ -0,0 +1,27 @@ | |||||
@model AddPhoneNumberViewModel | |||||
@{ | |||||
ViewData["Title"] = "Add Phone Number"; | |||||
} | |||||
<h2>@ViewData["Title"].</h2> | |||||
<form asp-controller="Manage" asp-action="AddPhoneNumber" method="post" class="form-horizontal"> | |||||
<h4>Add a phone number.</h4> | |||||
<hr /> | |||||
<div asp-validation-summary="All" class="text-danger"></div> | |||||
<div class="form-group"> | |||||
<label asp-for="PhoneNumber" class="col-md-2 control-label"></label> | |||||
<div class="col-md-10"> | |||||
<input asp-for="PhoneNumber" class="form-control" /> | |||||
<span asp-validation-for="PhoneNumber" class="text-danger"></span> | |||||
</div> | |||||
</div> | |||||
<div class="form-group"> | |||||
<div class="col-md-offset-2 col-md-10"> | |||||
<button type="submit" class="btn btn-default">Send verification code</button> | |||||
</div> | |||||
</div> | |||||
</form> | |||||
@section Scripts { | |||||
@{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); } | |||||
} |
@ -0,0 +1,42 @@ | |||||
@model ChangePasswordViewModel | |||||
@{ | |||||
ViewData["Title"] = "Change Password"; | |||||
} | |||||
<h2>@ViewData["Title"].</h2> | |||||
<form asp-controller="Manage" asp-action="ChangePassword" method="post" class="form-horizontal"> | |||||
<h4>Change Password Form</h4> | |||||
<hr /> | |||||
<div asp-validation-summary="All" class="text-danger"></div> | |||||
<div class="form-group"> | |||||
<label asp-for="OldPassword" class="col-md-2 control-label"></label> | |||||
<div class="col-md-10"> | |||||
<input asp-for="OldPassword" class="form-control" /> | |||||
<span asp-validation-for="OldPassword" class="text-danger"></span> | |||||
</div> | |||||
</div> | |||||
<div class="form-group"> | |||||
<label asp-for="NewPassword" class="col-md-2 control-label"></label> | |||||
<div class="col-md-10"> | |||||
<input asp-for="NewPassword" class="form-control" /> | |||||
<span asp-validation-for="NewPassword" class="text-danger"></span> | |||||
</div> | |||||
</div> | |||||
<div class="form-group"> | |||||
<label asp-for="ConfirmPassword" class="col-md-2 control-label"></label> | |||||
<div class="col-md-10"> | |||||
<input asp-for="ConfirmPassword" class="form-control" /> | |||||
<span asp-validation-for="ConfirmPassword" class="text-danger"></span> | |||||
</div> | |||||
</div> | |||||
<div class="form-group"> | |||||
<div class="col-md-offset-2 col-md-10"> | |||||
<button type="submit" class="btn btn-default">Change password</button> | |||||
</div> | |||||
</div> | |||||
</form> | |||||
@section Scripts { | |||||
@{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); } | |||||
} |
@ -0,0 +1,71 @@ | |||||
@model IndexViewModel | |||||
@{ | |||||
ViewData["Title"] = "Manage your account"; | |||||
} | |||||
<h2>@ViewData["Title"].</h2> | |||||
<p class="text-success">@ViewData["StatusMessage"]</p> | |||||
<div> | |||||
<h4>Change your account settings</h4> | |||||
<hr /> | |||||
<dl class="dl-horizontal"> | |||||
<dt>Password:</dt> | |||||
<dd> | |||||
@if (Model.HasPassword) | |||||
{ | |||||
<a asp-controller="Manage" asp-action="ChangePassword" class="btn-bracketed">Change</a> | |||||
} | |||||
else | |||||
{ | |||||
<a asp-controller="Manage" asp-action="SetPassword" class="btn-bracketed">Create</a> | |||||
} | |||||
</dd> | |||||
<dt>External Logins:</dt> | |||||
<dd> | |||||
@Model.Logins.Count <a asp-controller="Manage" asp-action="ManageLogins" class="btn-bracketed">Manage</a> | |||||
</dd> | |||||
<dt>Phone Number:</dt> | |||||
<dd> | |||||
<p> | |||||
Phone Numbers can used as a second factor of verification in two-factor authentication. | |||||
See <a href="http://go.microsoft.com/fwlink/?LinkID=532713">this article</a> | |||||
for details on setting up this ASP.NET application to support two-factor authentication using SMS. | |||||
</p> | |||||
@*@(Model.PhoneNumber ?? "None") | |||||
@if (Model.PhoneNumber != null) | |||||
{ | |||||
<br /> | |||||
<a asp-controller="Manage" asp-action="AddPhoneNumber" class="btn-bracketed">Change</a> | |||||
<form asp-controller="Manage" asp-action="RemovePhoneNumber" method="post"> | |||||
[<button type="submit" class="btn-link">Remove</button>] | |||||
</form> | |||||
} | |||||
else | |||||
{ | |||||
<a asp-controller="Manage" asp-action="AddPhoneNumber" class="btn-bracketed">Add</a> | |||||
}*@ | |||||
</dd> | |||||
<dt>Two-Factor Authentication:</dt> | |||||
<dd> | |||||
<p> | |||||
There are no two-factor authentication providers configured. See <a href="http://go.microsoft.com/fwlink/?LinkID=532713">this article</a> | |||||
for setting up this application to support two-factor authentication. | |||||
</p> | |||||
@*@if (Model.TwoFactor) | |||||
{ | |||||
<form asp-controller="Manage" asp-action="DisableTwoFactorAuthentication" method="post" class="form-horizontal"> | |||||
Enabled <button type="submit" class="btn-link btn-bracketed">Disable</button> | |||||
</form> | |||||
} | |||||
else | |||||
{ | |||||
<form asp-controller="Manage" asp-action="EnableTwoFactorAuthentication" method="post" class="form-horizontal"> | |||||
<button type="submit" class="btn-link btn-bracketed">Enable</button> Disabled | |||||
</form> | |||||
}*@ | |||||
</dd> | |||||
</dl> | |||||
</div> |
@ -0,0 +1,54 @@ | |||||
@model ManageLoginsViewModel | |||||
@using Microsoft.AspNetCore.Http.Authentication | |||||
@{ | |||||
ViewData["Title"] = "Manage your external logins"; | |||||
} | |||||
<h2>@ViewData["Title"].</h2> | |||||
<p class="text-success">@ViewData["StatusMessage"]</p> | |||||
@if (Model.CurrentLogins.Count > 0) | |||||
{ | |||||
<h4>Registered Logins</h4> | |||||
<table class="table"> | |||||
<tbody> | |||||
@for (var index = 0; index < Model.CurrentLogins.Count; index++) | |||||
{ | |||||
<tr> | |||||
<td>@Model.CurrentLogins[index].LoginProvider</td> | |||||
<td> | |||||
@if ((bool)ViewData["ShowRemoveButton"]) | |||||
{ | |||||
<form asp-controller="Manage" asp-action="RemoveLogin" method="post" class="form-horizontal"> | |||||
<div> | |||||
<input asp-for="@Model.CurrentLogins[index].LoginProvider" name="LoginProvider" type="hidden" /> | |||||
<input asp-for="@Model.CurrentLogins[index].ProviderKey" name="ProviderKey" type="hidden" /> | |||||
<input type="submit" class="btn btn-default" value="Remove" title="Remove this @Model.CurrentLogins[index].LoginProvider login from your account" /> | |||||
</div> | |||||
</form> | |||||
} | |||||
else | |||||
{ | |||||
@: | |||||
} | |||||
</td> | |||||
</tr> | |||||
} | |||||
</tbody> | |||||
</table> | |||||
} | |||||
@if (Model.OtherLogins.Count > 0) | |||||
{ | |||||
<h4>Add another service to log in.</h4> | |||||
<hr /> | |||||
<form asp-controller="Manage" asp-action="LinkLogin" method="post" class="form-horizontal"> | |||||
<div id="socialLoginList"> | |||||
<p> | |||||
@foreach (var provider in Model.OtherLogins) | |||||
{ | |||||
<button type="submit" class="btn btn-default" name="provider" value="@provider.AuthenticationScheme" title="Log in using your @provider.DisplayName account">@provider.AuthenticationScheme</button> | |||||
} | |||||
</p> | |||||
</div> | |||||
</form> | |||||
} |
@ -0,0 +1,38 @@ | |||||
@model SetPasswordViewModel | |||||
@{ | |||||
ViewData["Title"] = "Set Password"; | |||||
} | |||||
<p class="text-info"> | |||||
You do not have a local username/password for this site. Add a local | |||||
account so you can log in without an external login. | |||||
</p> | |||||
<form asp-controller="Manage" asp-action="SetPassword" asp-route-returnurl="@ViewData["ReturnUrl"]" method="post" class="form-horizontal"> | |||||
<h4>Set your password</h4> | |||||
<hr /> | |||||
<div asp-validation-summary="All" class="text-danger"></div> | |||||
<div class="form-group"> | |||||
<label asp-for="NewPassword" class="col-md-2 control-label"></label> | |||||
<div class="col-md-10"> | |||||
<input asp-for="NewPassword" class="form-control" /> | |||||
<span asp-validation-for="NewPassword" class="text-danger"></span> | |||||
</div> | |||||
</div> | |||||
<div class="form-group"> | |||||
<label asp-for="ConfirmPassword" class="col-md-2 control-label"></label> | |||||
<div class="col-md-10"> | |||||
<input asp-for="ConfirmPassword" class="form-control" /> | |||||
<span asp-validation-for="ConfirmPassword" class="text-danger"></span> | |||||
</div> | |||||
</div> | |||||
<div class="form-group"> | |||||
<div class="col-md-offset-2 col-md-10"> | |||||
<button type="submit" class="btn btn-default">Set password</button> | |||||
</div> | |||||
</div> | |||||
</form> | |||||
@section Scripts { | |||||
@{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); } | |||||
} |
@ -0,0 +1,30 @@ | |||||
@model VerifyPhoneNumberViewModel | |||||
@{ | |||||
ViewData["Title"] = "Verify Phone Number"; | |||||
} | |||||
<h2>@ViewData["Title"].</h2> | |||||
<form asp-controller="Manage" asp-action="VerifyPhoneNumber" asp-route-returnurl="@ViewData["ReturnUrl"]" method="post" class="form-horizontal"> | |||||
<input asp-for="PhoneNumber" type="hidden" /> | |||||
<h4>Add a phone number.</h4> | |||||
<h5>@ViewData["Status"]</h5> | |||||
<hr /> | |||||
<div asp-validation-summary="All" class="text-danger"></div> | |||||
<div class="form-group"> | |||||
<label asp-for="Code" class="col-md-2 control-label"></label> | |||||
<div class="col-md-10"> | |||||
<input asp-for="Code" class="form-control" /> | |||||
<span asp-validation-for="Code" class="text-danger"></span> | |||||
</div> | |||||
</div> | |||||
<div class="form-group"> | |||||
<div class="col-md-offset-2 col-md-10"> | |||||
<button type="submit" class="btn btn-default">Submit</button> | |||||
</div> | |||||
</div> | |||||
</form> | |||||
@section Scripts { | |||||
@{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); } | |||||
} |
@ -0,0 +1,34 @@ | |||||
@model IdentityServer4.Quickstart.UI.Models.ErrorViewModel | |||||
@{ | |||||
var error = Model?.Error?.Error; | |||||
var request_id = Model?.Error?.RequestId; | |||||
} | |||||
<div class="error-page"> | |||||
<div class="page-header"> | |||||
<h1>Error</h1> | |||||
</div> | |||||
<div class="row"> | |||||
<div class="col-sm-6"> | |||||
<div class="alert alert-danger"> | |||||
Sorry, there was an error | |||||
@if (error != null) | |||||
{ | |||||
<strong> | |||||
<em> | |||||
: @error | |||||
</em> | |||||
</strong> | |||||
} | |||||
</div> | |||||
@if (request_id != null) | |||||
{ | |||||
<div class="request-id">Request Id: @request_id</div> | |||||
} | |||||
</div> | |||||
</div> | |||||
</div> |
@ -0,0 +1,54 @@ | |||||
<!DOCTYPE html> | |||||
<html> | |||||
<head> | |||||
<meta charset="utf-8" /> | |||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |||||
<title>IdentityServer4</title> | |||||
<link rel="icon" type="image/x-icon" href="~/favicon.ico" /> | |||||
<link rel="shortcut icon" type="image/x-icon" href="~/favicon.ico" /> | |||||
<link rel="stylesheet" href="~/lib/bootstrap/css/bootstrap.css" /> | |||||
<link rel="stylesheet" href="~/css/site.css" /> | |||||
</head> | |||||
<body> | |||||
<div class="navbar navbar-inverse navbar-fixed-top"> | |||||
<div class="container-fluid"> | |||||
<div class="navbar-header"> | |||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false"> | |||||
<span class="sr-only">Toggle navigation</span> | |||||
<span class="icon-bar"></span> | |||||
<span class="icon-bar"></span> | |||||
<span class="icon-bar"></span> | |||||
</button> | |||||
<a href="~/"> | |||||
<span class="navbar-brand"> | |||||
<img src="~/icon.png" class="icon-banner"> | |||||
IdentityServer4 | |||||
</span> | |||||
</a> | |||||
</div> | |||||
@if (User.Identity.IsAuthenticated) | |||||
{ | |||||
<ul class="nav navbar-nav"> | |||||
<li class="dropdown"> | |||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown">@User.Identity.Name <b class="caret"></b></a> | |||||
<ul class="dropdown-menu"> | |||||
<li><a asp-action="Logout" asp-controller="Account">Logout</a></li> | |||||
@*<li class="divider"></li> | |||||
<li><a asp-route="Login" asp-route-id="@ViewContext.HttpContext.Request.Query["id"]">Login With Different Account</a></li>*@ | |||||
</ul> | |||||
</li> | |||||
</ul> | |||||
} | |||||
</div> | |||||
</div> | |||||
<div class="container body-content"> | |||||
@RenderBody() | |||||
</div> | |||||
<script src="~/lib/jquery/jquery.js"></script> | |||||
<script src="~/lib/bootstrap/js/bootstrap.js"></script> | |||||
@RenderSection("scripts", required: false) | |||||
</body> | |||||
</html> |
@ -0,0 +1,26 @@ | |||||
@using Microsoft.AspNetCore.Identity | |||||
@using eShopOnContainers.Identity.Models | |||||
@inject SignInManager<ApplicationUser> SignInManager | |||||
@inject UserManager<ApplicationUser> UserManager | |||||
@if (SignInManager.IsSignedIn(User)) | |||||
{ | |||||
<form asp-area="" asp-controller="Account" asp-action="LogOff" method="post" id="logoutForm" class="navbar-right"> | |||||
<ul class="nav navbar-nav navbar-right"> | |||||
<li> | |||||
<a asp-area="" asp-controller="Manage" asp-action="Index" title="Manage">Hello @UserManager.GetUserName(User)!</a> | |||||
</li> | |||||
<li> | |||||
<button type="submit" class="btn btn-link navbar-btn navbar-link">Log off</button> | |||||
</li> | |||||
</ul> | |||||
</form> | |||||
} | |||||
else | |||||
{ | |||||
<ul class="nav navbar-nav navbar-right"> | |||||
<li><a asp-area="" asp-controller="Account" asp-action="Register">Register</a></li> | |||||
<li><a asp-area="" asp-controller="Account" asp-action="Login">Log in</a></li> | |||||
</ul> | |||||
} |
@ -0,0 +1,14 @@ | |||||
<environment names="Development"> | |||||
<script src="~/lib/jquery-validation/dist/jquery.validate.js"></script> | |||||
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script> | |||||
</environment> | |||||
<environment names="Staging,Production"> | |||||
<script src="https://ajax.aspnetcdn.com/ajax/jquery.validate/1.14.0/jquery.validate.min.js" | |||||
asp-fallback-src="~/lib/jquery-validation/dist/jquery.validate.min.js" | |||||
asp-fallback-test="window.jQuery && window.jQuery.validator"> | |||||
</script> | |||||
<script src="https://ajax.aspnetcdn.com/ajax/jquery.validation.unobtrusive/3.2.6/jquery.validate.unobtrusive.min.js" | |||||
asp-fallback-src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js" | |||||
asp-fallback-test="window.jQuery && window.jQuery.validator && window.jQuery.validator.unobtrusive"> | |||||
</script> | |||||
</environment> |
@ -0,0 +1,7 @@ | |||||
@if (ViewContext.ModelState.IsValid == false) | |||||
{ | |||||
<div class="alert alert-danger"> | |||||
<strong>Error</strong> | |||||
<div asp-validation-summary="All" class="danger"></div> | |||||
</div> | |||||
} |
@ -0,0 +1 @@ | |||||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers |
@ -0,0 +1,3 @@ | |||||
@{ | |||||
Layout = "_Layout"; | |||||
} |
@ -0,0 +1,17 @@ | |||||
{ | |||||
"ConnectionStrings": { | |||||
"DefaultConnection": "Server=identity.data;Database=aspnet-Microsoft.eShopOnContainers.WebMVC;User Id=sa;Password=Pass@word" | |||||
//"DefaultConnection": "Server=127.0.0.1,5433;Database=aspnet-Microsoft.eShopOnContainers.WebMVC;User Id=sa;Password=Pass@word" | |||||
}, | |||||
"ClientsCallBackUrls": { | |||||
"Spa": "http://localhost:5003" | |||||
}, | |||||
"Logging": { | |||||
"IncludeScopes": false, | |||||
"LogLevel": { | |||||
"Default": "Debug", | |||||
"System": "Information", | |||||
"Microsoft": "Information" | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,10 @@ | |||||
{ | |||||
"name": "asp.net", | |||||
"private": true, | |||||
"dependencies": { | |||||
"bootstrap": "3.3.6", | |||||
"jquery": "2.2.0", | |||||
"jquery-validation": "1.14.0", | |||||
"jquery-validation-unobtrusive": "3.2.6" | |||||
} | |||||
} |
@ -0,0 +1,24 @@ | |||||
// Configure bundling and minification for the project. | |||||
// More info at https://go.microsoft.com/fwlink/?LinkId=808241 | |||||
[ | |||||
{ | |||||
"outputFileName": "wwwroot/css/site.min.css", | |||||
// An array of relative input file paths. Globbing patterns supported | |||||
"inputFiles": [ | |||||
"wwwroot/css/site.css" | |||||
] | |||||
}, | |||||
{ | |||||
"outputFileName": "wwwroot/js/site.min.js", | |||||
"inputFiles": [ | |||||
"wwwroot/js/site.js" | |||||
], | |||||
// Optionally specify minification options | |||||
"minify": { | |||||
"enabled": true, | |||||
"renameLocals": true | |||||
}, | |||||
// Optinally generate .map file | |||||
"sourceMap": false | |||||
} | |||||
] |
@ -0,0 +1,25 @@ | |||||
version: '2' | |||||
services: | |||||
identity.service: | |||||
image: eshop/identity | |||||
build: | |||||
context: . | |||||
dockerfile: Dockerfile | |||||
environment: | |||||
- ConnectionString=Server=identity.data;Initial Catalog=CatalogData;User Id=sa;Password=Pass@word | |||||
- Spa=http://localhost/5003 | |||||
expose: | |||||
- "80" | |||||
ports: | |||||
- "5101:80" | |||||
depends_on: | |||||
- identity.data | |||||
identity.data: | |||||
image: microsoft/mssql-server-linux | |||||
environment: | |||||
- SA_PASSWORD=Pass@word | |||||
- ACCEPT_EULA=Y | |||||
ports: | |||||
- "5434:1433" |
@ -0,0 +1,23 @@ | |||||
<?xml version="1.0" encoding="utf-8"?> | |||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | |||||
<PropertyGroup> | |||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion> | |||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath> | |||||
</PropertyGroup> | |||||
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" /> | |||||
<PropertyGroup Label="Globals"> | |||||
<ProjectGuid>a579e108-5445-403d-a407-339ac4d1611b</ProjectGuid> | |||||
<RootNamespace>eShopOnContainers.Identity</RootNamespace> | |||||
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath> | |||||
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath> | |||||
<TargetFrameworkVersion>v4.6</TargetFrameworkVersion> | |||||
</PropertyGroup> | |||||
<PropertyGroup> | |||||
<SchemaVersion>2.0</SchemaVersion> | |||||
</PropertyGroup> | |||||
<ItemGroup> | |||||
<DnxInvisibleContent Include="bower.json" /> | |||||
<DnxInvisibleContent Include=".bowerrc" /> | |||||
</ItemGroup> | |||||
<Import Project="$(VSToolsPath)\DotNet.Web\Microsoft.DotNet.Web.targets" Condition="'$(VSToolsPath)' != ''" /> | |||||
</Project> |
@ -0,0 +1,105 @@ | |||||
{ | |||||
"userSecretsId": "aspnet-eShopOnContainers.Identity-90487118-103c-4ff0-b9da-e5e26f7ab0c5", | |||||
"dependencies": { | |||||
"Microsoft.NETCore.App": { | |||||
"version": "1.0.1", | |||||
"type": "platform" | |||||
}, | |||||
"Microsoft.AspNetCore.Authentication.Cookies": "1.0.0", | |||||
"Microsoft.AspNetCore.Diagnostics": "1.0.0", | |||||
"Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore": "1.0.0", | |||||
"Microsoft.AspNetCore.Identity.EntityFrameworkCore": "1.0.0", | |||||
"Microsoft.AspNetCore.Mvc": "1.0.1", | |||||
"Microsoft.AspNetCore.Razor.Tools": { | |||||
"version": "1.0.0-preview2-final", | |||||
"type": "build" | |||||
}, | |||||
"Microsoft.AspNetCore.Routing": "1.0.1", | |||||
"Microsoft.AspNetCore.Server.IISIntegration": "1.0.0", | |||||
"Microsoft.AspNetCore.Server.Kestrel": "1.0.1", | |||||
"Microsoft.AspNetCore.StaticFiles": "1.0.0", | |||||
"Microsoft.EntityFrameworkCore.SqlServer": "1.0.1", | |||||
"Microsoft.EntityFrameworkCore.SqlServer.Design": { | |||||
"version": "1.0.1", | |||||
"type": "build" | |||||
}, | |||||
"Microsoft.EntityFrameworkCore.Tools": { | |||||
"version": "1.0.0-preview2-final", | |||||
"type": "build" | |||||
}, | |||||
"Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0", | |||||
"Microsoft.Extensions.Configuration.Json": "1.0.0", | |||||
"Microsoft.Extensions.Configuration.UserSecrets": "1.0.0", | |||||
"Microsoft.Extensions.Logging": "1.0.0", | |||||
"Microsoft.Extensions.Logging.Console": "1.0.0", | |||||
"Microsoft.Extensions.Logging.Debug": "1.0.0", | |||||
"Microsoft.Extensions.Options.ConfigurationExtensions": "1.0.0", | |||||
"Microsoft.VisualStudio.Web.BrowserLink.Loader": "14.0.0", | |||||
"Microsoft.VisualStudio.Web.CodeGeneration.Tools": { | |||||
"version": "1.0.0-preview2-final", | |||||
"type": "build" | |||||
}, | |||||
"Microsoft.VisualStudio.Web.CodeGenerators.Mvc": { | |||||
"version": "1.0.0-preview2-final", | |||||
"type": "build" | |||||
}, | |||||
"IdentityServer4.AspNetIdentity": "1.0.0-rc3", | |||||
"IdentityServer4.EntityFramework": "1.0.0-rc3" | |||||
}, | |||||
"tools": { | |||||
"BundlerMinifier.Core": "2.0.238", | |||||
"Microsoft.AspNetCore.Razor.Tools": "1.0.0-preview2-final", | |||||
"Microsoft.AspNetCore.Server.IISIntegration.Tools": "1.0.0-preview2-final", | |||||
"Microsoft.EntityFrameworkCore.Tools": "1.0.0-preview2-final", | |||||
"Microsoft.Extensions.SecretManager.Tools": "1.0.0-preview2-final", | |||||
"Microsoft.VisualStudio.Web.CodeGeneration.Tools": { | |||||
"version": "1.0.0-preview2-final", | |||||
"imports": [ | |||||
"portable-net45+win8" | |||||
] | |||||
} | |||||
}, | |||||
"frameworks": { | |||||
"netcoreapp1.0": { | |||||
"imports": [ | |||||
"dotnet5.6", | |||||
"portable-net45+win8" | |||||
] | |||||
} | |||||
}, | |||||
"buildOptions": { | |||||
"emitEntryPoint": true, | |||||
"preserveCompilationContext": true, | |||||
"debugType": "portable" | |||||
}, | |||||
"runtimeOptions": { | |||||
"configProperties": { | |||||
"System.GC.Server": true | |||||
} | |||||
}, | |||||
"publishOptions": { | |||||
"include": [ | |||||
"wwwroot", | |||||
"Views", | |||||
"Areas/**/Views", | |||||
"appsettings.json", | |||||
"web.config", | |||||
"Dockerfile", | |||||
"docker-compose.yml", | |||||
".dockerignore" | |||||
] | |||||
}, | |||||
"scripts": { | |||||
"prepublish": [ | |||||
"bower install", | |||||
"dotnet bundle" | |||||
], | |||||
"postpublish": [ | |||||
"dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" | |||||
] | |||||
} | |||||
} |
@ -0,0 +1,14 @@ | |||||
<?xml version="1.0" encoding="utf-8"?> | |||||
<configuration> | |||||
<!-- | |||||
Configure your application settings in appsettings.json. Learn more at http://go.microsoft.com/fwlink/?LinkId=786380 | |||||
--> | |||||
<system.webServer> | |||||
<handlers> | |||||
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified"/> | |||||
</handlers> | |||||
<aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false"/> | |||||
</system.webServer> | |||||
</configuration> |
@ -0,0 +1,6 @@ | |||||
/// <autosync enabled="true" /> | |||||
/// <reference path="js/site.js" /> | |||||
/// <reference path="lib/bootstrap/dist/js/bootstrap.js" /> | |||||
/// <reference path="lib/jquery/dist/jquery.js" /> | |||||
/// <reference path="lib/jquery-validation/dist/jquery.validate.js" /> | |||||
/// <reference path="lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js" /> |
@ -0,0 +1,39 @@ | |||||
body { | |||||
margin-top: 65px; | |||||
} | |||||
.navbar-header { | |||||
position: relative; | |||||
top: -4px; | |||||
} | |||||
.navbar-brand > .icon-banner { | |||||
position: relative; | |||||
top: -2px; | |||||
display: inline; | |||||
} | |||||
.icon { | |||||
position: relative; | |||||
top: -10px; | |||||
} | |||||
.page-consent .client-logo { | |||||
float: left; | |||||
} | |||||
.page-consent .client-logo img { | |||||
width: 80px; | |||||
height: 80px; | |||||
} | |||||
.page-consent .consent-buttons { | |||||
margin-top: 25px; | |||||
} | |||||
.page-consent .consent-form .consent-scopecheck { | |||||
display: inline-block; | |||||
margin-right: 5px; | |||||
} | |||||
.page-consent .consent-form .consent-description { | |||||
margin-left: 25px; | |||||
} | |||||
.page-consent .consent-form .consent-description label { | |||||
font-weight: normal; | |||||
} | |||||
.page-consent .consent-form .consent-remember { | |||||
padding-left: 16px; | |||||
} |
@ -0,0 +1,57 @@ | |||||
body { | |||||
margin-top: 65px; | |||||
} | |||||
.navbar-header { | |||||
position:relative; | |||||
top:-4px; | |||||
} | |||||
.navbar-brand > .icon-banner { | |||||
position:relative; | |||||
top:-2px; | |||||
display:inline; | |||||
} | |||||
.icon { | |||||
position:relative; | |||||
top:-10px; | |||||
} | |||||
.page-consent { | |||||
.client-logo { | |||||
float: left; | |||||
img { | |||||
width: 80px; | |||||
height: 80px; | |||||
} | |||||
} | |||||
.consent-buttons { | |||||
margin-top: 25px; | |||||
} | |||||
.consent-form { | |||||
.consent-scopecheck { | |||||
display: inline-block; | |||||
margin-right: 5px; | |||||
} | |||||
.consent-scopecheck[disabled] { | |||||
//visibility:hidden; | |||||
} | |||||
.consent-description { | |||||
margin-left: 25px; | |||||
label { | |||||
font-weight: normal; | |||||
} | |||||
} | |||||
.consent-remember { | |||||
padding-left: 16px; | |||||
} | |||||
} | |||||
} |
@ -0,0 +1 @@ | |||||
body{margin-top:65px}.navbar-header{position:relative;top:-4px}.navbar-brand>.icon-banner{position:relative;top:-2px;display:inline}.icon{position:relative;top:-10px}.page-consent .client-logo{float:left}.page-consent .client-logo img{width:80px;height:80px}.page-consent .consent-buttons{margin-top:25px}.page-consent .consent-form .consent-scopecheck{display:inline-block;margin-right:5px}.page-consent .consent-form .consent-description{margin-left:25px}.page-consent .consent-form .consent-description label{font-weight:normal}.page-consent .consent-form .consent-remember{padding-left:16px} |