PLAINCONCEPTS\ccanizares e89adb5b30 SPA: Change banner and logo
2017-01-12 16:18:43 +01:00

324 lines
12 KiB
C#

// 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;
using Microsoft.AspNetCore.Authorization;
using eShopOnContainers.Identity.Models.AccountViewModels;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Authentication;
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;
private readonly UserManager<ApplicationUser> _userManager;
public AccountController(
//InMemoryUserLoginService loginService,
ILoginService<ApplicationUser> loginService,
IIdentityServerInteractionService interaction,
IClientStore clientStore,
ILoggerFactory loggerFactory,
UserManager<ApplicationUser> userManager)
{
_loginService = loginService;
_interaction = interaction;
_clientStore = clientStore;
_logger = loggerFactory.CreateLogger<AccountController>();
_userManager = userManager;
}
/// <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);
ViewData["ReturnUrl"] = returnUrl;
return View(vm);
}
/// <summary>
/// Handle postback from username/password login
/// </summary>
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginViewModel model)
{
if (ModelState.IsValid)
{
var user = await _loginService.FindByUsername(model.Email);
if (await _loginService.ValidateCredentials(user, model.Password))
{
AuthenticationProperties props = null;
if (model.RememberMe)
{
props = new AuthenticationProperties
{
IsPersistent = true,
ExpiresUtc = DateTimeOffset.UtcNow.AddYears(10)
};
};
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);
ViewData["ReturnUrl"] = model.ReturnUrl;
return View(vm);
}
async Task<LoginViewModel> BuildLoginViewModelAsync(string returnUrl, AuthorizationRequest context)
{
var allowLocal = true;
if (context?.ClientId != null)
{
var client = await _clientStore.FindEnabledClientByIdAsync(context.ClientId);
if (client != null)
{
allowLocal = client.EnableLocalLogin;
}
}
return new LoginViewModel
{
ReturnUrl = returnUrl,
Email = context?.LoginHint,
};
}
async Task<LoginViewModel> BuildLoginViewModelAsync(LoginViewModel model)
{
var context = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl);
var vm = await BuildLoginViewModelAsync(model.ReturnUrl, context);
vm.Email = model.Email;
vm.RememberMe = model.RememberMe;
return vm;
}
/// <summary>
/// Show logout page
/// </summary>
[HttpGet]
public async Task<IActionResult> Logout(string logoutId)
{
if (User.Identity.IsAuthenticated == false)
{
// if the user is not authenticated, then just show logged out page
return await Logout(new LogoutViewModel { LogoutId = logoutId });
}
//Test for Xamarin.
var context = await _interaction.GetLogoutContextAsync(logoutId);
if (context?.ShowSignoutPrompt == false)
{
//it's safe to automatically sign-out
return await Logout(new LogoutViewModel { LogoutId = logoutId });
}
// show the logout prompt. this prevents attacks where the user
// is automatically signed out by another malicious web page.
var vm = new LogoutViewModel
{
LogoutId = logoutId
};
return View(vm);
}
/// <summary>
/// Handle logout page postback
/// </summary>
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Logout(LogoutViewModel model)
{
var idp = User?.FindFirst(JwtClaimTypes.IdentityProvider)?.Value;
if (idp != null && idp != IdentityServerConstants.LocalIdentityProvider)
{
if (model.LogoutId == null)
{
// if there's no current logout context, we need to create one
// this captures necessary info from the current logged in user
// before we signout and redirect away to the external IdP for signout
model.LogoutId = await _interaction.CreateLogoutContextAsync();
}
string url = "/Account/Logout?logoutId=" + model.LogoutId;
try
{
// hack: try/catch to handle social providers that throw
await HttpContext.Authentication.SignOutAsync(idp, new AuthenticationProperties { RedirectUri = url });
}
catch(Exception ex)
{
_logger.LogCritical(ex.Message);
}
}
// 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);
return Redirect(logout?.PostLogoutRedirectUri);
}
public async Task<IActionResult> DeviceLogOut(string redirectUrl)
{
// delete authentication cookie
await HttpContext.Authentication.SignOutAsync();
// set this so UI rendering sees an anonymous user
HttpContext.User = new ClaimsPrincipal(new ClaimsIdentity());
return Redirect(redirectUrl);
}
/// <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);
}
// GET: /Account/Register
[HttpGet]
[AllowAnonymous]
public IActionResult Register(string returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
return View();
}
//
// POST: /Account/Register
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Register(RegisterViewModel model, string returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
if (ModelState.IsValid)
{
var user = new ApplicationUser
{
UserName = model.Email,
Email = model.Email,
CardHolderName = model.User.CardHolderName,
CardNumber = model.User.CardNumber,
CardType = model.User.CardType,
City = model.User.City,
Country = model.User.Country,
Expiration = model.User.Expiration,
LastName = model.User.LastName,
Name = model.User.Name,
Street = model.User.Street,
State = model.User.State,
ZipCode = model.User.ZipCode,
PhoneNumber = model.User.PhoneNumber,
SecurityNumber = model.User.SecurityNumber
};
var result = await _userManager.CreateAsync(user, model.Password);
if (result.Errors.Count() > 0)
{
AddErrors(result);
// If we got this far, something failed, redisplay form
return View(model);
}
}
if (returnUrl != null) {
if (HttpContext.User.Identity.IsAuthenticated)
return Redirect(returnUrl);
else
if (ModelState.IsValid)
return RedirectToAction("login", "account", new { returnUrl = returnUrl });
else
return View(model);
}
return RedirectToAction("index", "home");
}
[HttpGet]
public IActionResult Redirecting()
{
return View();
}
private void AddErrors(IdentityResult result)
{
foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
}
}
}