| 
									
										
										
										
											2020-10-22 14:59:36 +08:00
										 |  |  | // 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; | 
					
						
							|  |  |  | using System.Linq; | 
					
						
							|  |  |  | using System.Threading.Tasks; | 
					
						
							|  |  |  | using IdentityModel; | 
					
						
							|  |  |  | using IdentityServer4.Events; | 
					
						
							|  |  |  | using IdentityServer4.Extensions; | 
					
						
							|  |  |  | using IdentityServer4.Models; | 
					
						
							|  |  |  | using IdentityServer4.Services; | 
					
						
							|  |  |  | using IdentityServer4.Stores; | 
					
						
							| 
									
										
										
										
											2021-01-14 23:35:54 +08:00
										 |  |  | using Infrastructure; | 
					
						
							| 
									
										
										
										
											2020-10-22 14:59:36 +08:00
										 |  |  | using Microsoft.AspNetCore.Authentication; | 
					
						
							|  |  |  | using Microsoft.AspNetCore.Authorization; | 
					
						
							|  |  |  | using Microsoft.AspNetCore.Http; | 
					
						
							|  |  |  | using Microsoft.AspNetCore.Mvc; | 
					
						
							|  |  |  | using OpenAuth.App; | 
					
						
							|  |  |  | using OpenAuth.Repository.Domain; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace OpenAuth.IdentityServer.Quickstart.Account | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     /// <summary> | 
					
						
							|  |  |  |     /// This sample controller implements a typical login/logout/provision workflow for local and external accounts. | 
					
						
							|  |  |  |     /// The login service encapsulates the interactions with the user data store. This data store is in-memory only and cannot be used for production! | 
					
						
							|  |  |  |     /// The interaction service provides a way for the UI to communicate with identityserver for validation and context retrieval | 
					
						
							|  |  |  |     /// </summary> | 
					
						
							|  |  |  |     [SecurityHeaders] | 
					
						
							|  |  |  |     [AllowAnonymous] | 
					
						
							|  |  |  |     public class AccountController : Controller | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         private readonly UserManagerApp _userManager; | 
					
						
							|  |  |  |         private readonly IIdentityServerInteractionService _interaction; | 
					
						
							|  |  |  |         private readonly IClientStore _clientStore; | 
					
						
							|  |  |  |         private readonly IAuthenticationSchemeProvider _schemeProvider; | 
					
						
							|  |  |  |         private readonly IEventService _events; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         public AccountController( | 
					
						
							|  |  |  |             IIdentityServerInteractionService interaction, | 
					
						
							|  |  |  |             IClientStore clientStore, | 
					
						
							|  |  |  |             IAuthenticationSchemeProvider schemeProvider, | 
					
						
							|  |  |  |             IEventService events, UserManagerApp userManager) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             _interaction = interaction; | 
					
						
							|  |  |  |             _clientStore = clientStore; | 
					
						
							|  |  |  |             _schemeProvider = schemeProvider; | 
					
						
							|  |  |  |             _events = events; | 
					
						
							|  |  |  |             _userManager = userManager; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /// <summary> | 
					
						
							|  |  |  |         /// Entry point into the login workflow | 
					
						
							|  |  |  |         /// </summary> | 
					
						
							|  |  |  |         [HttpGet] | 
					
						
							|  |  |  |         public async Task<IActionResult> Login(string returnUrl) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             // build a model so we know what to show on the login page | 
					
						
							|  |  |  |             var vm = await BuildLoginViewModelAsync(returnUrl); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (vm.IsExternalLoginOnly) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 // we only have one option for logging in and it's an external provider | 
					
						
							|  |  |  |                 return RedirectToAction("Challenge", "External", new { provider = vm.ExternalLoginScheme, returnUrl }); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             return View(vm); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /// <summary> | 
					
						
							|  |  |  |         /// Handle postback from username/password login | 
					
						
							|  |  |  |         /// </summary> | 
					
						
							|  |  |  |         [HttpPost] | 
					
						
							|  |  |  |         [ValidateAntiForgeryToken] | 
					
						
							|  |  |  |         public async Task<IActionResult> Login(LoginInputModel model, string button) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             // check if we are in the context of an authorization request | 
					
						
							|  |  |  |             var context = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // the user clicked the "cancel" button | 
					
						
							|  |  |  |             if (button != "login") | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 if (context != null) | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                     // if the user cancels, send a result back into IdentityServer as if they  | 
					
						
							|  |  |  |                     // denied the consent (even if this client does not require consent). | 
					
						
							|  |  |  |                     // this will send back an access denied OIDC error response to the client. | 
					
						
							|  |  |  |                     await _interaction.GrantConsentAsync(context, ConsentResponse.Denied); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     // we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null | 
					
						
							|  |  |  |                     if (await _clientStore.IsPkceClientAsync(context.ClientId)) | 
					
						
							|  |  |  |                     { | 
					
						
							|  |  |  |                         // if the client is PKCE then we assume it's native, so this change in how to | 
					
						
							|  |  |  |                         // return the response is for better UX for the end user. | 
					
						
							|  |  |  |                         return View("Redirect", new RedirectViewModel { RedirectUrl = model.ReturnUrl }); | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     return Redirect(model.ReturnUrl); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 else | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                     // since we don't have a valid context, then we just go back to the home page | 
					
						
							|  |  |  |                     return Redirect("~/"); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (ModelState.IsValid) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 User user; | 
					
						
							|  |  |  |                 if (model.Username == Define.SYSTEM_USERNAME && model.Password == Define.SYSTEM_USERPWD) | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                     user = new User | 
					
						
							|  |  |  |                     { | 
					
						
							|  |  |  |                         Account = Define.SYSTEM_USERNAME, | 
					
						
							|  |  |  |                         Password = Define.SYSTEM_USERPWD, | 
					
						
							|  |  |  |                         Id = Define.SYSTEM_USERNAME | 
					
						
							|  |  |  |                     }; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 else | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                     user = _userManager.GetByAccount(model.Username); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 if (user != null &&(user.Password ==model.Password)) | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                     if (user.Status != 0)   //判断用户状态 | 
					
						
							|  |  |  |                     { | 
					
						
							|  |  |  |                         await _events.RaiseAsync(new UserLoginFailureEvent(model.Username, "invalid user status")); | 
					
						
							|  |  |  |                         ModelState.AddModelError(string.Empty, "user.status must be 0"); | 
					
						
							|  |  |  |                         var err = await BuildLoginViewModelAsync(model); | 
					
						
							|  |  |  |                         return View(err); | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     await _events.RaiseAsync(new UserLoginSuccessEvent(user.Account, user.Id, user.Account)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     // only set explicit expiration here if user chooses "remember me".  | 
					
						
							|  |  |  |                     // otherwise we rely upon expiration configured in cookie middleware. | 
					
						
							|  |  |  |                     AuthenticationProperties props = null; | 
					
						
							|  |  |  |                     if (AccountOptions.AllowRememberLogin && model.RememberLogin) | 
					
						
							|  |  |  |                     { | 
					
						
							|  |  |  |                         props = new AuthenticationProperties | 
					
						
							|  |  |  |                         { | 
					
						
							|  |  |  |                             IsPersistent = true, | 
					
						
							|  |  |  |                             ExpiresUtc = DateTimeOffset.UtcNow.Add(AccountOptions.RememberMeLoginDuration) | 
					
						
							|  |  |  |                         }; | 
					
						
							|  |  |  |                     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     // issue authentication cookie with subject ID and username | 
					
						
							|  |  |  |                     await HttpContext.SignInAsync(user.Id, user.Account, props); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     if (context != null) | 
					
						
							|  |  |  |                     { | 
					
						
							|  |  |  |                         if (await _clientStore.IsPkceClientAsync(context.ClientId)) | 
					
						
							|  |  |  |                         { | 
					
						
							|  |  |  |                             // if the client is PKCE then we assume it's native, so this change in how to | 
					
						
							|  |  |  |                             // return the response is for better UX for the end user. | 
					
						
							|  |  |  |                             return View("Redirect", new RedirectViewModel { RedirectUrl = model.ReturnUrl }); | 
					
						
							|  |  |  |                         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                         // we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null | 
					
						
							|  |  |  |                         return Redirect(model.ReturnUrl); | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     // request for a local page | 
					
						
							|  |  |  |                     if (Url.IsLocalUrl(model.ReturnUrl)) | 
					
						
							|  |  |  |                     { | 
					
						
							|  |  |  |                         return Redirect(model.ReturnUrl); | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                     else if (string.IsNullOrEmpty(model.ReturnUrl)) | 
					
						
							|  |  |  |                     { | 
					
						
							|  |  |  |                         return Redirect("~/"); | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                     else | 
					
						
							|  |  |  |                     { | 
					
						
							|  |  |  |                         // user might have clicked on a malicious link - should be logged | 
					
						
							|  |  |  |                         throw new Exception("invalid return URL"); | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 await _events.RaiseAsync(new UserLoginFailureEvent(model.Username, "invalid credentials", clientId:context?.ClientId)); | 
					
						
							|  |  |  |                 ModelState.AddModelError(string.Empty, AccountOptions.InvalidCredentialsErrorMessage); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // something went wrong, show form with error | 
					
						
							|  |  |  |             var vm = await BuildLoginViewModelAsync(model); | 
					
						
							|  |  |  |             return View(vm); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         /// <summary> | 
					
						
							|  |  |  |         /// Show logout page | 
					
						
							|  |  |  |         /// </summary> | 
					
						
							|  |  |  |         [HttpGet] | 
					
						
							|  |  |  |         public async Task<IActionResult> Logout(string logoutId) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             // build a model so the logout page knows what to display | 
					
						
							|  |  |  |             var vm = await BuildLogoutViewModelAsync(logoutId); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (vm.ShowLogoutPrompt == false) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 // if the request for logout was properly authenticated from IdentityServer, then | 
					
						
							|  |  |  |                 // we don't need to show the prompt and can just log the user out directly. | 
					
						
							|  |  |  |                 return await Logout(vm); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             return View(vm); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /// <summary> | 
					
						
							|  |  |  |         /// Handle logout page postback | 
					
						
							|  |  |  |         /// </summary> | 
					
						
							|  |  |  |         [HttpPost] | 
					
						
							|  |  |  |         [ValidateAntiForgeryToken] | 
					
						
							|  |  |  |         public async Task<IActionResult> Logout(LogoutInputModel model) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             // build a model so the logged out page knows what to display | 
					
						
							|  |  |  |             var vm = await BuildLoggedOutViewModelAsync(model.LogoutId); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (User?.Identity.IsAuthenticated == true) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 // delete local authentication cookie | 
					
						
							|  |  |  |                 await HttpContext.SignOutAsync(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 // raise the logout event | 
					
						
							|  |  |  |                 await _events.RaiseAsync(new UserLogoutSuccessEvent(User.GetSubjectId(), User.GetDisplayName())); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // check if we need to trigger sign-out at an upstream identity provider | 
					
						
							|  |  |  |             if (vm.TriggerExternalSignout) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 // build a return URL so the upstream provider will redirect back | 
					
						
							|  |  |  |                 // to us after the user has logged out. this allows us to then | 
					
						
							|  |  |  |                 // complete our single sign-out processing. | 
					
						
							|  |  |  |                 string url = Url.Action("Logout", new { logoutId = vm.LogoutId }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 // this triggers a redirect to the external provider for sign-out | 
					
						
							|  |  |  |                 return SignOut(new AuthenticationProperties { RedirectUri = url }, vm.ExternalAuthenticationScheme); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             return View("LoggedOut", vm); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         [HttpGet] | 
					
						
							|  |  |  |         public IActionResult AccessDenied() | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             return View(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /*****************************************/ | 
					
						
							|  |  |  |         /* helper APIs for the AccountController */ | 
					
						
							|  |  |  |         /*****************************************/ | 
					
						
							|  |  |  |         private async Task<LoginViewModel> BuildLoginViewModelAsync(string returnUrl) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             var context = await _interaction.GetAuthorizationContextAsync(returnUrl); | 
					
						
							|  |  |  |             if (context?.IdP != null && await _schemeProvider.GetSchemeAsync(context.IdP) != null) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 var local = context.IdP == IdentityServer4.IdentityServerConstants.LocalIdentityProvider; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 // this is meant to short circuit the UI and only trigger the one external IdP | 
					
						
							|  |  |  |                 var vm = new LoginViewModel | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                     EnableLocalLogin = local, | 
					
						
							|  |  |  |                     ReturnUrl = returnUrl, | 
					
						
							|  |  |  |                     Username = context?.LoginHint, | 
					
						
							|  |  |  |                 }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 if (!local) | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                     vm.ExternalProviders = new[] { new ExternalProvider { AuthenticationScheme = context.IdP } }; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 return vm; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             var schemes = await _schemeProvider.GetAllSchemesAsync(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             var providers = schemes | 
					
						
							|  |  |  |                 .Where(x => x.DisplayName != null || | 
					
						
							|  |  |  |                             (x.Name.Equals(AccountOptions.WindowsAuthenticationSchemeName, StringComparison.OrdinalIgnoreCase)) | 
					
						
							|  |  |  |                 ) | 
					
						
							|  |  |  |                 .Select(x => new ExternalProvider | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                     DisplayName = x.DisplayName, | 
					
						
							|  |  |  |                     AuthenticationScheme = x.Name | 
					
						
							|  |  |  |                 }).ToList(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             var allowLocal = true; | 
					
						
							|  |  |  |             if (context?.ClientId != null) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 var client = await _clientStore.FindEnabledClientByIdAsync(context.ClientId); | 
					
						
							|  |  |  |                 if (client != null) | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                     allowLocal = client.EnableLocalLogin; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     if (client.IdentityProviderRestrictions != null && client.IdentityProviderRestrictions.Any()) | 
					
						
							|  |  |  |                     { | 
					
						
							|  |  |  |                         providers = providers.Where(provider => client.IdentityProviderRestrictions.Contains(provider.AuthenticationScheme)).ToList(); | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             return new LoginViewModel | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 AllowRememberLogin = AccountOptions.AllowRememberLogin, | 
					
						
							|  |  |  |                 EnableLocalLogin = allowLocal && AccountOptions.AllowLocalLogin, | 
					
						
							|  |  |  |                 ReturnUrl = returnUrl, | 
					
						
							|  |  |  |                 Username = context?.LoginHint, | 
					
						
							|  |  |  |                 ExternalProviders = providers.ToArray() | 
					
						
							|  |  |  |             }; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         private async Task<LoginViewModel> BuildLoginViewModelAsync(LoginInputModel model) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             var vm = await BuildLoginViewModelAsync(model.ReturnUrl); | 
					
						
							|  |  |  |             vm.Username = model.Username; | 
					
						
							|  |  |  |             vm.RememberLogin = model.RememberLogin; | 
					
						
							|  |  |  |             return vm; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         private async Task<LogoutViewModel> BuildLogoutViewModelAsync(string logoutId) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             var vm = new LogoutViewModel { LogoutId = logoutId, ShowLogoutPrompt = AccountOptions.ShowLogoutPrompt }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (User?.Identity.IsAuthenticated != true) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 // if the user is not authenticated, then just show logged out page | 
					
						
							|  |  |  |                 vm.ShowLogoutPrompt = false; | 
					
						
							|  |  |  |                 return vm; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             var context = await _interaction.GetLogoutContextAsync(logoutId); | 
					
						
							|  |  |  |             if (context?.ShowSignoutPrompt == false) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 // it's safe to automatically sign-out | 
					
						
							|  |  |  |                 vm.ShowLogoutPrompt = false; | 
					
						
							|  |  |  |                 return vm; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // show the logout prompt. this prevents attacks where the user | 
					
						
							|  |  |  |             // is automatically signed out by another malicious web page. | 
					
						
							|  |  |  |             return vm; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         private async Task<LoggedOutViewModel> BuildLoggedOutViewModelAsync(string logoutId) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             // get context information (client name, post logout redirect URI and iframe for federated signout) | 
					
						
							|  |  |  |             var logout = await _interaction.GetLogoutContextAsync(logoutId); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             var vm = new LoggedOutViewModel | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 AutomaticRedirectAfterSignOut = AccountOptions.AutomaticRedirectAfterSignOut, | 
					
						
							|  |  |  |                 PostLogoutRedirectUri = logout?.PostLogoutRedirectUri, | 
					
						
							|  |  |  |                 ClientName = string.IsNullOrEmpty(logout?.ClientName) ? logout?.ClientId : logout?.ClientName, | 
					
						
							|  |  |  |                 SignOutIframeUrl = logout?.SignOutIFrameUrl, | 
					
						
							|  |  |  |                 LogoutId = logoutId | 
					
						
							|  |  |  |             }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (User?.Identity.IsAuthenticated == true) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 var idp = User.FindFirst(JwtClaimTypes.IdentityProvider)?.Value; | 
					
						
							|  |  |  |                 if (idp != null && idp != IdentityServer4.IdentityServerConstants.LocalIdentityProvider) | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                     var providerSupportsSignout = await HttpContext.GetSchemeSupportsSignOutAsync(idp); | 
					
						
							|  |  |  |                     if (providerSupportsSignout) | 
					
						
							|  |  |  |                     { | 
					
						
							|  |  |  |                         if (vm.LogoutId == null) | 
					
						
							|  |  |  |                         { | 
					
						
							|  |  |  |                             // if there's no current logout context, we need to create one | 
					
						
							|  |  |  |                             // this captures necessary info from the current logged in user | 
					
						
							|  |  |  |                             // before we signout and redirect away to the external IdP for signout | 
					
						
							|  |  |  |                             vm.LogoutId = await _interaction.CreateLogoutContextAsync(); | 
					
						
							|  |  |  |                         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                         vm.ExternalAuthenticationScheme = idp; | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             return vm; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } |