// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. using IdentityServer4.Services; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using System.Linq; using System.Threading.Tasks; using IdentityServer4.Models; using IdentityServer4.Stores; using IdentityServer4.Quickstart.UI.Models; using Identity.API.Models.AccountViewModels; using Identity.API.Services; namespace IdentityServer4.Quickstart.UI.Controllers { /// /// This controller implements the consent logic /// public class ConsentController : Controller { private readonly ILogger _logger; private readonly IClientStore _clientStore; private readonly IResourceStore _resourceStore; private readonly IIdentityServerInteractionService _interaction; public ConsentController( ILogger logger, IIdentityServerInteractionService interaction, IClientStore clientStore, IResourceStore resourceStore) { _logger = logger; _interaction = interaction; _clientStore = clientStore; _resourceStore = resourceStore; } /// /// Shows the consent screen /// /// /// [HttpGet] public async Task Index(string returnUrl) { var vm = await BuildViewModelAsync(returnUrl); ViewData["ReturnUrl"] = returnUrl; if (vm != null) { return View("Index", vm); } return View("Error"); } /// /// Handles the consent screen postback /// [HttpPost] [ValidateAntiForgeryToken] public async Task Index(ConsentInputModel model) { // parse the return URL back to an AuthorizeRequest object var request = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl); ConsentResponse response = null; // user clicked 'no' - send back the standard 'access_denied' response if (model.Button == "no") { response = ConsentResponse.Denied; } // user clicked 'yes' - validate the data else if (model.Button == "yes" && model != null) { // if the user consented to some scope, build the response model if (model.ScopesConsented != null && model.ScopesConsented.Any()) { response = new ConsentResponse { RememberConsent = model.RememberConsent, ScopesConsented = model.ScopesConsented }; } else { ModelState.AddModelError("", "You must pick at least one permission."); } } else { ModelState.AddModelError("", "Invalid Selection"); } if (response != null) { // communicate outcome of consent back to identityserver await _interaction.GrantConsentAsync(request, response); // redirect back to authorization endpoint return Redirect(model.ReturnUrl); } var vm = await BuildViewModelAsync(model.ReturnUrl, model); if (vm != null) { return View("Index", vm); } return View("Error"); } async Task BuildViewModelAsync(string returnUrl, ConsentInputModel model = null) { var request = await _interaction.GetAuthorizationContextAsync(returnUrl); if (request != null) { var client = await _clientStore.FindEnabledClientByIdAsync(request.ClientId); if (client != null) { var resources = await _resourceStore.FindEnabledResourcesByScopeAsync(request.ScopesRequested); if (resources != null && (resources.IdentityResources.Any() || resources.ApiResources.Any())) { return new ConsentViewModel(model, returnUrl, request, client, resources); } else { _logger.LogError("No scopes matching: {0}", request.ScopesRequested.Aggregate((x, y) => x + ", " + y)); } } else { _logger.LogError("Invalid client id: {0}", request.ClientId); } } else { _logger.LogError("No consent request matching request: {0}", returnUrl); } return null; } } }