source: src/FinkiChattery/FinkiChattery.Identity/Quickstart/Consent/ConsentController.cs@ e6a6d9a

dev
Last change on this file since e6a6d9a was e6a6d9a, checked in by Стојков Марко <mst@…>, 3 years ago

Initialized FinkiChattery project

  • Property mode set to 100644
File size: 9.7 KB
Line 
1// Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
2// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
3
4
5using IdentityServer4.Events;
6using IdentityServer4.Models;
7using IdentityServer4.Services;
8using IdentityServer4.Extensions;
9using Microsoft.AspNetCore.Authorization;
10using Microsoft.AspNetCore.Mvc;
11using Microsoft.Extensions.Logging;
12using System.Linq;
13using System.Threading.Tasks;
14using IdentityServer4.Validation;
15using System.Collections.Generic;
16using System;
17
18namespace IdentityServerHost.Quickstart.UI
19{
20 /// <summary>
21 /// This controller processes the consent UI
22 /// </summary>
23 [SecurityHeaders]
24 [Authorize]
25 public class ConsentController : Controller
26 {
27 private readonly IIdentityServerInteractionService _interaction;
28 private readonly IEventService _events;
29 private readonly ILogger<ConsentController> _logger;
30
31 public ConsentController(
32 IIdentityServerInteractionService interaction,
33 IEventService events,
34 ILogger<ConsentController> logger)
35 {
36 _interaction = interaction;
37 _events = events;
38 _logger = logger;
39 }
40
41 /// <summary>
42 /// Shows the consent screen
43 /// </summary>
44 /// <param name="returnUrl"></param>
45 /// <returns></returns>
46 [HttpGet]
47 public async Task<IActionResult> Index(string returnUrl)
48 {
49 var vm = await BuildViewModelAsync(returnUrl);
50 if (vm != null)
51 {
52 return View("Index", vm);
53 }
54
55 return View("Error");
56 }
57
58 /// <summary>
59 /// Handles the consent screen postback
60 /// </summary>
61 [HttpPost]
62 [ValidateAntiForgeryToken]
63 public async Task<IActionResult> Index(ConsentInputModel model)
64 {
65 var result = await ProcessConsent(model);
66
67 if (result.IsRedirect)
68 {
69 var context = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl);
70 if (context?.IsNativeClient() == true)
71 {
72 // The client is native, so this change in how to
73 // return the response is for better UX for the end user.
74 return this.LoadingPage("Redirect", result.RedirectUri);
75 }
76
77 return Redirect(result.RedirectUri);
78 }
79
80 if (result.HasValidationError)
81 {
82 ModelState.AddModelError(string.Empty, result.ValidationError);
83 }
84
85 if (result.ShowView)
86 {
87 return View("Index", result.ViewModel);
88 }
89
90 return View("Error");
91 }
92
93 /*****************************************/
94 /* helper APIs for the ConsentController */
95 /*****************************************/
96 private async Task<ProcessConsentResult> ProcessConsent(ConsentInputModel model)
97 {
98 var result = new ProcessConsentResult();
99
100 // validate return url is still valid
101 var request = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl);
102 if (request == null) return result;
103
104 ConsentResponse grantedConsent = null;
105
106 // user clicked 'no' - send back the standard 'access_denied' response
107 if (model?.Button == "no")
108 {
109 grantedConsent = new ConsentResponse { Error = AuthorizationError.AccessDenied };
110
111 // emit event
112 await _events.RaiseAsync(new ConsentDeniedEvent(User.GetSubjectId(), request.Client.ClientId, request.ValidatedResources.RawScopeValues));
113 }
114 // user clicked 'yes' - validate the data
115 else if (model?.Button == "yes")
116 {
117 // if the user consented to some scope, build the response model
118 if (model.ScopesConsented != null && model.ScopesConsented.Any())
119 {
120 var scopes = model.ScopesConsented;
121 if (ConsentOptions.EnableOfflineAccess == false)
122 {
123 scopes = scopes.Where(x => x != IdentityServer4.IdentityServerConstants.StandardScopes.OfflineAccess);
124 }
125
126 grantedConsent = new ConsentResponse
127 {
128 RememberConsent = model.RememberConsent,
129 ScopesValuesConsented = scopes.ToArray(),
130 Description = model.Description
131 };
132
133 // emit event
134 await _events.RaiseAsync(new ConsentGrantedEvent(User.GetSubjectId(), request.Client.ClientId, request.ValidatedResources.RawScopeValues, grantedConsent.ScopesValuesConsented, grantedConsent.RememberConsent));
135 }
136 else
137 {
138 result.ValidationError = ConsentOptions.MustChooseOneErrorMessage;
139 }
140 }
141 else
142 {
143 result.ValidationError = ConsentOptions.InvalidSelectionErrorMessage;
144 }
145
146 if (grantedConsent != null)
147 {
148 // communicate outcome of consent back to identityserver
149 await _interaction.GrantConsentAsync(request, grantedConsent);
150
151 // indicate that's it ok to redirect back to authorization endpoint
152 result.RedirectUri = model.ReturnUrl;
153 result.Client = request.Client;
154 }
155 else
156 {
157 // we need to redisplay the consent UI
158 result.ViewModel = await BuildViewModelAsync(model.ReturnUrl, model);
159 }
160
161 return result;
162 }
163
164 private async Task<ConsentViewModel> BuildViewModelAsync(string returnUrl, ConsentInputModel model = null)
165 {
166 var request = await _interaction.GetAuthorizationContextAsync(returnUrl);
167 if (request != null)
168 {
169 return CreateConsentViewModel(model, returnUrl, request);
170 }
171 else
172 {
173 _logger.LogError("No consent request matching request: {0}", returnUrl);
174 }
175
176 return null;
177 }
178
179 private ConsentViewModel CreateConsentViewModel(
180 ConsentInputModel model, string returnUrl,
181 AuthorizationRequest request)
182 {
183 var vm = new ConsentViewModel
184 {
185 RememberConsent = model?.RememberConsent ?? true,
186 ScopesConsented = model?.ScopesConsented ?? Enumerable.Empty<string>(),
187 Description = model?.Description,
188
189 ReturnUrl = returnUrl,
190
191 ClientName = request.Client.ClientName ?? request.Client.ClientId,
192 ClientUrl = request.Client.ClientUri,
193 ClientLogoUrl = request.Client.LogoUri,
194 AllowRememberConsent = request.Client.AllowRememberConsent
195 };
196
197 vm.IdentityScopes = request.ValidatedResources.Resources.IdentityResources.Select(x => CreateScopeViewModel(x, vm.ScopesConsented.Contains(x.Name) || model == null)).ToArray();
198
199 var apiScopes = new List<ScopeViewModel>();
200 foreach(var parsedScope in request.ValidatedResources.ParsedScopes)
201 {
202 var apiScope = request.ValidatedResources.Resources.FindApiScope(parsedScope.ParsedName);
203 if (apiScope != null)
204 {
205 var scopeVm = CreateScopeViewModel(parsedScope, apiScope, vm.ScopesConsented.Contains(parsedScope.RawValue) || model == null);
206 apiScopes.Add(scopeVm);
207 }
208 }
209 if (ConsentOptions.EnableOfflineAccess && request.ValidatedResources.Resources.OfflineAccess)
210 {
211 apiScopes.Add(GetOfflineAccessScope(vm.ScopesConsented.Contains(IdentityServer4.IdentityServerConstants.StandardScopes.OfflineAccess) || model == null));
212 }
213 vm.ApiScopes = apiScopes;
214
215 return vm;
216 }
217
218 private ScopeViewModel CreateScopeViewModel(IdentityResource identity, bool check)
219 {
220 return new ScopeViewModel
221 {
222 Value = identity.Name,
223 DisplayName = identity.DisplayName ?? identity.Name,
224 Description = identity.Description,
225 Emphasize = identity.Emphasize,
226 Required = identity.Required,
227 Checked = check || identity.Required
228 };
229 }
230
231 public ScopeViewModel CreateScopeViewModel(ParsedScopeValue parsedScopeValue, ApiScope apiScope, bool check)
232 {
233 var displayName = apiScope.DisplayName ?? apiScope.Name;
234 if (!String.IsNullOrWhiteSpace(parsedScopeValue.ParsedParameter))
235 {
236 displayName += ":" + parsedScopeValue.ParsedParameter;
237 }
238
239 return new ScopeViewModel
240 {
241 Value = parsedScopeValue.RawValue,
242 DisplayName = displayName,
243 Description = apiScope.Description,
244 Emphasize = apiScope.Emphasize,
245 Required = apiScope.Required,
246 Checked = check || apiScope.Required
247 };
248 }
249
250 private ScopeViewModel GetOfflineAccessScope(bool check)
251 {
252 return new ScopeViewModel
253 {
254 Value = IdentityServer4.IdentityServerConstants.StandardScopes.OfflineAccess,
255 DisplayName = ConsentOptions.OfflineAccessDisplayName,
256 Description = ConsentOptions.OfflineAccessDescription,
257 Emphasize = true,
258 Checked = check
259 };
260 }
261 }
262}
Note: See TracBrowser for help on using the repository browser.