Browse Source

Add consent screen to account module

pull/848/head
Halil ibrahim Kalkan 7 years ago
parent
commit
bb3ef544ea
  1. 9
      modules/account/src/Volo.Abp.Account.Web.IdentityServer/AbpAccountWebIdentityServerModule.cs
  2. 113
      modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Consent.cshtml
  3. 240
      modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Consent.cshtml.cs
  4. 4
      modules/account/src/Volo.Abp.Account.Web.IdentityServer/Volo.Abp.Account.Web.IdentityServer.csproj

9
modules/account/src/Volo.Abp.Account.Web.IdentityServer/AbpAccountWebIdentityServerModule.cs

@ -1,5 +1,6 @@
using Volo.Abp.IdentityServer;
using Volo.Abp.Modularity;
using Volo.Abp.VirtualFileSystem;
namespace Volo.Abp.Account.Web
{
@ -9,6 +10,12 @@ namespace Volo.Abp.Account.Web
)]
public class AbpAccountWebIdentityServerModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<VirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<AbpAccountWebIdentityServerModule>("Volo.Abp.Account.Web");
});
}
}
}

113
modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Consent.cshtml

@ -0,0 +1,113 @@
@page
@using Volo.Abp.Account.Web.Pages
@using Volo.Abp.Account.Web.Pages.Account
@model ConsentModel
<abp-card id="IdentityServerConsentWrapper">
<abp-card-header>
<div class="row">
<div class="col-md-12">
<h2>
@if (Model.ClientInfo.ClientLogoUrl != null)
{
<img src="@Model.ClientInfo.ClientLogoUrl">
}
@Model.ClientInfo.ClientName
<small>is requesting your permission</small>
</h2>
</div>
</div>
</abp-card-header>
<abp-card-body>
<form method="post" asp-page="/Consent">
<input type="hidden" asp-for="ReturnUrl" />
<input type="hidden" asp-for="ReturnUrlHash" />
<div>Uncheck the permissions you do not wish to grant.</div>
@if (Model.ConsentInput.IdentityScopes.Any())
{
<h3>Personal Information</h3>
<ul class="list-group">
@for (var i = 0; i < Model.ConsentInput.IdentityScopes.Count; i++)
{
<li class="list-group-item">
<div class="form-check">
<label asp-for="@Model.ConsentInput.IdentityScopes[i].Checked" class="form-check-label">
<input asp-for="@Model.ConsentInput.IdentityScopes[i].Checked" class="form-check-input" />
@Model.ConsentInput.IdentityScopes[i].DisplayName
@if (Model.ConsentInput.IdentityScopes[i].Required)
{
<span><em>(required)</em></span>
}
</label>
</div>
<input asp-for="@Model.ConsentInput.IdentityScopes[i].Name" type="hidden" /> @* TODO: Use attributes on the view model instead of using hidden here *@
@if (Model.ConsentInput.IdentityScopes[i].Description != null)
{
<div class="consent-description">
@Model.ConsentInput.IdentityScopes[i].Description
</div>
}
</li>
}
</ul>
}
@if (Model.ConsentInput.ApiScopes.Any())
{
<h3>Application Access</h3>
<ul class="list-group">
@for (var i = 0; i < Model.ConsentInput.ApiScopes.Count; i++)
{
<li class="list-group-item">
<div class="form-check">
<label asp-for="@Model.ConsentInput.ApiScopes[i].Checked" class="form-check-label">
<input asp-for="@Model.ConsentInput.ApiScopes[i].Checked" class="form-check-input" disabled="@Model.ConsentInput.ApiScopes[i].Required" />
@Model.ConsentInput.ApiScopes[i].DisplayName
@if (Model.ConsentInput.ApiScopes[i].Required)
{
<span><em>(required)</em></span>
}
</label>
</div>
<input asp-for="@Model.ConsentInput.ApiScopes[i].Name" type="hidden" /> @* TODO: Use attributes on the view model instead of using hidden here *@
@if (Model.ConsentInput.ApiScopes[i].Description != null)
{
<div class="consent-description">
@Model.ConsentInput.ApiScopes[i].Description
</div>
}
</li>
}
</ul>
}
@if (Model.ClientInfo.AllowRememberConsent)
{
<div class="form-check">
<label asp-for="@Model.ConsentInput.RememberConsent" class="form-check-label">
<input asp-for="@Model.ConsentInput.RememberConsent" class="form-check-input" />
<strong>Remember My Decision</strong>
</label>
</div>
}
<div>
<button name="UserDecision" value="yes" class="btn btn-primary" autofocus>Yes, Allow</button>
<button name="UserDecision" value="no" class="btn">No, Do Not Allow</button>
@if (Model.ClientInfo.ClientUrl != null)
{
<a class="pull-right btn btn-secondary" target="_blank" href="@Model.ClientInfo.ClientUrl">
<strong>@Model.ClientInfo.ClientName</strong>
</a>
}
</div>
<div asp-validation-summary="All" class="text-danger"></div>
</form>
</abp-card-body>
</abp-card>

240
modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Consent.cshtml.cs

@ -0,0 +1,240 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
using IdentityServer4.Models;
using IdentityServer4.Services;
using IdentityServer4.Stores;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.RazorPages;
using Volo.Abp.UI;
namespace Volo.Abp.Account.Web.Pages
{
//TODO: Move this into the Account folder!!!
public class ConsentModel : AbpPageModel
{
[HiddenInput]
[BindProperty(SupportsGet = true)]
public string ReturnUrl { get; set; }
[HiddenInput]
[BindProperty(SupportsGet = true)]
public string ReturnUrlHash { get; set; }
[BindProperty]
public ConsentModel.ConsentInputModel ConsentInput { get; set; }
public ClientInfoModel ClientInfo { get; set; }
private readonly IIdentityServerInteractionService _interaction;
private readonly IClientStore _clientStore;
private readonly IResourceStore _resourceStore;
public ConsentModel(
IIdentityServerInteractionService interaction,
IClientStore clientStore,
IResourceStore resourceStore)
{
_interaction = interaction;
_clientStore = clientStore;
_resourceStore = resourceStore;
}
public virtual async Task OnGet()
{
var request = await _interaction.GetAuthorizationContextAsync(ReturnUrl);
if (request == null)
{
throw new ApplicationException($"No consent request matching request: {ReturnUrl}");
}
var client = await _clientStore.FindEnabledClientByIdAsync(request.ClientId);
if (client == null)
{
throw new ApplicationException($"Invalid client id: {request.ClientId}");
}
var resources = await _resourceStore.FindEnabledResourcesByScopeAsync(request.ScopesRequested);
if (resources == null || (!resources.IdentityResources.Any() && !resources.ApiResources.Any()))
{
throw new ApplicationException($"No scopes matching: {request.ScopesRequested.Aggregate((x, y) => x + ", " + y)}");
}
ClientInfo = new ClientInfoModel(client);
ConsentInput = new ConsentInputModel
{
RememberConsent = true,
IdentityScopes = resources.IdentityResources.Select(x => CreateScopeViewModel(x, true)).ToList(),
ApiScopes = resources.ApiResources.SelectMany(x => x.Scopes).Select(x => CreateScopeViewModel(x, true)).ToList()
};
if (resources.OfflineAccess)
{
ConsentInput.ApiScopes.Add(GetOfflineAccessScope(true));
}
}
public virtual async Task<IActionResult> OnPost(string userDecision)
{
var result = await ProcessConsentAsync();
if (result.IsRedirect)
{
return Redirect(result.RedirectUri);
}
if (result.HasValidationError)
{
//ModelState.AddModelError("", result.ValidationError);
throw new ApplicationException("Error: " + result.ValidationError);
}
throw new ApplicationException("Unknown Error!");
}
protected virtual async Task<ConsentModel.ProcessConsentResult> ProcessConsentAsync()
{
var result = new ConsentModel.ProcessConsentResult();
ConsentResponse grantedConsent;
if (ConsentInput.UserDecision == "no")
{
grantedConsent = ConsentResponse.Denied;
}
else
{
if (ConsentInput.IdentityScopes.Any() || ConsentInput.ApiScopes.Any())
{
grantedConsent = new ConsentResponse
{
RememberConsent = ConsentInput.RememberConsent,
ScopesConsented = ConsentInput.GetAllowedScopeNames()
};
}
else
{
throw new UserFriendlyException("You must pick at least one permission"); //TODO: How to handle this
}
}
if (grantedConsent != null)
{
var request = await _interaction.GetAuthorizationContextAsync(ReturnUrl);
if (request == null)
{
return result;
}
await _interaction.GrantConsentAsync(request, grantedConsent);
result.RedirectUri = ReturnUrl; //TODO: ReturnUrlHash?
}
return result;
}
protected virtual ConsentModel.ScopeViewModel CreateScopeViewModel(IdentityResource identity, bool check)
{
return new ConsentModel.ScopeViewModel
{
Name = identity.Name,
DisplayName = identity.DisplayName,
Description = identity.Description,
Emphasize = identity.Emphasize,
Required = identity.Required,
Checked = check || identity.Required
};
}
protected virtual ConsentModel.ScopeViewModel CreateScopeViewModel(Scope scope, bool check)
{
return new ConsentModel.ScopeViewModel
{
Name = scope.Name,
DisplayName = scope.DisplayName,
Description = scope.Description,
Emphasize = scope.Emphasize,
Required = scope.Required,
Checked = check || scope.Required
};
}
protected virtual ConsentModel.ScopeViewModel GetOfflineAccessScope(bool check)
{
return new ConsentModel.ScopeViewModel
{
Name = IdentityServer4.IdentityServerConstants.StandardScopes.OfflineAccess,
DisplayName = "Offline Access", //TODO: Localize
Description = "Access to your applications and resources, even when you are offline",
Emphasize = true,
Checked = check
};
}
public class ConsentInputModel
{
public List<ConsentModel.ScopeViewModel> IdentityScopes { get; set; }
public List<ConsentModel.ScopeViewModel> ApiScopes { get; set; }
[Required]
public string UserDecision { get; set; }
public bool RememberConsent { get; set; }
public List<string> GetAllowedScopeNames()
{
return IdentityScopes.Union(ApiScopes).Where(s => s.Checked).Select(s => s.Name).ToList();
}
}
public class ScopeViewModel
{
[Required]
[HiddenInput]
public string Name { get; set; }
public bool Checked { get; set; }
public string DisplayName { get; set; }
public string Description { get; set; }
public bool Emphasize { get; set; }
public bool Required { get; set; }
}
public class ProcessConsentResult
{
public bool IsRedirect => RedirectUri != null;
public string RedirectUri { get; set; }
public bool HasValidationError => ValidationError != null;
public string ValidationError { get; set; }
}
public class ClientInfoModel
{
public string ClientName { get; set; }
public string ClientUrl { get; set; }
public string ClientLogoUrl { get; set; }
public bool AllowRememberConsent { get; set; }
public ClientInfoModel(Client client)
{
//TODO: Automap
ClientName = client.ClientId;
ClientUrl = client.ClientUri;
ClientLogoUrl = client.LogoUri;
AllowRememberConsent = client.AllowRememberConsent;
}
}
}
}

4
modules/account/src/Volo.Abp.Account.Web.IdentityServer/Volo.Abp.Account.Web.IdentityServer.csproj

@ -20,8 +20,10 @@
</ItemGroup>
<ItemGroup>
<Content Remove="Pages\**\*.cshtml" />
<Content Remove="Pages\**\*.css" />
<Content Remove="Pages\**\*.js" />
<Content Remove="Properties\launchSettings.json" />
<EmbeddedResource Remove="Pages\Account\IdentityServerSupportedLoginModel.cs" />
<None Include="Properties\launchSettings.json" />
</ItemGroup>

Loading…
Cancel
Save