Base solution for your next web application

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!

Hello,

I am experiencing a strange error when trying to log in with IE/Edge, in the UI nothing happens and when I look at the console I see a "Unexpected authenticateResult!". I put a breakpoint in "login.service.ts" inside the subscribe function of the authenticate call:

this._tokenAuthService
    .authenticate(this.authenticateModel)
    .finally(finallyCallback)
    .subscribe((result: AuthenticateResultModel) => {
    ->    this.processAuthenticateResult(result, redirectUrl);
    });

In Chrome the result is populated but in IE/Edge the result is empty (all properties are undefined). What I also saw is that in IE/Edge I see an additional GET call to <a class="postlink" href="http://localhost:22742/api/TokenAuth/Authenticate">http://localhost:22742/api/TokenAuth/Authenticate</a> with remark "(from cache)". Looking at the timings it seems as if the GET (cache) call is fired at exactly the same time as the POST call and finishes (of course if it's really from cache) before the POST returns. This would explain why the result is empty if it is taken from some unknown cached result. I checked the headers and all the headers that should prevent caching have been applied:

Accept: application/json
Accept-Encoding: gzip, deflate
Accept-Language: en-CH, en; q=0.5
Cache-Control: no-cache
Connection: Keep-Alive
Content-Length: 153
Content-Type: application/json
Expires: Sat, 01 Jan 2000 00:00:00 GMT
Host: localhost:22742
Origin: http://localhost:4200
Pragma: no-cache
Referer: http://localhost:4200/account/login
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 Edge/16.16299
X-Requested-With: XMLHttpRequest

So why this cached GET? I am a bit lost, of course IE/Edge is the corporate default browser. Any ideas on what this could be?

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?

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

Showing 1 to 8 of 8 entries