Base solution for your next web application
Open Closed

Corporate Identity Server #1725


User avatar
0
pnw created

In my company, all on premises web servers are behind a web-based authenticator (web single sign on). Once signed on, my request header comes with my employee id. e.g. var userid = HttpContext.Current.Request.Headers['employeeid']. If the header exists and is not blank, that means authentication is successful.

How do I inform AbpZero that authentication is complete and to find my userid in the headers, and also thereby to not display the login page?


9 Answer(s)
  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi,

    You can create a custom AuthorizeFilter for MVC or WebAPI and check the request here in that. Please see this issue <a class="postlink" href="https://github.com/aspnetboilerplate/aspnetboilerplate/issues/1256">https://github.com/aspnetboilerplate/as ... ssues/1256</a>

  • User Avatar
    0
    pnw created

    <cite>ismcagdas: </cite> Hi,

    You can create a custom AuthorizeFilter for MVC or WebAPI and check the request here in that. Please see this issue <a class="postlink" href="https://github.com/aspnetboilerplate/aspnetboilerplate/issues/1256">https://github.com/aspnetboilerplate/as ... ssues/1256</a>

    I think I followed the instructions but the Log In page still displays. This is what I did:

    I implemented a class called MyAuthorizeFilter and derived it from AbpMvcAuthorizeFilter. Then I overrode OnAuthorization to create the ClaimsPrincipal and attach it to the HttpContext.User.

    public override void OnAuthorization(AuthorizationContext filterContext)
            {
                var empid = filterContext.HttpContext.Request.Headers["employeeid"];
                if (empid == null) empid = "123456"; // for local testing
    
                ClaimsPrincipal currentPrincipal = new ClaimsPrincipal(
                    new ClaimsIdentity(
                        new List<Claim>
                        {
                            new Claim(ClaimTypes.NameIdentifier, empid),
                            new Claim("http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider", empid)
                        }, "wsso"
                    )
                );
    
                Thread.CurrentPrincipal = currentPrincipal;
                filterContext.HttpContext.User = currentPrincipal;
    
                base.OnAuthorization(filterContext);
            }
    

    Then I added my class to the GlobalFilters by overriding the PostInitialize of MyProjectWebModule, per instructions.

    public override void PostInitialize()
            {
                GlobalFilters.Filters.Remove(GlobalFilters.Filters.Single(f => f.Instance is AbpMvcAuthorizeFilter));
                GlobalFilters.Filters.Add(IocManager.Resolve<MyAuthorizeFilter>());
                base.PostInitialize();
            }
    

    In the debugger, I see all the code get executed. Why isn't this enough to prevent the Log in page from appearing?

    Oh yes, I do have the same employeeid in the Abp Users. The user properties User name: 123456 Full name: pnw Email address: <a href="mailto:[email protected]">[email protected]</a> IsActive: Yes Password: "1"

    Then, when I try to log in, using Log in page and 123456/1 an error dialog appears "Empty or invalid anti forgery header token". What's that??

  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi,

    Sorry for the late response. Did you manage to work this out ? If not, I will try to run your code in a test project to see and fix the error.

  • User Avatar
    0
    pnw created

    It is still a mystery.

    I don't know how to get AbpZero to realize that I'm already authenticated and look for my employee id in the Headers (every single web server in my intranet is behind WSSO so it is not possible to be unauthenticated prior to AbpZero taking over.)

    var empid = filterContext.HttpContext.Request.Headers["employeeid"];

    Then, how to bypass the Login page and tie my empid to whatever necessary records in the Abp tables. (e.g. Does my empid need to be in AbpUsers? If so, does it go in UserName field? What about the password field, which is superfluous in my case.)

    Since I don't need AbpZero for Authentication, it still would be nice to take advantage Authorization features. But maybe that too much to hope for.

  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi,

    new Claim(ClaimTypes.NameIdentifier, empid)
    

    is correct but you you have to set a valid userId instead of your empid.

    To do that, you can add an empid into AbpUsers table (<a class="postlink" href="https://aspnetzero.com/Documents/Extending-Existing-Entities">https://aspnetzero.com/Documents/Extend ... g-Entities</a>) and find the appropriate userId for the "empid" in http request.

  • User Avatar
    0
    pnw created

    By userIddo you mean the PK of AbpUsers (ID)? Before I change the schema, I tried to test it first. I created a new user by logging in as Admin and going to the Users tab of the UI.

    I then looked in AbpUsers and see that this new user has ID = 3.

    I tried "3" in

    new Claim(ClaimTypes.NameIdentifier, "3")
    

    but the Login page still displays and while the MyMvcAuthorizeFilter is in place, I cannot login with Admin or the new account via the Login page. It throws "Empty or invalid anti forgery header token".

    I've tried three claims

    new Claim(ClaimTypes.Name, "3"),
    new Claim(ClaimTypes.NameIdentifier, "3"),
    new Claim("http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider", "3")
    

    I hope I interpreted your suggestion correctly!

  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi,

    What you have done is correct, but "Empty or invalid anti forgery header token." is another problem. Can you add "DisableAbpAntiForgeryTokenValidation" attribute to Controller you want to redirect user.This is for just skipping this exception for now.

    If this works, we can take a look at anti forgery exception later on.

  • User Avatar
    0
    pnw created

    I added [DisableAbpAntiForgeryTokenValidation] to the abstract base class

    [DisableAbpAntiForgeryTokenValidation]
        public abstract class MyControllerBase : AbpController
    

    Without doing anything else, I verify that the usual login page comes up and I can still log in as admin/123qwe

    Now I uncomment the PostInitialize() filters to remove and add filters. The definition of MyMvcAuthorizeFilter is still the same as shown above.

    public override void PostInitialize()
            {
                GlobalFilters.Filters.Remove(GlobalFilters.Filters.Single(f => f.Instance is AbpMvcAuthorizeFilter));
                GlobalFilters.Filters.Add(IocManager.Resolve<MyMvcAuthorizeFilter>());
                base.PostInitialize();
            }
    

    Now when I run, the login page still comes up and I can log in as admin\123qwe. The home page starts to render, but an Abp error dialog "An error has occurred!" pops up. Then I notice that the upper right user badge shows {{vm.getShownUserName()}} instead of '.\admin' and view isn't getting rendered.

    This is really frustrating. All I want to do is programmatically log in so that the Log in page doesn't appear. Should I just rip AbpZero out of my solution and just do plain old claims based authentication and authorization?

  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi,

    Sorry for the delay, I had a chance to take a look at this today. I have figured out how to do this. And this problem is not totaly related to ABP or ABP Zero.

    I have followed the steps in this nice article <a class="postlink" href="https://dotnetcodr.com/2015/11/16/wiring-up-a-custom-authentication-method-with-owin-in-web-api-part-1-preparation/">https://dotnetcodr.com/2015/11/16/wirin ... eparation/</a>, you can read this article to understand how this works. You can skip the first part. It's just for creating an empty project.

    This article explains how to create and use a custom owin authentication middleware. The previous approach I suggested using a custom AuthorizeFilter was wrong, you can delete it.

    And these are the changes I have made this to work.

    1. I have created below classes in my project.
    public class MyAuthenticationHandler : AuthenticationHandler<HttpHeaderBasedAuthenticationOptions>
    {
        protected override Task<AuthenticationTicket> AuthenticateCoreAsync()
        {
            bool authorized = true;
            if (authorized)
            {
                AuthenticationProperties authProperties = new AuthenticationProperties();
                authProperties.IssuedUtc = DateTime.UtcNow;
                authProperties.ExpiresUtc = DateTime.UtcNow.AddDays(1);
                authProperties.AllowRefresh = true;
                authProperties.IsPersistent = true;
    
                IList<Claim> claimCollection = new List<Claim>
                {
                    new Claim(ClaimTypes.NameIdentifier, "2"),
                    new Claim("http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider", "2"),
                    new Claim(AbpClaimTypes.TenantId, "1"),
                    new Claim(ClaimTypes.Name, "admin"),
                    new Claim(ClaimTypes.Surname, "admin"),
                    new Claim(ClaimTypes.Email, "[email protected]"),
                };
    
                ClaimsIdentity claimsIdentity = new ClaimsIdentity(claimCollection, "Custom");
                AuthenticationTicket ticket = new AuthenticationTicket(claimsIdentity, authProperties);
                return Task.FromResult(ticket);
            }
    
            return null;
        }
    }
    
    public class HttpHeaderBasedAuthenticationOptions : AuthenticationOptions
    {
        public HttpHeaderBasedAuthenticationOptions() : base("x-company-auth")
        {
            
        }
    }
    
    public class HttpHeaderBasedAuthMiddleware : AuthenticationMiddleware<HttpHeaderBasedAuthenticationOptions>
    {
        public HttpHeaderBasedAuthMiddleware(OwinMiddleware nextMiddleware, HttpHeaderBasedAuthenticationOptions authOptions)
            : base(nextMiddleware, authOptions)
        { }
    
        protected override AuthenticationHandler<HttpHeaderBasedAuthenticationOptions> CreateHandler()
        {
            return new MyAuthenticationHandler();
        }
    }
    
    public static class HttpHeaderAuthenticationExtension
    {
        public static void UseHttpHeaderBasedAuthentication(this IAppBuilder appBuilder)
        {
            appBuilder.Use<HttpHeaderBasedAuthMiddleware>(new HttpHeaderBasedAuthenticationOptions());
        }
    }
    

    After creating these classes, in Startup.cs add this line

    app.UseHttpHeaderBasedAuthentication();
    

    And remove the line which starts with

    app.UseCookieAuthentication.....
    

    If you dont want user's to see Login page, you have to manually check AbpSession.UserId and if it's bigger than 0, redirect user to application. Or in your case, since you dont need it you can just delete it.

    Please let me know of your progress.