Base solution for your next web application
Open Closed

Recommended way to achieve seamless Windows Authentication? #4787


User avatar
0
bomaint created

Hello,

first of all: great work on ASP.NET Zero.

I am using the ASP.NET Core (.NetFramework) & Angular Template for an Intranet Web Application in a corporate environment and the idea is to use some kind of seamless Windows Authentication. What is currently the best way to achieve this with this template?

I searched the forum a bit and saw that there was support for federation services in earlier versions but that seems gone now. I also had a look at IdentityServer4 and while this seems like a possible approach, it also seems like it has beed added as an additional feature to use with external clients and not to replace the existing authentication structure (could not find anything related to IdentityServer4/OpenId in the Account/Login part of the Angular frontend and also the whole UI part for IdentityServer4 seems to be missing).

Any help or information would be very welcome on how I can achieve some kind of Windows Authentication with the least effort (and changes to existing code).

Thanks


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

    Enable Windows Authentication and then try

    HttpContext.Current.User.Identity.Name
    

    launchSettings.json

    "iisSettings": {
        "windowsAuthentication": true,
        "anonymousAuthentication": false,
        "iisExpress": {
          "applicationUrl": "http://localhost:55962/",
          "sslPort": 0
        }
    }
    
    • <a class="postlink" href="https://docs.microsoft.com/en-us/aspnet/core/security/authentication/windowsauth?tabs=aspnetcore2x">https://docs.microsoft.com/en-us/aspnet ... pnetcore2x</a>
    • <a class="postlink" href="http://mikko.repolainen.fi/documents/aspdotnet-core-windows-authentication">http://mikko.repolainen.fi/documents/as ... entication</a>
  • User Avatar
    0
    bomaint created

    Thanks for the reply.

    Yes the server side is not so much an issue as the client - speak Angular - side. How can I get Angular to see/use these credentials? From the information in the second link I see that the addition of "{ credentials: 'include' }" to the http calls could be the answer but is there some kind of configuration to enables this application wide? Because I think it would be quite a task the chase down every http call in the client application (especially the parts not using the default API through service-proxies, like the call to '/AbpUserConfiguration/GetAll' at the start of the application etc.) I also once thought about creating an http interceptor to inject exactly this option ("{ credentials: 'include' }") but then saw that the template is not using the the new httpclient from '@angular/common/http' but the old '@angular/http' so this is also not an option.

    So how can I access the Windows Authentication information from within Angular to create a 'standard' Abp user session?

  • User Avatar
    0
    aaron created
    Support Team

    the template is not using the the new httpclient from '@angular/common/http' but the old '@angular/http'

    v5.2.0 uses HttpClient. Related commit: 5e1e9f8

  • User Avatar
    0
    alper created
    Support Team

    For ASP.Net Core 2.0 you need to follow the "Windows Authentication (HTTP.sys / IISIntegration)" section here <a class="postlink" href="https://docs.microsoft.com/en-us/aspnet/core/migration/1x-to-2x/identity-2x">https://docs.microsoft.com/en-us/aspnet ... dentity-2x</a>

    Don't forget to enable Windows Authentication on the host e.g IIS or IISExpress.

    If not enabled (by default it's enabled) - Enable CORS the documentation here is good: <a class="postlink" href="https://docs.microsoft.com/en-us/aspnet/core/security/cors">https://docs.microsoft.com/en-us/aspnet ... urity/cors</a>

    If you get errors around "Preflight checks" then you will also need to enable Anonymous Access

  • User Avatar
    0
    bomaint created

    Thanks for all the replies. I spent quite some time testing and trying to get things to work (and I am still a bit astonished that this is not a more frequent request, I can't be the first customer with such a request).

    I still need help to getting the right dots connected.

    What I have done so far:

    • Enabled Windows Authentication
    • Left Anonymous Authentication enabled because otherwhise the call to <a class="postlink" href="http://localhost:22742/AbpUserConfiguration/GetAll">http://localhost:22742/AbpUserConfiguration/GetAll</a> would fail with preflight error
    • Adjusted CORS (replacing wildcard origin and adding AllowCredentials)
    • Adjusted AuthConfigurer to use Windows Authentication Schemes as default:
    var authenticationBuilder = services.AddAuthentication(opt => {
        opt.DefaultScheme = IISDefaults.AuthenticationScheme;
        opt.DefaultAuthenticateScheme = IISDefaults.AuthenticationScheme;
        opt.DefaultChallengeScheme = IISDefaults.AuthenticationScheme;
    });
    

    With this configuration I was able to get Windows Authentication working even from Angular. I added a test button to the login page that fires:

    getUser(): Observable<string> {
        console.log('Calling getUser');
        let serviceUrl: string = `${AppConsts.remoteServiceBaseUrl}/Home/GetUser`;
        return this.http.get(serviceUrl, { responseType: 'text' })
            .map((rslt: string) => {
                return rslt;
            });
    }
    

    Using an Http Interceptor adding "withCredentials" and targeting test action "/Home/GetUser" that simply returns

    return Ok($"Authenticated: {User.Identity.Name}");
    

    I then even added the [Authorize] attribute to "http://localhost:22742/api/services/app/Session/GetCurrentLoginInformations" for testing which also worked.

    But the question now is: how do I now get a valid AbpSession that the application can use? How can a achieve an application side login with the information from Windows Authentication and where is the best place to initiate this? My guess would be the mentioned "GetCurrentLoginInformations" but how can I achieve a login there?

    Any help is really appreciated.

  • User Avatar
    0
    bomaint created

    I am able to find the existing user based on the infos provided by Windows Authentication with the UserManager but I cannot find a way to do some kind of application login (so that AbpSession gets set, access tokens get generated etc.)

    Do I have to add Windows Authentication as External Login Provider and if so how? Is there a way to extend the LogInManager so I can pass in an existing user or the Windows Authentication Principal and perform a valid application login?

  • User Avatar
    0
    bomaint created

    Ok I found a 'hacky' solution by creating my own ExternalAuthenticationSource

    Configuration.Modules.Zero().UserManagement.ExternalAuthenticationSources.Add<WinAuthSource>();
    
    public class WinAuthSource : DefaultExternalAuthenticationSource<Tenant, User>, ITransientDependency
    {
        public override string Name
        {
            get { return "Windows Authentication"; }
        }
    
        public override Task<bool> TryAuthenticateAsync(string userNameOrEmailAddress, string plainPassword, Tenant tenant)
        {
            if (plainPassword == AppConsts.WinAuthSecret)
            {
                return Task.FromResult(true);
            } else
            {
                return Task.FromResult(false);
            }
            
        }
    }
    

    And adding a new Method to the TokenAuthController:

    [HttpPost]
    [Authorize]
    public async Task<AuthenticateResultModel> WinAuthenticate()
    {
    
        if (User.Identity.IsAuthenticated && User is System.Security.Principal.WindowsPrincipal)
        {
            var userName = User.Identity.Name.Split('\\').Last();
            var usr = await _userManager.FindByNameAsync(userName);
            if (usr != null)
            {
                var loginResult = await _logInManager.LoginAsync(userName, AppConsts.WinAuthSecret, GetTenancyNameOrNull());
                if (loginResult.Result == AbpLoginResultType.Success)
                {
                    //Login!
                    var accessToken = CreateAccessToken(CreateJwtClaims(loginResult.Identity));
                    return new AuthenticateResultModel
                    {
                        AccessToken = accessToken,
                        EncryptedAccessToken = GetEncrpyedAccessToken(accessToken),
                        ExpireInSeconds = (int)_configuration.Expiration.TotalSeconds,
                        TwoFactorRememberClientToken = null,
                        UserId = loginResult.User.Id,
                        ReturnUrl = null
                    };
                }
            }
            
        }
    
        return null;
    
    }
    

    So basically WinAuthSource authenticates every request with a given secret that I inject in case a valid Windows Authentication has been found. Not really nice but it works.

    If I use a browser that has no support for Integrated Authentication a login prompt appears and if this gets canceled a 401 error is triggered because of the required authorization on the action. Is there a way to prevent the built-in error modal popup just for this case? Where is the configuration for the global error handling in Angular?

  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi @bomaint,

    You can implement a custom external auth source (IExternalAuthenticationSource) and add it to ExternalAuthenticationSources of your project. Then, you can authenticate the user via windows AD seamlessly.

    For details please check <a class="postlink" href="https://aspnetboilerplate.com/Pages/Documents/Zero/User-Management#external-authentication">https://aspnetboilerplate.com/Pages/Doc ... entication</a>. Let us know if you have any problems.

  • User Avatar
    0
    bomaint created

    I was a little bit faster ;)

    That is exactly what I did, although it is not a really polished approach I took. One thing that bothered me was when I was testing with the IIS settings:

    services.Configure<IISOptions>(iis =>
          {
              iis.AuthenticationDisplayName = "Windows";
              iis.AutomaticAuthentication = true;
          });
    

    According to the documentation "iis.AutomaticAuthentication = true" should populate the "HttpContext.User" without the explicit need of an "[Authorize]" attribute which would be perfect to avoid the 401 but during my tests it never worked.