Browse Source

Refactored login page.

pull/217/head
Halil İbrahim Kalkan 9 years ago
parent
commit
4d27fbffd8
  1. 17
      src/MicroserviceDemo/MicroserviceDemo.AuthServer/MicroservicesAuthServerModule.cs
  2. 8
      src/Volo.Abp.Account.Web.IdentityServer/AbpAccountWebIdentityServerModule.cs
  3. 61
      src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/Login.cshtml
  4. 228
      src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/Login.cshtml.cs
  5. 2
      src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/_ViewImports.cshtml
  6. 6
      src/Volo.Abp.Account.Web.IdentityServer/Volo.Abp.Account.Web.IdentityServer.csproj
  7. 13
      src/Volo.Abp.Account.Web/AbpAccountOptions.cs
  8. 4
      src/Volo.Abp.Account.Web/AbpAccountWebModule.cs
  9. 2
      src/Volo.Abp.Account.Web/Pages/Account/_ViewImports.cshtml
  10. 8
      src/Volo.Abp.Account.Web/Volo.Abp.Account.Web.csproj

17
src/MicroserviceDemo/MicroserviceDemo.AuthServer/MicroservicesAuthServerModule.cs

@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Builder; using System.IO;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
@ -6,12 +7,15 @@ using Microsoft.Extensions.DependencyInjection;
using Volo.Abp; using Volo.Abp;
using Volo.Abp.Account.Web; using Volo.Abp.Account.Web;
using Volo.Abp.AspNetCore.Modularity; using Volo.Abp.AspNetCore.Modularity;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap;
using Volo.Abp.Autofac; using Volo.Abp.Autofac;
using Volo.Abp.Data; using Volo.Abp.Data;
using Volo.Abp.EntityFrameworkCore; using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.Identity.EntityFrameworkCore; using Volo.Abp.Identity.EntityFrameworkCore;
using Volo.Abp.IdentityServer.EntityFrameworkCore; using Volo.Abp.IdentityServer.EntityFrameworkCore;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
using Volo.Abp.VirtualFileSystem;
namespace MicroserviceDemo.AuthServer namespace MicroserviceDemo.AuthServer
{ {
@ -43,6 +47,17 @@ namespace MicroserviceDemo.AuthServer
}); });
}); });
if (hostingEnvironment.IsDevelopment())
{
services.Configure<VirtualFileSystemOptions>(options =>
{
options.FileSets.ReplaceEmbeddedByPyhsical<AbpAspNetCoreMvcUiModule>(Path.Combine(hostingEnvironment.ContentRootPath, "..\\..\\Volo.Abp.AspNetCore.Mvc.UI"));
options.FileSets.ReplaceEmbeddedByPyhsical<AbpAspNetCoreMvcUiBootstrapModule>(Path.Combine(hostingEnvironment.ContentRootPath, "..\\..\\Volo.Abp.AspNetCore.Mvc.UI.Bootstrap"));
options.FileSets.ReplaceEmbeddedByPyhsical<AbpAccountWebModule>(Path.Combine(hostingEnvironment.ContentRootPath, "..\\..\\Volo.Abp.Account.Web"));
options.FileSets.ReplaceEmbeddedByPyhsical<AbpAccountWebIdentityServerModule>(Path.Combine(hostingEnvironment.ContentRootPath, "..\\..\\Volo.Abp.Account.Web.IdentityServer"));
});
}
services.AddAssemblyOf<MicroservicesAuthServerModule>(); services.AddAssemblyOf<MicroservicesAuthServerModule>();
} }

8
src/Volo.Abp.Account.Web.IdentityServer/AbpAccountWebIdentityServerModule.cs

@ -1,20 +1,22 @@
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.IdentityServer;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
using Volo.Abp.VirtualFileSystem; using Volo.Abp.VirtualFileSystem;
namespace Volo.Abp.Account.Web namespace Volo.Abp.Account.Web
{ {
[DependsOn(typeof(AbpAccountWebModule))] [DependsOn(typeof(AbpAccountWebModule))]
[DependsOn(typeof(AbpIdentityServerDomainModule))]
public class AbpAccountWebIdentityServerModule : AbpModule public class AbpAccountWebIdentityServerModule : AbpModule
{ {
public override void ConfigureServices(IServiceCollection services) public override void ConfigureServices(IServiceCollection services)
{ {
services.AddAssemblyOf<AbpAccountWebModule>();
services.Configure<VirtualFileSystemOptions>(options => services.Configure<VirtualFileSystemOptions>(options =>
{ {
options.FileSets.AddEmbedded<AbpAccountWebModule>("Volo.Abp.Account.Web"); options.FileSets.AddEmbedded<AbpAccountWebIdentityServerModule>("Volo.Abp.Account.Web");
}); });
services.AddAssemblyOf<AbpAccountWebIdentityServerModule>();
} }
} }
} }

61
src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/Login.cshtml

@ -0,0 +1,61 @@
@page
@model Volo.Abp.Account.Web.Pages.Account.IdsLoginModel
<div class="row">
@if (Model.EnableLocalLogin)
{
<div class="col-md-6">
<div class="col-md-4">
<form method="post" asp-page="/Account/Login">
<input asp-for="ReturnUrl" />
<input asp-for="ReturnUrlHash" />
<div class="form-group">
<label asp-for="LoginInput.UserNameOrEmailAddress"></label>
<input asp-for="LoginInput.UserNameOrEmailAddress" class="form-control" />
<span asp-validation-for="LoginInput.UserNameOrEmailAddress" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="LoginInput.Password"></label>
<input asp-for="LoginInput.Password" class="form-control" />
<span asp-validation-for="LoginInput.Password" class="text-danger"></span>
</div>
<div class="form-check">
<label asp-for="LoginInput.RememberMe" class="form-check-label">
<input asp-for="LoginInput.RememberMe" class="form-check-input" />
@Html.DisplayNameFor(m => m.LoginInput.RememberMe)
</label>
</div>
<button type="submit" class="btn btn-primary" name="Action" value="Login">Login</button>
<button type="button" class="btn btn-secondary" name="Action" value="Cancel">Cancel</button>
</form>
<div style="padding-top: 20px">
<a href="@Url.Page("./Register", new {returnUrl = Model.ReturnUrl, returnUrlHash = Model.ReturnUrlHash})">Register</a>
</div>
</div>
</div>
}
@if (Model.VisibleExternalProviders.Any())
{
<div class="col-md-6">
<h4>Use another service to log in.</h4>
<form asp-page="./Login" asp-page-handler="ExternalLogin" asp-route-returnUrl="@Model.ReturnUrl" asp-route-returnUrlHash="@Model.ReturnUrlHash" method="post">
<div>
@foreach (var provider in Model.VisibleExternalProviders)
{
<button type="submit" class="btn btn-primary" name="provider" value="@provider.AuthenticationScheme" title="Log in using your @provider.DisplayName account">@provider.DisplayName</button>
}
</div>
</form>
</div>
}
@if (!Model.EnableLocalLogin && !Model.VisibleExternalProviders.Any())
{
<div class="alert alert-warning">
<strong>Invalid login request</strong>
There are no login schemes configured for this client.
</div>
}
</div>

228
src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/Login.cshtml.cs

@ -0,0 +1,228 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using IdentityServer4.Services;
using IdentityServer4.Stores;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Volo.Abp.Identity;
using Volo.Abp.Ui;
using Volo.Abp.Uow;
namespace Volo.Abp.Account.Web.Pages.Account
{
public class IdsLoginModel : AccountModelBase
{
[HiddenInput]
[BindProperty(SupportsGet = true)]
public string ReturnUrl { get; set; }
[HiddenInput]
[BindProperty(SupportsGet = true)]
public string ReturnUrlHash { get; set; }
[BindProperty]
public LoginInputModel LoginInput { get; set; }
public bool EnableLocalLogin { get; set; }
//TODO: Why there is an ExternalProviders if only the VisibleExternalProviders is used.
public IEnumerable<ExternalProviderModel> ExternalProviders { get; set; }
public IEnumerable<ExternalProviderModel> VisibleExternalProviders => ExternalProviders.Where(x => !String.IsNullOrWhiteSpace(x.DisplayName));
public bool IsExternalLoginOnly => EnableLocalLogin == false && ExternalProviders?.Count() == 1;
public string ExternalLoginScheme => IsExternalLoginOnly ? ExternalProviders?.SingleOrDefault()?.AuthenticationScheme : null;
private readonly SignInManager<IdentityUser> _signInManager;
private readonly IdentityUserManager _userManager;
private readonly IIdentityServerInteractionService _interaction;
private readonly IAuthenticationSchemeProvider _schemeProvider;
private readonly AbpAccountOptions _accountOptions;
private readonly IClientStore _clientStore;
public IdsLoginModel(
SignInManager<IdentityUser> signInManager,
IdentityUserManager userManager,
IIdentityServerInteractionService interaction,
IAuthenticationSchemeProvider schemeProvider,
IOptions<AbpAccountOptions> accountOptions,
IClientStore clientStore)
{
_signInManager = signInManager;
_userManager = userManager;
_interaction = interaction;
_schemeProvider = schemeProvider;
_clientStore = clientStore;
_accountOptions = accountOptions.Value;
}
public async Task OnGetAsync()
{
LoginInput = new LoginInputModel();
var context = await _interaction.GetAuthorizationContextAsync(ReturnUrl);
LoginInput.UserNameOrEmailAddress = context?.LoginHint;
if (context?.IdP != null)
{
LoginInput.UserNameOrEmailAddress = context.LoginHint;
ExternalProviders = new[] {new ExternalProviderModel {AuthenticationScheme = context.IdP}};
return;
}
var schemes = await _schemeProvider.GetAllSchemesAsync();
var providers = schemes
.Where(x => x.DisplayName != null || x.Name.Equals(_accountOptions.WindowsAuthenticationSchemeName, StringComparison.OrdinalIgnoreCase))
.Select(x => new ExternalProviderModel
{
DisplayName = x.DisplayName,
AuthenticationScheme = x.Name
})
.ToList();
EnableLocalLogin = true; //TODO: We can get default from a setting?
if (context?.ClientId != null)
{
var client = await _clientStore.FindEnabledClientByIdAsync(context.ClientId);
if (client != null)
{
EnableLocalLogin = client.EnableLocalLogin;
if (client.IdentityProviderRestrictions != null && client.IdentityProviderRestrictions.Any())
{
providers = providers.Where(provider => client.IdentityProviderRestrictions.Contains(provider.AuthenticationScheme)).ToList();
}
}
}
ExternalProviders = providers.ToArray();
if (IsExternalLoginOnly)
{
//return await ExternalLogin(vm.ExternalLoginScheme, returnUrl);
throw new NotImplementedException();
}
}
[UnitOfWork] //TODO: Will be removed when we implement action filter
public virtual async Task<IActionResult> OnPostAsync()
{
ValidateModel();
var result = await _signInManager.PasswordSignInAsync(
LoginInput.UserNameOrEmailAddress,
LoginInput.Password,
LoginInput.RememberMe,
true
);
if (!result.Succeeded)
{
throw new UserFriendlyException("Login failed!"); //TODO: Handle other cases, do not throw exception
}
return RedirectSafely(ReturnUrl, ReturnUrlHash);
}
[UnitOfWork]
public virtual IActionResult OnPostExternalLogin(string provider, string returnUrl = "", string returnUrlHash = "")
{
var redirectUrl = Url.Page("./Login", pageHandler: "ExternalLoginCallback", values: new { returnUrl, returnUrlHash });
var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl);
return new ChallengeResult(provider, properties);
}
[UnitOfWork]
public virtual async Task<IActionResult> OnGetExternalLoginCallbackAsync(string returnUrl = "", string returnUrlHash = "", string remoteError = null)
{
if (remoteError != null)
{
Logger.LogWarning($"External login callback error: {remoteError}");
return RedirectToPage("./Login");
}
var loginInfo = await _signInManager.GetExternalLoginInfoAsync();
if (loginInfo == null)
{
Logger.LogWarning("External login info is not available");
return RedirectToPage("./Login");
}
var result = await _signInManager.ExternalLoginSignInAsync(
loginInfo.LoginProvider,
loginInfo.ProviderKey,
isPersistent: false,
bypassTwoFactor: true
);
if (result.IsLockedOut)
{
throw new UserFriendlyException("Cannot proceed because user is locked out!");
}
//TODO: Handle other cases
if (result.Succeeded)
{
return RedirectSafely(returnUrl, returnUrlHash);
}
// Get the information about the user from the external login provider
var info = await _signInManager.GetExternalLoginInfoAsync();
if (info == null)
{
throw new ApplicationException("Error loading external login information during confirmation.");
}
var user = await CreateExternalUserAsync(info);
await _signInManager.SignInAsync(user, false);
return RedirectSafely(returnUrl, returnUrlHash);
}
private async Task<IdentityUser> CreateExternalUserAsync(ExternalLoginInfo info)
{
var emailAddress = info.Principal.FindFirstValue(ClaimTypes.Email);
var user = new IdentityUser(GuidGenerator.Create(), emailAddress, CurrentTenant.Id);
CheckIdentityErrors(await _userManager.CreateAsync(user));
CheckIdentityErrors(await _userManager.SetEmailAsync(user, emailAddress));
CheckIdentityErrors(await _userManager.AddLoginAsync(user, info));
return user;
}
public class LoginInputModel
{
[Required]
[StringLength(255)]
[Display(Name = "UserNameOrEmailAddress")]
public string UserNameOrEmailAddress { get; set; }
[Required]
[StringLength(32)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[Display(Name = "RememberMe")]
public bool RememberMe { get; set; }
}
public class ExternalProviderModel
{
public string DisplayName { get; set; }
public string AuthenticationScheme { get; set; }
}
}
}

2
src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/_ViewImports.cshtml

@ -0,0 +1,2 @@
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bootstrap

6
src/Volo.Abp.Account.Web.IdentityServer/Volo.Abp.Account.Web.IdentityServer.csproj

@ -19,13 +19,17 @@
<EmbeddedResource Include="Pages\**\*.*" Exclude="*.cs" /> <EmbeddedResource Include="Pages\**\*.*" Exclude="*.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<EmbeddedResource Remove="Pages\Account\_ViewImports.cshtml" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<Folder Include="Areas\Account\Controllers\" /> <Folder Include="Areas\Account\Controllers\" />
<Folder Include="Pages\" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Volo.Abp.Account.Web\Volo.Abp.Account.Web.csproj" /> <ProjectReference Include="..\Volo.Abp.Account.Web\Volo.Abp.Account.Web.csproj" />
<ProjectReference Include="..\Volo.Abp.IdentityServer.Domain\Volo.Abp.IdentityServer.Domain.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

13
src/Volo.Abp.Account.Web/AbpAccountOptions.cs

@ -0,0 +1,13 @@
namespace Volo.Abp.Account.Web
{
public class AbpAccountOptions
{
public string WindowsAuthenticationSchemeName { get; set; }
public AbpAccountOptions()
{
//TODO: This makes us depend on the Microsoft.AspNetCore.Server.IISIntegration package.
WindowsAuthenticationSchemeName = Microsoft.AspNetCore.Server.IISIntegration.IISDefaults.AuthenticationScheme;
}
}
}

4
src/Volo.Abp.Account.Web/AbpAccountWebModule.cs

@ -12,12 +12,12 @@ namespace Volo.Abp.Account.Web
{ {
public override void ConfigureServices(IServiceCollection services) public override void ConfigureServices(IServiceCollection services)
{ {
services.AddAssemblyOf<AbpAccountWebModule>();
services.Configure<VirtualFileSystemOptions>(options => services.Configure<VirtualFileSystemOptions>(options =>
{ {
options.FileSets.AddEmbedded<AbpAccountWebModule>("Volo.Abp.Account.Web"); options.FileSets.AddEmbedded<AbpAccountWebModule>("Volo.Abp.Account.Web");
}); });
services.AddAssemblyOf<AbpAccountWebModule>();
} }
} }
} }

2
src/Volo.Abp.Account.Web/Pages/Account/_ViewImports.cshtml

@ -0,0 +1,2 @@
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bootstrap

8
src/Volo.Abp.Account.Web/Volo.Abp.Account.Web.csproj

@ -19,6 +19,14 @@
<EmbeddedResource Include="Pages\**\*.*" Exclude="*.cs" /> <EmbeddedResource Include="Pages\**\*.*" Exclude="*.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<EmbeddedResource Remove="Pages\Account\_ViewImports.cshtml" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Server.IISIntegration" Version="2.0.1" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Volo.Abp.AspNetCore.Mvc.UI.Bootstrap\Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.csproj" /> <ProjectReference Include="..\Volo.Abp.AspNetCore.Mvc.UI.Bootstrap\Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.csproj" />
<ProjectReference Include="..\Volo.Abp.Identity.Domain\Volo.Abp.Identity.Domain.csproj" /> <ProjectReference Include="..\Volo.Abp.Identity.Domain\Volo.Abp.Identity.Domain.csproj" />

Loading…
Cancel
Save