Search code examples
asp.netasp.net-web-api2multi-tenantasp.net-identity-2

How to re-validate token for multi-tenant ASP.NET Identity?


I have implemented a custom OAuthAuthorizationServerProvider to add a domain constraint for the account login. Everything was good. However, I met a problem that, once the user get the token, they can use it for whatever system they want. For example:

They request the TokenEndpointPath with proper username and password (assume it is the admin account of Tenant 1): http://localhost:40721/api/v1/account/auth and receive the Bearer Token.

Now they use it to access: http://localhost:40720/api/v1/info/admin, which is of Tenant 0. The request is considered Authorized.

I tried changing the CreateProperties method but it did not help:

    public static AuthenticationProperties CreateProperties(string userName)
    {
        var tenant = DependencyUtils.Resolve<IdentityTenant>();
        IDictionary<string, string> data = new Dictionary<string, string>
        {
            { "userName", userName },
            { "tenantId", tenant.Tenant.Id.ToString() },
        };
        return new AuthenticationProperties(data);
    }

I also tried overriding ValidateAuthorizeRequest, but it is never called in my debug.

Do I need to implement a check anywhere else, so the Token is only valid for a domain/correct tenant?

(NOTE: a tenant may have multiple domains, so it's great if I can manually perform an account check against correct tenant rather than sticking to a domain. However, it's a plus if I could do that, or else, simply limit the token to the domain is ok)


Solution

  • Not a direct answer to my question (since it's not inside ASP.NET Identity workflow), but the simplest fix I applied was to use ActionFilterAttribute instead.

    public class DomainValidationFilter : ActionFilterAttribute
    {
    
        public override Task OnActionExecutingAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
        {
            // Other Code...
    
            // Validate if the logged in user is from correct tenant
            var principal = actionContext.ControllerContext.RequestContext.Principal;
            if (principal != null && principal.Identity != null && principal.Identity.IsAuthenticated)
            {
                var userId = int.Parse(principal.Identity.GetUserId());
                // Validate against the tenant Id of your own storage, and use this code to invalidate the request if it is trying to exploit:
               actionContext.Response = actionContext.Request.CreateResponse(System.Net.HttpStatusCode.Unauthorized, "Invalid Token"); 
    
            }
    
            return base.OnActionExecutingAsync(actionContext, cancellationToken);
        }
    
    }
    

    Then applies the Filter to all actions by registering it in either FilterConfig or WebApiConfig:

    config.Filters.Add(new DomainValidationFilter());