Base solution for your next web application
Ends in:
01 DAYS
01 HRS
01 MIN
01 SEC

Activities of "bomaint"

Ok I should definitely enhance my search skill, just did a quick search on the open issues, missed the closed ones.... :(

Thanks!

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.

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?

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?

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.

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?

Showing 1 to 6 of 6 entries