Add claims with Owin Middleware
Asked Answered
S

3

9

Is it possible with an Owin Middleware implementation to add claims prior to the execution of a Web API controller?

Created an OwinMiddleware implementation and added an identity:

 var id = new ClaimsIdentity();
 id.AddClaim(new Claim("Whatever", "is possible"));
 context.Authentication.User.AddIdentity(id);

 await Next.Invoke(context);

However, even this Invoke method call the identities are not updated (just the internal claims array). And the controller when executed of course never gets the new dummy claim.

Ideas?

Sidle answered 5/2, 2015 at 13:56 Comment(1)
After a chat with @Pinpoint on the Owin lobby in JabbR it appears that the only way into the authentication line is through the IAppBuilder's UseOAuthBearerAuthentication by specifying a custom provider. Claims can then be added into the identity context (context.Ticket.Identity.AddClaim(...)). This is done per request.Sidle
G
5

There's already a class that can provide claims enrichment ClaimsAuthenticationManager, which you can extend so it handles your domain-specific claims, for example...

public class MyClaimsAuthenticationManager : ClaimsAuthenticationManager
{
    public override ClaimsPrincipal Authenticate(string resourceName, ClaimsPrincipal incomingPrincipal)
    {
        if (!incomingPrincipal.Identity.IsAuthenticated)
        {
            return base.Authenticate(resourceName, incomingPrincipal);
        }

        return AddApplicationClaims(incomingPrincipal);
    }

    private ClaimsPrincipal AddApplicationClaims(ClaimsPrincipal principal)
    {
        // TODO: Add custom claims here based on current principal.

        return principal;
    }
}

Next task is to provide appropriate middleware to invoke this. For my projects I've written the following classes...

/// <summary>
/// Middleware component to apply claims transformation to current context
/// </summary>
public class ClaimsTransformationMiddleware
{
    private readonly Func<IDictionary<string, object>, Task> next;
    private readonly IServiceProvider serviceProvider;

    public ClaimsTransformationMiddleware(Func<IDictionary<string, object>, Task> next, IServiceProvider serviceProvider)
    {
        this.next = next;
        this.serviceProvider = serviceProvider;
    }

    public async Task Invoke(IDictionary<string, object> env)
    {
        // Use Katana's OWIN abstractions
        var context = new OwinContext(env);

        if (context.Authentication != null && context.Authentication.User != null)
        {
            var manager = serviceProvider.GetService<ClaimsAuthenticationManager>();
            context.Authentication.User = manager.Authenticate(context.Request.Uri.AbsoluteUri, context.Authentication.User);
        }

        await next(env);
    }
}

And then a wiring extension...

public static class AppBuilderExtensions
{
    /// <summary>
    /// Add claims transformation using <see cref="ClaimsTransformationMiddleware" /> any depdendency resolution is done via IoC
    /// </summary>
    /// <param name="app"></param>
    /// <param name="serviceProvider"></param>
    /// <returns></returns>
    public static IAppBuilder UseClaimsTransformation(this IAppBuilder app, IServiceProvider serviceProvider)
    {
        app.Use<ClaimsTransformationMiddleware>(serviceProvider);

        return app;
    }
}

I know this is service locator anti-pattern but using IServiceProvider is container neutral and seems to be the accepted way of putting dependencies into Owin middleware.

Last you need to wire this up in your Startup, example below presumes Unity and registering/exposing a IServiceLocator property...

// Owin config
app.UseClaimsTransformation(UnityConfig.ServiceLocator);
Gleaning answered 16/11, 2016 at 15:54 Comment(0)
R
1

You may find useful inheriting from Authorizate Attribute and extending it to meet your requirements:

public class DemoAuthorizeAttribute : AuthorizeAttribute
    {     

        public override void OnAuthorization(System.Web.Http.Controllers.HttpActionContext actionContext){
            if (Authorize(actionContext)){
                return;
            }
            HandleUnauthorizedRequest(actionContext);
        }

        protected override void HandleUnauthorizedRequest(System.Web.Http.Controllers.HttpActionContext actionContext){
            var challengeMessage = new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized;

//Adding your code here
 var id = new ClaimsIdentity();
 id.AddClaim(new Claim("Whatever", "is possible"));
 context.Authentication.User.AddIdentity(id);

            challengeMessage.Headers.Add("WWW-Authenticate", "Basic");
            throw new HttpResponseException(challengeMessage);
        }

        private bool Authorize(System.Web.Http.Controllers.HttpActionContext actionContext){
            try{
                var someCode = (from h in actionContext.Request.Headers where h.Key == "demo" select h.Value.First()).FirstOrDefault();

                 // or check for the claims identity property.
                return someCode == "myCode";
            }
            catch (Exception){
                return false;
            }
        }
    }

And in your controller:

[DemoAuthorize]
public class ValuesController : ApiController{

Here is a link on other custom implemenation for WebApi Authorizations:

http://www.piotrwalat.net/basic-http-authentication-in-asp-net-web-api-using-membership-provider/

Retake answered 5/2, 2015 at 14:0 Comment(1)
Only issue I've found with this is if you are supporting multiple authorization methods e.g. OAuth and HMAC you have to implement the claims enrichment in multiple places, hence my answer below.Gleaning
T
1

This is how I ended up adding a new claim in owin middleware, based on the OP's comment about hooking into UseOAuthBearerAuthentication. It uses IdentityServer3.AccessTokenValidation, which calls UseOAuthBearerAuthentication internally and passes the OAuthBearerAuthenticationProvider through to it.

using System.Security.Claims;
using System.Threading.Tasks;
using IdentityServer3.AccessTokenValidation;
using Owin;
using Microsoft.Owin.Security.OAuth;

//...

public void Configuration(IAppBuilder app)
{
    app.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions
    {
        Authority = "http://127.0.0.1/identityserver", 
        TokenProvider = new OAuthBearerAuthenticationProvider
        {
            OnValidateIdentity = AddClaim
        }
    });
}

private Task AddClaim(OAuthValidateIdentityContext context)
{
    context.Ticket.Identity.AddClaim(new Claim("test", "123"));
    return Task.CompletedTask;
}
Trinee answered 23/11, 2016 at 16:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.