'IdentityServer4 Authorization context as transient service
We have a running IS4 instance for corporate customers (built on top of QuickUI), now the mgmt wants to extend it with another client and they want to have a special layout and page adjustments when the authorization process comes from this client (in essence we need to collect additional info in login form and it has to be branded slightly differently).
Now, adjusting the form and visual appearance is not a problem, but deciding when to show it is proving a challenge. We have our templating and branding in _Layout which is used by all the pages, and I somehow need to know inside _layout, if the page load is part of the authentication context and if so, which one.
The way QuickUI did it in AccountController, is using IS interaction, which generates AuthenticationRequest which contains the client:
var context = await interaction.GetAuthorizationContextAsync(returnUrl);
var clientName = context.Client?.ClientName;
This fits nicely with AccountController logic and works just fine for authentication purposes, but does little for visual. I do not have the returnUrl inside _Layout (and layout is used by other pages, for registration, etc, so I cannot really cram specific viewmodels into it) and apparently no clean way to determine the context.
a) I could start digging through HttpRequest and parsing request URLs, fishing for returnUrls, but I would first like to see if there is a more streamlined, or even existing solution.
b) Another option is to have multiple layout files, and expose Client to the ViewModel and switch layouts based on it.
Ideally however, I would like something "clean" and maintainable, a service I could @inject into _Layout.cshtml and just do @if (contextService.Client == ...)
Has someone seen or done something like it?
Solution 1:[1]
Until (and if) someone posts a "cleaner" solution (especially the one that does not revolve around returnUrl magic string), this is the workaround I implemented. It's dirty, I admit, it pokes on HttpContext from a service, which is not really asp.net core way, but at this point, I see no other way to obtain the information I need to construct authorization context:
using System.Threading.Tasks;
using IdentityServer4.Models;
using IdentityServer4.Services;
using Microsoft.AspNetCore.Http;
namespace MyServices
{
public interface IContextService
{
Task<AuthorizationRequest> GetContextAsync();
}
public class ContextService : IContextService
{
private readonly IIdentityServerInteractionService interaction;
private readonly IHttpContextAccessor contextAccessor;
private AuthorizationRequest context;
public ContextService(IIdentityServerInteractionService interaction, IHttpContextAccessor contextAccessor)
{
this.interaction = interaction;
this.contextAccessor = contextAccessor;
}
public async Task<AuthorizationRequest> GetContextAsync() => context ??= await InternalObtainContextAsync();
private async Task<AuthorizationRequest> InternalObtainContextAsync()
{
var query = contextAccessor.HttpContext?.Request.Query;
if (query == null || query.Count == 0 || !query.ContainsKey("returnUrl")) return new AuthorizationRequest(); // empty context
return await interaction.GetAuthorizationContextAsync(query["returnUrl"]);
}
}
}
Register in startup with services.AddTransient<IContextService, ContextService>(); and then you can inject it into any page and/or layout:
@inject IContextService contextService;
@{
// render only when invoked from authorization context
var context = await contextService.GetContextAsync();
if (context.Client != null)
{
<div>Hi, I am inside authorization context for client @context.Client.ClientId</div>
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | mmix |
