Improvements in identity, securice basket api, login-logout from mvc application and consume securiced basket.

This commit is contained in:
Carlos Cañizares Estévez 2016-11-29 15:10:16 +01:00
parent a939fe7a51
commit 0a344f6cdc
50 changed files with 4912 additions and 695 deletions

View File

@ -14,7 +14,6 @@ services:
ports:
- "5100:80"
depends_on:
- catalog.api
- identity.service
- basket.api
@ -31,14 +30,13 @@ services:
ports:
- "5104:80"
depends_on:
- catalog.api
- basket.api
- identity.service
catalog.api:
image: eshop/catalog.api
environment:
- ConnectionString=Server=catalog.data;Initial Catalog=CatalogData;User Id=sa;Password=Pass@word
- ConnectionString=Server=catalog.data;Database=CatalogDB;User Id=sa;Password=Pass@word
expose:
- "80"
ports:
@ -77,6 +75,7 @@ services:
image: eshop/identity
environment:
- Spa:http://webspa
- ConnectionString=Server=identity.data;Database=aspnet-Microsoft.eShopOnContainers.WebMVC;User Id=sa;Password=Pass@word
expose:
- "80"
ports:

View File

@ -4,6 +4,7 @@ using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.eShopOnContainers.Services.Basket.API.Model;
using Microsoft.AspNetCore.Authorization;
namespace Microsoft.eShopOnContainers.Services.Basket.API.Controllers
{
@ -12,6 +13,7 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API.Controllers
//If this is the case we should also investigate changing the serialization format used for Redis,
//using a HashSet instead of a simple string.
[Route("/")]
[Authorize]
public class BasketController : Controller
{
private IBasketRepository _repository;

View File

@ -17,6 +17,7 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>()
.UseUrls("http://localhost:5008")
.Build();
host.Run();

View File

@ -47,6 +47,15 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API
return ConnectionMultiplexer.Connect(ips.First().ToString());
});
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
});
services.AddTransient<IBasketRepository, RedisBasketRepository>();
}
@ -56,9 +65,12 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
// Use frameworks
app.UseCors("CorsPolicy");
app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
{
Authority = Configuration.GetValue("IdentityUrl", "http://localhost:5000"),
Authority = "http://localhost:5000",
ScopeName = "basket",
RequireHttpsMetadata = false
});

View File

@ -7,6 +7,6 @@
"Microsoft": "Information"
}
},
"identityUrl": "http://localhost:5105",
"identityUrl": "http://localhost:5000",
"ConnectionString": "127.0.0.1"
}

View File

@ -49,11 +49,9 @@ namespace eShopOnContainers.Identity.Configuration
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,
@ -68,11 +66,9 @@ namespace eShopOnContainers.Identity.Configuration
ClientName = "eShop Xamarin 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,
@ -90,9 +86,7 @@ namespace eShopOnContainers.Identity.Configuration
new Secret("secret".Sha256())
},
ClientUri = "http://localhost:2114",
AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
RedirectUris = new List<string>
{
"http://localhost:2114/signin-oidc"

View File

@ -19,6 +19,10 @@ 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
{
@ -34,6 +38,7 @@ namespace IdentityServer4.Quickstart.UI.Controllers
private readonly IIdentityServerInteractionService _interaction;
private readonly IClientStore _clientStore;
private readonly ILogger _logger;
private readonly UserManager<ApplicationUser> _userManager;
public AccountController(
@ -41,12 +46,14 @@ namespace IdentityServer4.Quickstart.UI.Controllers
ILoginService<ApplicationUser> loginService,
IIdentityServerInteractionService interaction,
IClientStore clientStore,
ILoggerFactory loggerFactory)
ILoggerFactory loggerFactory,
UserManager<ApplicationUser> userManager)
{
_loginService = loginService;
_interaction = interaction;
_clientStore = clientStore;
_logger = loggerFactory.CreateLogger<AccountController>();
_userManager = userManager;
}
/// <summary>
@ -64,12 +71,6 @@ namespace IdentityServer4.Quickstart.UI.Controllers
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);
}
@ -78,11 +79,11 @@ namespace IdentityServer4.Quickstart.UI.Controllers
/// </summary>
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginInputModel model)
public async Task<IActionResult> Login(LoginViewModel model)
{
if (ModelState.IsValid)
{
var user = await _loginService.FindByUsername(model.Username);
var user = await _loginService.FindByUsername(model.Email);
// validate username/password against in-memory store
if (await _loginService.ValidateCredentials(user, model.Password))
{
@ -92,7 +93,7 @@ namespace IdentityServer4.Quickstart.UI.Controllers
AuthenticationProperties props = null;
// only set explicit expiration here if persistent.
// otherwise we reply upon expiration configured in cookie middleware.
if (model.RememberLogin)
if (model.RememberMe)
{
props = new AuthenticationProperties
{
@ -101,7 +102,6 @@ namespace IdentityServer4.Quickstart.UI.Controllers
};
};
//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
@ -123,14 +123,6 @@ namespace IdentityServer4.Quickstart.UI.Controllers
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)
{
@ -138,29 +130,22 @@ namespace IdentityServer4.Quickstart.UI.Controllers
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()
Email = context?.LoginHint,
};
}
async Task<LoginViewModel> BuildLoginViewModelAsync(LoginInputModel model)
async Task<LoginViewModel> BuildLoginViewModelAsync(LoginViewModel model)
{
var context = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl);
var vm = await BuildLoginViewModelAsync(model.ReturnUrl, context);
vm.Username = model.Username;
vm.RememberLogin = model.RememberLogin;
vm.Email = model.Email;
vm.RememberMe = model.RememberMe;
return vm;
}
@ -329,5 +314,62 @@ namespace IdentityServer4.Quickstart.UI.Controllers
return Redirect("~/");
}
// 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);
}
}
return RedirectToAction("index", "home");
}
private void AddErrors(IdentityResult result)
{
foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
}
}
}

View File

@ -10,6 +10,7 @@ using System.Threading.Tasks;
using IdentityServer4.Models;
using IdentityServer4.Stores;
using IdentityServer4.Quickstart.UI.Models;
using eShopOnContainers.Identity.Models.AccountViewModels;
namespace IdentityServer4.Quickstart.UI.Controllers
{

View File

@ -4,7 +4,7 @@
using System.Collections.Generic;
namespace IdentityServer4.Quickstart.UI.Models
namespace eShopOnContainers.Identity.Models.AccountViewModels
{
public class ConsentInputModel
{

View File

@ -6,7 +6,7 @@ using System.Collections.Generic;
using System.Linq;
using IdentityServer4.Models;
namespace IdentityServer4.Quickstart.UI.Models
namespace eShopOnContainers.Identity.Models.AccountViewModels
{
public class ConsentViewModel : ConsentInputModel
{

View File

@ -2,7 +2,7 @@
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
namespace IdentityServer4.Quickstart.UI.Models
namespace eShopOnContainers.Identity.Models.AccountViewModels
{
public class LoggedOutViewModel
{

View File

@ -1,8 +1,8 @@
using System;
using System.Collections.Generic;
// 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;
using System.Linq;
using System.Threading.Tasks;
namespace eShopOnContainers.Identity.Models.AccountViewModels
{
@ -18,5 +18,6 @@ namespace eShopOnContainers.Identity.Models.AccountViewModels
[Display(Name = "Remember me?")]
public bool RememberMe { get; set; }
public string ReturnUrl { get; set; }
}
}
}

View File

@ -2,7 +2,7 @@
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
namespace IdentityServer4.Quickstart.UI.Models
namespace eShopOnContainers.Identity.Models.AccountViewModels
{
public class LogoutViewModel
{

View File

@ -23,5 +23,7 @@ namespace eShopOnContainers.Identity.Models.AccountViewModels
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
public ApplicationUser User { get; set; }
}
}

View File

@ -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 eShopOnContainers.Identity.Models.AccountViewModels
{
//public class _LoginViewModel : LoginViewModel
//{
// public bool EnableLocalLogin { get; set; }
// public IEnumerable<ExternalProvider> ExternalProviders { get; set; }
//}
//public class ExternalProvider
//{
// public string DisplayName { get; set; }
// public string AuthenticationScheme { get; set; }
//}
}

View File

@ -1,18 +0,0 @@
// 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; }
}
}

View File

@ -1,20 +0,0 @@
// 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; }
}
}

View File

@ -62,7 +62,7 @@ namespace eShopOnContainers.Identity
.AddInMemoryScopes(Config.GetScopes())
.AddInMemoryClients(Config.GetClients())
.AddAspNetIdentity<ApplicationUser>()
.Services.AddTransient<IProfileService, ProfileService>();
.Services.AddTransient<IProfileService, ProfileService>();
//Configuration Settings:
services.AddOptions();

View File

@ -1,4 +1,4 @@
@model IdentityServer4.Quickstart.UI.Models.LoggedOutViewModel
@model eShopOnContainers.Identity.Models.AccountViewModels.LoggedOutViewModel
<div class="page-header">
<h1>

View File

@ -1,83 +1,56 @@
@model IdentityServer4.Quickstart.UI.Models.LoginViewModel
@using System.Collections.Generic
@using Microsoft.AspNetCore.Http
@using Microsoft.AspNetCore.Http.Authentication
@model eShopOnContainers.Identity.Models.AccountViewModels.LoginViewModel
<div class="login-page">
<div class="page-header">
<h1>Login</h1>
</div>
@Html.Partial("_ValidationSummary")
@{
ViewData["Title"] = "Log in";
}
<div class="brand-header-block">
<ul class="container">
<li><a asp-area="" asp-controller="Account" asp-action="Register">REGISTER</a></li>
<li class="active" style="margin-right: 65px;">LOGIN</li>
</ul>
</div>
<div class="container account-login-container">
<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 class="col-md-12">
<section>
<form asp-controller="Account" asp-action="Login" asp-route-returnurl="@ViewData["ReturnUrl"]" method="post" class="form-horizontal">
<input type="hidden" asp-for="ReturnUrl" />
<h4>ARE YOU REGISTERED?</h4>
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="Email" class="control-label form-label"></label>
<input asp-for="Email" class="form-control form-input form-input-center" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
<div class="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 class="form-group">
<label asp-for="Password" class="control-label form-label"></label>
<input asp-for="Password" class="form-control form-input form-input-center" />
<span asp-validation-for="Password" class="text-danger"></span>
</div>
</div>
</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 class="form-group">
<div class="checkbox">
<label asp-for="RememberMe">
<input asp-for="RememberMe" />
@Html.DisplayNameFor(m => m.RememberMe)
</label>
</div>
</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 class="form-group">
<button type="submit" class="btn btn-default btn-brand btn-brand-big">&nbsp;LOG IN&nbsp;</button>
</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>
}
<p>
<a asp-action="Register" asp-route-returnurl="@ViewData["ReturnUrl"]" class="text">Register as a new user?</a>
</p>
</form>
</section>
</div>
</div>
</div>
</div>
@section Scripts {
@{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
}

View File

@ -1,4 +1,4 @@
@model IdentityServer4.Quickstart.UI.Models.LogoutViewModel
@model eShopOnContainers.Identity.Models.AccountViewModels.LogoutViewModel
<div class="logout-page">
<div class="page-header">

View File

@ -1,42 +1,107 @@
@model RegisterViewModel
@using eShopOnContainers.Identity.Models.AccountViewModels
@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 class="brand-header-block">
<ul class="container">
<li class="active">REGISTER</li>
<li style="margin-right: 65px;"><a asp-area="" asp-controller="Account" asp-action="Login">LOGIN</a></li>
</ul>
</div>
<div class="container register-container">
<form asp-controller="Account" asp-action="Register" asp-route-returnurl="@ViewData["ReturnUrl"]" method="post" class="form-horizontal">
<h4 class="order-create-section-title">CREATE NEW ACCOUNT</h4>
<div asp-validation-summary="All" class="text-danger"></div>
<div class="row">
<div class="form-group col-sm-6">
<label asp-for="User.Name" class="control-label form-label">NAME</label>
<input asp-for="User.Name" class="form-control form-input" />
<span asp-validation-for="User.Name" class="text-danger" />
</div>
<div class="form-group col-sm-6">
<label asp-for="User.LastName" class="control-label form-label">LAST NAME</label>
<input asp-for="User.LastName" class="form-control form-input" />
<span asp-validation-for="User.LastName" class="text-danger" />
</div>
<div class="form-group col-sm-6">
<label asp-for="User.Street" class="control-label form-label">ADDRESS</label>
<input asp-for="User.Street" class="form-control form-input" />
<span asp-validation-for="User.Street" class="text-danger" />
</div>
<div class="form-group col-sm-6">
<label asp-for="User.City" class="control-label form-label"></label>
<input asp-for="User.City" class="form-control form-input" />
<span asp-validation-for="User.City" class="text-danger" />
</div>
<div class="form-group col-sm-6">
<label asp-for="User.State" class="control-label form-label"></label>
<input asp-for="User.State" class="form-control form-input" />
<span asp-validation-for="User.State" class="text-danger" />
</div>
<div class="form-group col-sm-6">
<label asp-for="User.Country" class="control-label form-label"></label>
<input asp-for="User.Country" class="form-control form-input" />
<span asp-validation-for="User.Country" class="text-danger" />
</div>
<div class="form-group col-sm-6">
<label asp-for="User.ZipCode" class="control-label form-label">POSTCODE</label>
<input asp-for="User.ZipCode" class="form-control form-input" />
<span asp-validation-for="User.ZipCode" class="text-danger" />
</div>
<div class="form-group col-sm-6">
<label asp-for="User.PhoneNumber" class="control-label form-label">PHONE NUMBER</label>
<input asp-for="User.PhoneNumber" class="form-control form-input" />
<span asp-validation-for="User.PhoneNumber" class="text-danger" />
</div>
<div class="form-group col-sm-6">
<label asp-for="User.CardNumber" class="control-label form-label">Card Number</label>
<input asp-for="User.CardNumber" class="form-control form-input" />
<span asp-validation-for="User.CardNumber" class="text-danger" />
</div>
<div class="form-group col-sm-6">
<label asp-for="User.CardHolderName" class="control-label form-label">Cardholder Name</label>
<input asp-for="User.CardHolderName" class="form-control form-input" />
<span asp-validation-for="User.CardHolderName" class="text-danger" />
</div>
<div class="form-group col-sm-3">
<label asp-for="User.Expiration" class="control-label form-label">Expiration Date</label>
<input asp-for="User.Expiration" placeholder="MM/YY" class="form-control form-input form-input-small" />
<span asp-validation-for="User.Expiration" class="text-danger" />
</div>
<div class="form-group col-sm-3">
<label asp-for="User.SecurityNumber" class="control-label form-label">Security Code</label>
<input asp-for="User.SecurityNumber" class="form-control form-input form-input-small" />
<span asp-validation-for="User.SecurityNumber" class="text-danger" />
</div>
</div>
</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>
<br /><br />
<div class="row">
<div class="form-group col-sm-6">
<label asp-for="Email" class="control-label form-label"></label>
<input asp-for="Email" class="form-control form-input" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
<div class="form-group col-sm-offset-6"></div>
<div class="form-group col-sm-6">
<label asp-for="Password" class="control-label form-label"></label>
<input asp-for="Password" class="form-control form-input" />
<span asp-validation-for="Password" class="text-danger"></span>
</div>
<div class="form-group col-sm-6">
<label asp-for="ConfirmPassword" class="control-label form-label"></label>
<input asp-for="ConfirmPassword" class="form-control form-input" />
<span asp-validation-for="ConfirmPassword" class="text-danger"></span>
</div>
</div>
<br /><br />
<div class="form-group">
<button type="submit" class="btn btn-default btn-brand">&nbsp;Register&nbsp;</button>
</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>
<br /><br />
</form>
</div>
@section Scripts {
@{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
}

View File

@ -0,0 +1,49 @@
@*@model eShopOnContainers.Identity.Models.AccountViewModels.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>
}
</div>
</div>*@

View File

@ -1,4 +1,4 @@
@model IdentityServer4.Quickstart.UI.Models.ConsentViewModel
@model eShopOnContainers.Identity.Models.AccountViewModels.ConsentViewModel
<div class="page-consent">
<div class="row page-header">

View File

@ -1,4 +1,4 @@
@model IdentityServer4.Quickstart.UI.Models.ScopeViewModel
@model eShopOnContainers.Identity.Models.AccountViewModels.ScopeViewModel
<li class="list-group-item">
<label>

View File

@ -8,6 +8,7 @@
<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" />
<link rel="stylesheet" href="~/css/sitemvc.css" />
</head>
<body>
<div class="navbar navbar-inverse navbar-fixed-top">
@ -26,7 +27,9 @@
</span>
</a>
</div>
<div>
<a asp-action="Register" asp-controller="Account">Register User</a>
</div>
@if (User.Identity.IsAuthenticated)
{
<ul class="nav navbar-nav">
@ -34,8 +37,6 @@
<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>

View File

@ -5,7 +5,8 @@
"outputFileName": "wwwroot/css/site.min.css",
// An array of relative input file paths. Globbing patterns supported
"inputFiles": [
"wwwroot/css/site.css"
"wwwroot/css/site.css",
"wwwroot/css/sitemvc.css"
]
},
{

View File

@ -55,3 +55,870 @@
}
}
}
@font-face {
font-family: Montserrat;
font-weight: 400;
src: url("/fonts/Montserrat-Regular.eot?") format("eot"),url("/fonts/Montserrat-Regular.woff") format("woff"),url("/fonts/Montserrat-Regular.ttf") format("truetype"),url("/fonts/Montserrat-Regular.svg#Montserrat") format("svg")
}
@font-face {
font-family: Montserrat;
font-weight: 700;
src: url("/fonts/Montserrat-Bold.eot?") format("eot"),url("/fonts/Montserrat-Bold.woff") format("woff"),url("/fonts/Montserrat-Bold.ttf") format("truetype"),url("/fonts/Montserrat-Bold.svg#Montserrat") format("svg")
}
body {
padding-top: 80px;
/*padding-bottom: 20px;*/
font-family: Montserrat,sans-serif;
min-width:480px;
}
.mt-15 {
margin-top:15px;
}
/* Wrapping element */
/* Set some basic padding to keep content from hitting the edges */
.body-content {
padding-left: 15px;
padding-right: 15px;
}
/* Set widths on the form inputs since otherwise they're 100% wide */
input,
select,
textarea {
max-width: 280px;
}
.select-filter {
background-color: transparent;
padding: 10px;
margin: 10px;
margin-right: 20px;
color: white;
padding-top: 20px;
padding-bottom: 3px;
min-width: 140px;
border-color: #37c7ca;
max-height: 43px;
-webkit-appearance: none;
}
.select-filter option {
background-color: #00a69c;
}
select::-ms-expand {
display: none;
}
.select-filter-wrapper {
z-index: 0;
display:inline-block;
margin-left: -10px;
}
.select-filter-wrapper::before {
content: attr(data-name);
opacity: 0.5;
z-index: 1;
text-transform: uppercase;
position: absolute;
font-size: 10px;
margin-top: 15px;
margin-left: 21px;
color: white;
}
.select-filter-arrow {
position: absolute;
margin-left: 130px;
margin-top: 40px;
}
.btn-brand-small-filter {
margin-top: 10px;
position: absolute;
margin-left: 15px;
}
/* Carousel */
.carousel-caption p {
font-size: 20px;
line-height: 1.4;
}
.layout-cart-image {
height: 36px;
margin-top: 5px;
}
.layout-cart-badge {
position: absolute;
margin-top: 2px;
margin-left: 14px;
background-color: #83d01b;
padding: 1px;
color: white;
border-radius: 50%;
width: 18px;
height: 18px;
font-size: 12px;
cursor: pointer;
}
/* buttons and links extension to use brackets: [ click me ] */
.btn-bracketed:hover:before {
display: inline-block;
content: "[";
padding-right: 0.5em;
color: chartreuse;
}
.btn-bracketed:hover:after {
display: inline-block;
content: "]";
padding-left: 0.5em;
color: chartreuse;
}
.btn-brand {
background-color: #83D01B;
color: white;
padding: 10px 20px 10px 20px;
border-radius: 0px;
border: none;
width: 255px;
display: inline-block;
text-align: center;
text-transform: uppercase;
height: 45px;
font-size: 16px;
font-weight: normal;
}
.btn-brand::before {
content: '['
}
.btn-brand::after {
content: ']'
}
.btn-brand:hover:before {
padding-right: 5px;
}
.btn-brand:hover:after {
padding-left: 5px;
}
.btn-brand-big {
width: 360px;
margin-top: 20px;
}
.btn-brand-small {
width: 45px;
}
.btn-brand-small::before {
content: '';
}
.btn-brand-small::after {
content: '';
}
.btn-brand-small:hover:before {
content: '';
padding: 0;
}
.btn-brand-small:hover:after {
content: '';
padding: 0;
}
.btn-brand-dark {
background-color: #00a69c;
}
.btn-brand:hover {
color: white;
background-color: #83D01B;
text-decoration:none;
}
.btn-brand-dark:hover {
background-color: #00a69c;
}
.btn-cart {
float: right;
margin-top: 40px;
margin-bottom: 40px;
}
.btn-catalog-apply {
padding:0;
}
.form-label {
text-transform: uppercase;
font-weight: normal!important;
text-align: left;
margin-bottom: 10px !important;
color: #404040;
}
.form-input {
border-radius: 0;
padding: 10px;
height: 45px;
}
.form-input-small {
max-width: 100px!important;
}
.form-select {
border-radius: 0;
padding: 10px;
height: 45px;
width: 150px;
}
/* Make .svg files in the carousel display properly in older browsers */
.carousel-inner .item img[src$=".svg"] {
width: 100%;
}
.navbar-inverse {
background-color: #FFF;
border-color: #FFF;
}
/*.navbar-inverse li {
margin-top: 10px;
}*/
.btn-login {
border: 1px solid #00A69C;
height: 36px!important;
margin-right: 10px;
margin-top: 10px;
background-color: white;
color: #00a69c;
text-transform:uppercase;
max-width: 140px;
width: 140px;
padding-top:8px!important;
}
.btn-login {
font-weight:normal!important;
}
.btn-login::before {
content: '[';
}
.btn-login::after {
content: ']';
}
.btn-login:hover:before {
content: '[ ';
}
.btn-login:hover:after {
content: ' ]';
}
.navbar-inverse li a {
height: 30px;
padding: 5px 20px;
color: #00A69C !important;
}
.navbar-brand {
margin-top: 20px;
background-image: url(../images/brand.PNG);
width: 201px;
height: 44px;
margin-left: 0px !important;
}
.nav > li > a {
color: white;
}
.nav > li > a:hover, .nav > li > a:focus {
background-color: #00A69C;
font-weight: bolder;
}
.container-fluid {
padding-left: 0px;
padding-right: 0px;
}
.home-banner {
width: 100%;
margin-right: 0px;
margin-left: 0px;
background-image: url(../images/main_banner.png);
background-size: cover;
height: 258px;
background-position: center;
}
.home-banner-text {
margin-top: 70px;
}
.home-catalog-container {
min-height: 400px;
margin-bottom: 20px;
}
.home-catalog-filter-container {
background-color: #00A69C;
height:63px;
}
.home-catalog-filter-container li a {
padding-top: 5px !important;
}
.home-catalog-filter-brands::before {
content: 'BRAND';
color: white;
font-size: x-small;
opacity: 0.5;
margin: 10px 0px 0px 15px;
}
.home-catalog-filter-types::before {
content: 'TYPES';
color: white;
font-size: x-small;
opacity: 0.5;
margin: 10px 0px 0px 15px;
}
.home-catalog-item {
margin-top: 10px;
margin-bottom: 10px;
}
.home-catalog-item-image {
width: 100%;
object-fit: cover;
/* max-width: 320px; */
text-align: center;
}
.home-catalog-item-image-addCart {
background-color: #83D01B;
color: white;
display: inline-block;
height: 43px;
padding: 10px 20px 10px 20px;
font-weight: bold;
text-align: center;
margin-top: 10px;
margin-left: 60px;
margin-right: 60px;
font-size: 16px;
font-weight: normal;
}
.home-catalog-item-image-addCart:hover {
color: white;
text-decoration: none;
}
.home-catalog-item-image:hover:after {
cursor: pointer;
}
.home-catalog-item-title {
text-align: center;
text-transform: uppercase;
font-weight: 300;
font-size: 16px;
margin-top: 20px;
}
.home-catalog-item-price {
text-align: center;
font-weight: 900;
font-size: 28px;
}
.home-catalog-item-price::before {
content: '$';
}
.home-catalog-noResults {
text-align:center;
margin-top: 100px;
}
.container .nav .navbar-nav .col-sm-6 ::before {
content: 'BRAND';
}
.validation-summary-errors li {
list-style: none;
}
footer {
background-color: black;
height: 150px;
vertical-align: middle;
}
footer .brand {
margin-top: 25px;
background-image: url(../images/brand_dark.PNG);
max-width: 231px;
height: 52px;
margin-left: 0px !important;
}
footer .text {
text-align: right;
width: 100%;
height: 100%;
color: #83D01B;
margin-top: 10px;
}
.text {
color: #83D01B;
}
.text:hover {
color: #83D01B;
}
form .col-md-4 {
text-align: right;
}
.brand-header-block {
background-color: #00A69C;
height: 63px;
}
.brand-header-block li {
list-style: none;
display: inline;
opacity: 0.5;
margin-top: 25px;
margin-left: 10px;
float: right;
cursor: pointer;
color: white;
}
.brand-header-block li a {
color: white;
}
.brand-header-block li a:hover {
text-decoration:none;
}
.brand-header-block .active {
opacity: 1;
}
.brand-header-block .active::before {
content: '[ ';
color: greenyellow;
}
.brand-header-block .active::after {
content: ' ]';
color: greenyellow;
}
.brand-header-back {
float: left!important;
margin-top: 20px!important;
text-transform: uppercase;
}
.account-login-container {
min-height: 70vh;
text-align: center;
padding-top: 40px;
}
.account-register-container {
min-height: 70vh;
text-align: center !important;
align-content: center;
}
.cart-index-container {
min-height: 70vh;
padding-top: 40px;
margin-bottom: 30px;
min-width: 992px;
}
.register-container {
min-height: 70vh;
padding-top: 40px;
margin-bottom: 30px;
padding-left: 30px;
}
.order-create-container {
min-height: 70vh;
padding-top: 40px;
margin-bottom: 30px;
padding-left: 30px;
min-width: 995px;
}
.cart-product-column {
max-width: 120px;
text-transform: uppercase;
vertical-align: middle!important;
}
.order-create-container .cart-product-column {
max-width: 130px;
}
.cart-product-column-name {
width: 220px;
}
.cart-subtotal-label {
font-size: 12px;
color: #404040;
margin-top:10px;
}
.cart-subtotal-value {
font-size: 20px;
color: #00a69c;
}
.cart-total-label {
font-size: 14px;
color: #404040;
margin-top:10px;
}
.cart-total-value {
font-size: 28px;
color: #00a69c;
text-align: left;
}
.cart-product-image {
max-width: 210px;
}
.cart-section-total {
margin-bottom: 5px;
margin-left: 175px;
text-align: left;
}
.cart-product-column input {
width: 70px;
text-align: center;
}
.cart-refresh-button {
margin-top:0;
background-image: url('../images/refresh.svg');
color: white;
font-size: 8px;
width: 40px;
height: 40px;
background-color:transparent;
border:none;
margin-top: 25px;
margin-left:15px;
}
.cart-refresh-button:hover {
background-color:transparent;
}
.cart-totals {
border-bottom:none!important;
}
.input-validation-error {
border: 1px solid #fb0d0d;
}
.text-danger {
color: #fb0d0d;
font-size: 12px;
}
.cart {
border: none !important;
}
.form-horizontal h4 {
margin-top: 30px;
}
.form-horizontal .form-group {
margin-right: 0px!important;
}
.form-control:focus {
border-color: #83d01b;
}
.form-input-center {
margin: auto;
}
.order-index-container {
min-height: 70vh;
padding-top: 40px;
margin-bottom: 30px;
}
.order-index-container .table tbody tr {
border-bottom:none;
}
.order-index-container .table tbody tr td {
border-top:none;
padding-top:10px;
padding-bottom:10px;
}
.order-index-container .table tbody tr:nth-child(even) {
background-color: #f5f5f5;
}
.order-create-section-title {
margin-left: -15px;
text-transform: uppercase;
}
.order-create-section-items {
margin-left: -45px;
width: 102%;
}
.order-detail-button {
}
.order-detail-button a {
color: #83d01b;
}
.order-detail-container {
min-height: 70vh;
padding-top: 40px;
margin-bottom: 30px;
}
.order-detail-container .table tbody tr:first-child td{
border-top:none;
}
.order-detail-container .table tr{
border-bottom:none;
}
.order-detail-section {
margin-top: 50px;
}
.order-detail-container .table {
margin-left: -7px;
}
.order-section-total {
margin-bottom: 5px;
margin-left: 40px;
text-align: left;
}
.fr {
float:right!important;
}
.down-arrow {
background-image: url('../images/arrow-down.png');
height: 7px;
width: 10px;
display: inline-block;
margin-left: 20px;
}
.logout-icon {
background-image: url('../images/logout.PNG');
display: inline-block;
height:19px;
width:19px;
margin-left: 15px;
}
.myorders-icon {
background-image: url('../images/my_orders.PNG');
display: inline-block;
height: 20px;
width: 20px;
margin-left: 15px;
}
.login-user {
position: absolute!important;
top: 30px;
right: 65px;
cursor:pointer;
}
.login-user-dropdown {
position: relative;
display: inline-block;
}
.login-user-dropdown-content {
display: none;
position: absolute;
background-color: #FFFFFF;
min-width: 160px;
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
/*left: 100px;*/
right: 0px;
}
.login-user-dropdown-content a {
color: black;
padding: 12px 16px;
text-decoration: none;
display: block;
text-align:right;
text-transform:uppercase;
}
.login-user:hover .login-user-dropdown-content {
display: block;
}
.down-arrow:hover > .login-user-dropdown-content {
display: block;
}
.login-user-dropdown-content a:hover {
color: #83d01b;
}
.es-header {
min-height: 80px!important;
}
.es-pager-bottom {
margin-top: 40px;
}
.es-pager-top {
margin-bottom: 20px;
margin-top: 20px;
}
.es-pager-top ul {
list-style: none;
}
.es-pager-bottom ul {
list-style: none;
}
.page-item {
cursor: pointer;
}
.next {
position: absolute;
right: 15px;
top: 0;
}
.previous {
position: absolute;
left: 0;
top: 0;
}
.is-disabled{
cursor: not-allowed;
opacity: .5;
pointer-events: none;
}
.table tr {
border-bottom:1px solid #ddd;
}
.table th {
text-transform: uppercase;
}
.navbar-nav {
margin-top: 10px;
margin-bottom: 7.5px;
margin-right: -10px;
float: right;
}
@media screen and (max-width: 1195px) {
.cart-product-column-name {
display:none;
}
}
/* Hide/rearrange for smaller screens */
@media screen and (max-width: 767px) {
/* Hide captions */
.carousel-caption {
display: none;
}
footer .text {
text-align: left;
margin-top: -15px;
}
.cart-product-column-brand {
display:none;
}
}
@media screen and (min-width: 992px) {
.form-input {
width: 360px;
max-width: 360px;
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 111 KiB

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 104 KiB

View File

@ -1,4 +1,4 @@
{
//"ConnectionString": "Server=tcp:ordering.data;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word;"
//"ConnectionString": "Server=ordering.data;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word;"
"ConnectionString": "Server=tcp:127.0.0.1,5432;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word;"
}

View File

@ -1,18 +1,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using System.Security.Claims;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.Extensions.Logging;
using Microsoft.eShopOnContainers.WebMVC.Models;
using Microsoft.eShopOnContainers.WebMVC.Models.AccountViewModels;
using Microsoft.eShopOnContainers.WebMVC.Services;
using System.Net.Http;
using Newtonsoft.Json.Linq;
using Microsoft.eShopOnContainers.WebMVC.Extensions;
using Microsoft.AspNetCore.Http.Authentication;
namespace Microsoft.eShopOnContainers.WebMVC.Controllers
{
@ -45,456 +36,9 @@ namespace Microsoft.eShopOnContainers.WebMVC.Controllers
return Redirect("/");
}
[Authorize]
public async Task<ActionResult> CallApi()
public IActionResult Signout()
{
var token = (User as ClaimsPrincipal).FindFirst("access_token").Value;
var client = new HttpClient();
client.SetBearerToken(token);
var result = await client.GetStringAsync("apiuri");
ViewBag.Json = JArray.Parse(result.ToString());
return View();
return new SignOutResult("oidc", new AuthenticationProperties { RedirectUri = "/" });
}
public async Task<ActionResult> Signout()
{
await Request.HttpContext.Authentication.SignOutAsync("oidc");
return Redirect("/");
}
public async Task SignoutCleanup(string sid)
{
var cp = (ClaimsPrincipal)User;
var sidClaim = cp.FindFirst("sid");
if (sidClaim != null && sidClaim.Value == sid)
{
await Request.HttpContext.Authentication.SignOutAsync("Cookies");
}
}
//private readonly UserManager<ApplicationUser> _userManager;
//private readonly SignInManager<ApplicationUser> _signInManager;
//private readonly ILogger _logger;
//public AccountController(
// UserManager<ApplicationUser> userManager,
// SignInManager<ApplicationUser> signInManager,
// ILoggerFactory loggerFactory)
//{
// _userManager = userManager;
// _signInManager = signInManager;
// _logger = loggerFactory.CreateLogger<AccountController>();
//}
////
//// GET: /Account/Login
//[HttpGet]
//[AllowAnonymous]
//public IActionResult Login(string returnUrl = null)
//{
// ViewData["ReturnUrl"] = returnUrl;
// return View();
//}
////
//// POST: /Account/Login
//[HttpPost]
//[AllowAnonymous]
//[ValidateAntiForgeryToken]
//public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null)
//{
// ViewData["ReturnUrl"] = returnUrl;
// if (ModelState.IsValid)
// {
// // This doesn't count login failures towards account lockout
// // To enable password failures to trigger account lockout, set lockoutOnFailure: true
// var result = await _signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, lockoutOnFailure: false);
// if (result.Succeeded)
// {
// _logger.LogInformation(1, "User logged in.");
// return RedirectToLocal(returnUrl);
// }
// if (result.RequiresTwoFactor)
// {
// return RedirectToAction(nameof(SendCode), new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
// }
// if (result.IsLockedOut)
// {
// _logger.LogWarning(2, "User account locked out.");
// return View("Lockout");
// }
// else
// {
// ModelState.AddModelError(string.Empty, "Invalid login attempt.");
// return View(model);
// }
// }
// // If we got this far, something failed, redisplay form
// return View(model);
//}
////
//// 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.Succeeded)
// {
// // For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=532713
// // Send an email with this link
// //var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
// //var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: HttpContext.Request.Scheme);
// //await _emailSender.SendEmailAsync(model.Email, "Confirm your account",
// // $"Please confirm your account by clicking this link: <a href='{callbackUrl}'>link</a>");
// await _signInManager.SignInAsync(user, isPersistent: false);
// _logger.LogInformation(3, "User created a new account with password.");
// return RedirectToLocal(returnUrl);
// }
// AddErrors(result);
// }
// // If we got this far, something failed, redisplay form
// return View(model);
//}
////
//// POST: /Account/LogOff
//[HttpPost]
//[ValidateAntiForgeryToken]
//public async Task<IActionResult> LogOff()
//{
// await _signInManager.SignOutAsync();
// _logger.LogInformation(4, "User logged out.");
// return RedirectToAction(nameof(CatalogController.Index), "Catalog");
//}
////
//// POST: /Account/ExternalLogin
//[HttpPost]
//[AllowAnonymous]
//[ValidateAntiForgeryToken]
//public IActionResult ExternalLogin(string provider, string returnUrl = null)
//{
// // Request a redirect to the external login provider.
// var redirectUrl = Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl });
// var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl);
// return Challenge(properties, provider);
//}
////
//// GET: /Account/ExternalLoginCallback
//[HttpGet]
//[AllowAnonymous]
//public async Task<IActionResult> ExternalLoginCallback(string returnUrl = null, string remoteError = null)
//{
// if (remoteError != null)
// {
// ModelState.AddModelError(string.Empty, $"Error from external provider: {remoteError}");
// return View(nameof(Login));
// }
// var info = await _signInManager.GetExternalLoginInfoAsync();
// if (info == null)
// {
// return RedirectToAction(nameof(Login));
// }
// // Sign in the user with this external login provider if the user already has a login.
// var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false);
// if (result.Succeeded)
// {
// _logger.LogInformation(5, "User logged in with {Name} provider.", info.LoginProvider);
// return RedirectToLocal(returnUrl);
// }
// if (result.RequiresTwoFactor)
// {
// return RedirectToAction(nameof(SendCode), new { ReturnUrl = returnUrl });
// }
// if (result.IsLockedOut)
// {
// return View("Lockout");
// }
// else
// {
// // If the user does not have an account, then ask the user to create an account.
// ViewData["ReturnUrl"] = returnUrl;
// ViewData["LoginProvider"] = info.LoginProvider;
// var email = info.Principal.FindFirstValue(ClaimTypes.Email);
// return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { Email = email });
// }
//}
////
//// POST: /Account/ExternalLoginConfirmation
//[HttpPost]
//[AllowAnonymous]
//[ValidateAntiForgeryToken]
//public async Task<IActionResult> ExternalLoginConfirmation(ExternalLoginConfirmationViewModel model, string returnUrl = null)
//{
// if (ModelState.IsValid)
// {
// // Get the information about the user from the external login provider
// var info = await _signInManager.GetExternalLoginInfoAsync();
// if (info == null)
// {
// return View("ExternalLoginFailure");
// }
// var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
// var result = await _userManager.CreateAsync(user);
// if (result.Succeeded)
// {
// result = await _userManager.AddLoginAsync(user, info);
// if (result.Succeeded)
// {
// await _signInManager.SignInAsync(user, isPersistent: false);
// _logger.LogInformation(6, "User created an account using {Name} provider.", info.LoginProvider);
// return RedirectToLocal(returnUrl);
// }
// }
// AddErrors(result);
// }
// ViewData["ReturnUrl"] = returnUrl;
// return View(model);
//}
//// GET: /Account/ConfirmEmail
//[HttpGet]
//[AllowAnonymous]
//public async Task<IActionResult> ConfirmEmail(string userId, string code)
//{
// if (userId == null || code == null)
// {
// return View("Error");
// }
// var user = await _userManager.FindByIdAsync(userId);
// if (user == null)
// {
// return View("Error");
// }
// var result = await _userManager.ConfirmEmailAsync(user, code);
// return View(result.Succeeded ? "ConfirmEmail" : "Error");
//}
////
//// GET: /Account/ForgotPassword
//[HttpGet]
//[AllowAnonymous]
//public IActionResult ForgotPassword()
//{
// return View();
//}
////
//// POST: /Account/ForgotPassword
//[HttpPost]
//[AllowAnonymous]
//[ValidateAntiForgeryToken]
//public async Task<IActionResult> ForgotPassword(ForgotPasswordViewModel model)
//{
// if (ModelState.IsValid)
// {
// var user = await _userManager.FindByNameAsync(model.Email);
// if (user == null || !(await _userManager.IsEmailConfirmedAsync(user)))
// {
// // Don't reveal that the user does not exist or is not confirmed
// return View("ForgotPasswordConfirmation");
// }
// // For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=532713
// // Send an email with this link
// //var code = await _userManager.GeneratePasswordResetTokenAsync(user);
// //var callbackUrl = Url.Action("ResetPassword", "Account", new { userId = user.Id, code = code }, protocol: HttpContext.Request.Scheme);
// //await _emailSender.SendEmailAsync(model.Email, "Reset Password",
// // $"Please reset your password by clicking here: <a href='{callbackUrl}'>link</a>");
// //return View("ForgotPasswordConfirmation");
// }
// // If we got this far, something failed, redisplay form
// return View(model);
//}
////
//// GET: /Account/ForgotPasswordConfirmation
//[HttpGet]
//[AllowAnonymous]
//public IActionResult ForgotPasswordConfirmation()
//{
// return View();
//}
////
//// GET: /Account/ResetPassword
//[HttpGet]
//[AllowAnonymous]
//public IActionResult ResetPassword(string code = null)
//{
// return code == null ? View("Error") : View();
//}
////
//// POST: /Account/ResetPassword
//[HttpPost]
//[AllowAnonymous]
//[ValidateAntiForgeryToken]
//public async Task<IActionResult> ResetPassword(ResetPasswordViewModel model)
//{
// if (!ModelState.IsValid)
// {
// return View(model);
// }
// var user = await _userManager.FindByNameAsync(model.Email);
// if (user == null)
// {
// // Don't reveal that the user does not exist
// return RedirectToAction(nameof(AccountController.ResetPasswordConfirmation), "Account");
// }
// var result = await _userManager.ResetPasswordAsync(user, model.Code, model.Password);
// if (result.Succeeded)
// {
// return RedirectToAction(nameof(AccountController.ResetPasswordConfirmation), "Account");
// }
// AddErrors(result);
// return View();
//}
////
//// GET: /Account/ResetPasswordConfirmation
//[HttpGet]
//[AllowAnonymous]
//public IActionResult ResetPasswordConfirmation()
//{
// return View();
//}
////
//// GET: /Account/SendCode
//[HttpGet]
//[AllowAnonymous]
//public async Task<ActionResult> SendCode(string returnUrl = null, bool rememberMe = false)
//{
// var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
// if (user == null)
// {
// return View("Error");
// }
// var userFactors = await _userManager.GetValidTwoFactorProvidersAsync(user);
// var factorOptions = userFactors.Select(purpose => new SelectListItem { Text = purpose, Value = purpose }).ToList();
// return View(new SendCodeViewModel { Providers = factorOptions, ReturnUrl = returnUrl, RememberMe = rememberMe });
//}
////
//// GET: /Account/VerifyCode
//[HttpGet]
//[AllowAnonymous]
//public async Task<IActionResult> VerifyCode(string provider, bool rememberMe, string returnUrl = null)
//{
// // Require that the user has already logged in via username/password or external login
// var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
// if (user == null)
// {
// return View("Error");
// }
// return View(new VerifyCodeViewModel { Provider = provider, ReturnUrl = returnUrl, RememberMe = rememberMe });
//}
////
//// POST: /Account/VerifyCode
//[HttpPost]
//[AllowAnonymous]
//[ValidateAntiForgeryToken]
//public async Task<IActionResult> VerifyCode(VerifyCodeViewModel model)
//{
// if (!ModelState.IsValid)
// {
// return View(model);
// }
// // The following code protects for brute force attacks against the two factor codes.
// // If a user enters incorrect codes for a specified amount of time then the user account
// // will be locked out for a specified amount of time.
// var result = await _signInManager.TwoFactorSignInAsync(model.Provider, model.Code, model.RememberMe, model.RememberBrowser);
// if (result.Succeeded)
// {
// return RedirectToLocal(model.ReturnUrl);
// }
// if (result.IsLockedOut)
// {
// _logger.LogWarning(7, "User account locked out.");
// return View("Lockout");
// }
// else
// {
// ModelState.AddModelError(string.Empty, "Invalid code.");
// return View(model);
// }
//}
//#region Helpers
//private void AddErrors(IdentityResult result)
//{
// foreach (var error in result.Errors)
// {
// ModelState.AddModelError(string.Empty, error.Description);
// }
//}
//private Task<ApplicationUser> GetCurrentUserAsync()
//{
// return _userManager.GetUserAsync(HttpContext.User);
//}
//private IActionResult RedirectToLocal(string returnUrl)
//{
// if (Url.IsLocalUrl(returnUrl))
// {
// return Redirect(returnUrl);
// }
// else
// {
// return RedirectToAction(nameof(CatalogController.Index), "Catalog");
// }
//}
//#endregion
}
}

View File

@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.eShopOnContainers.WebMVC.Services;
using Microsoft.eShopOnContainers.WebMVC.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authentication;
namespace Microsoft.eShopOnContainers.WebMVC.Controllers
{

View File

@ -7,6 +7,8 @@ using Microsoft.AspNetCore.Http;
using System.Net.Http;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using Microsoft.AspNetCore.Authentication;
using Microsoft.eShopOnContainers.WebMVC.Extensions;
namespace Microsoft.eShopOnContainers.WebMVC.Services
{
@ -15,16 +17,23 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services
private readonly IOptions<AppSettings> _settings;
private HttpClient _apiClient;
private readonly string _remoteServiceBaseUrl;
private IHttpContextAccessor _httpContextAccesor;
public BasketService(IOptions<AppSettings> settings)
public BasketService(IOptions<AppSettings> settings, IHttpContextAccessor httpContextAccesor)
{
_settings = settings;
_remoteServiceBaseUrl = _settings.Value.BasketUrl;
_httpContextAccesor = httpContextAccesor;
}
public async Task<Basket> GetBasket(ApplicationUser user)
{
var context = _httpContextAccesor.HttpContext;
var token = await context.Authentication.GetTokenAsync("access_token");
_apiClient = new HttpClient();
_apiClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
var basketUrl = $"{_remoteServiceBaseUrl}/{user.Id.ToString()}";
var dataString = await _apiClient.GetStringAsync(basketUrl);
var response = JsonConvert.DeserializeObject<Basket>(dataString);
@ -41,7 +50,12 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services
public async Task<Basket> UpdateBasket(Basket basket)
{
var context = _httpContextAccesor.HttpContext;
var token = await context.Authentication.GetTokenAsync("access_token");
_apiClient = new HttpClient();
_apiClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
var basketUrl = _remoteServiceBaseUrl;
StringContent content = new StringContent(JsonConvert.SerializeObject(basket), System.Text.Encoding.UTF8, "application/json");
var response = await _apiClient.PostAsync(basketUrl, content);
@ -106,7 +120,11 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services
public async Task CleanBasket(ApplicationUser user)
{
var context = _httpContextAccesor.HttpContext;
var token = await context.Authentication.GetTokenAsync("access_token");
_apiClient = new HttpClient();
_apiClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
var basketUrl = $"{_remoteServiceBaseUrl}/{user.Id.ToString()}";
var response = await _apiClient.DeleteAsync(basketUrl);

View File

@ -11,6 +11,7 @@ using Microsoft.eShopOnContainers.WebMVC.Models;
using Microsoft.eShopOnContainers.WebMVC.Services;
using System.IdentityModel.Tokens.Jwt;
using Microsoft.IdentityModel.Tokens;
using Microsoft.AspNetCore.Http;
namespace Microsoft.eShopOnContainers.WebMVC
{
@ -38,17 +39,11 @@ namespace Microsoft.eShopOnContainers.WebMVC
// 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();
// Add application services.
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddTransient<ICatalogService, CatalogService>();
services.AddSingleton<IOrderingService, OrderingService>(); //CCE: Once services are integrated, a singleton is not needed we can left transient.
services.AddTransient<IBasketService, BasketService>();
@ -83,8 +78,6 @@ namespace Microsoft.eShopOnContainers.WebMVC
AutomaticAuthenticate = true,
});
//app.UseIdentity();
var oidcOptions = new OpenIdConnectOptions
{
AuthenticationScheme = "oidc",
@ -96,27 +89,16 @@ namespace Microsoft.eShopOnContainers.WebMVC
ResponseType = "code id_token",
SaveTokens = true,
GetClaimsFromUserInfoEndpoint = true,
RequireHttpsMetadata = false,
//TokenValidationParameters = new TokenValidationParameters
//{
// NameClaimType = "name",
// RoleClaimType = "role"
//}
RequireHttpsMetadata = false,
};
oidcOptions.Scope.Clear();
oidcOptions.Scope.Add("openid");
oidcOptions.Scope.Add("profile");
oidcOptions.Scope.Add("orders");
oidcOptions.Scope.Add("basket");
app.UseOpenIdConnectAuthentication(oidcOptions);
app.UseJwtBearerAuthentication(new JwtBearerOptions
{
Authority = "http://localhost:5000/", // base address of your OIDC server.
Audience = "http://localhost:5000/", // base address of your API.
RequireHttpsMetadata = false
});
app.UseMvc(routes =>
{

View File

@ -31,6 +31,7 @@ namespace Microsoft.eShopOnContainers.WebMVC.ViewComponents
{
var basket = await _cartSvc.GetBasket(user);
return basket.Items.Count;
return await Task<int>.Run(()=> { return 0; });
}
}
}

View File

@ -6,7 +6,7 @@
@if(Context.User.Identity.IsAuthenticated)
{
<form asp-area="" asp-controller="Account" asp-action="LogOff" method="post" id="logoutForm" class="navbar-right">
<form asp-area="" asp-controller="Account" asp-action="SignOut" method="post" id="logoutForm" class="navbar-right">
<div class="nav navbar-nav navbar-right mt-15">
@await Component.InvokeAsync("Cart", new { user = UserManager.Parse(User) })
<div class="fr login-user">

View File

@ -4,9 +4,9 @@
//"DefaultConnection": "Server=identity.data;Database=aspnet-Microsoft.eShopOnContainers.WebMVC;User Id=sa;Password=Pass@word"
},
"CatalogUrl": "http://localhost:5101",
"OrderingUrl": "http://localhost:2446",
"OrderingUrl": "http://localhost:5102",
"BasketUrl": "http://localhost:5103",
"IdentityUrl": "http://localhost:5000",
"IdentityUrl": "http://localhost:5105",
"Logging": {
"IncludeScopes": false,
"LogLevel": {

View File

@ -18,6 +18,7 @@ namespace eShopConContainers.WebSPA
.UseConfiguration(config)
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseUrls("http://localhost:1250/")
.UseStartup<Startup>()
.Build();