dosnam
dosnam

Reputation: 31

Windows Authentication for Angular 5 and .NET Core 2

My front-end is running on localhost:4200 and back-end on localhost:5000 I have setup Windows Authentication on the back-end and front-end as below

Program.cs

public class Program
{
    public static void Main(string[] args)
    {
        BuildWebHost(args).Run();
    }

    public static IWebHost BuildWebHost(string[] args) =>
     WebHost.CreateDefaultBuilder(args)
        .UseContentRoot(Directory.GetCurrentDirectory())
        .UseStartup<Startup>()
        .Build();
}

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<IISOptions>(options =>
    {
        options.AutomaticAuthentication = true;
    });

    services.AddAuthentication(IISDefaults.AuthenticationScheme);

    services.AddAuthorization(options => {
            options.AddPolicy("AllUsers", policy => {
                policy.AddAuthenticationSchemes(IISDefaults.AuthenticationScheme);
                policy.RequireRole("S - 1 - 1 - 0");
            });
     });

    services.AddCors(options =>
    {
        options.AddPolicy("CorsPolicy",
            builder => builder.AllowAnyOrigin()
            .AllowAnyMethod()
            .AllowAnyHeader()
            .AllowCredentials());
    });

   var mvcBuilder = services.AddMvc();
   mvcBuilder.AddJsonOptions(opts => opts.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver());
  services.AddAutoMapper(typeof(Startup));
  services.AddSingleton<IConfigurationRoot>(_config);
  services.AddRouting();
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    loggerFactory.AddConsole();


    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();

    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
    }

    app.UseStaticFiles();
    DefaultFilesOptions options = new DefaultFilesOptions();
    options.DefaultFileNames.Clear();
    options.DefaultFileNames.Add("index.html");
    app.UseDefaultFiles(options);

    app.UseAuthentication();

    app.UseCors("CorsPolicy");


    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{id?}");
    });

    // Route all unknown requests to app root
    app.Use(async (context, next) =>
    {
        await next();

        // If there's no available file and the request doesn't contain an extension, we're probably trying to access a page.
        // Rewrite request to use app root
        if (context.Response.StatusCode == 404 && !Path.HasExtension(context.Request.Path.Value))
        {
            context.Request.Path = "/index.html"; // Put your Angular root page here 
            context.Response.StatusCode = 200; // Make sure we update the status code, otherwise it returns 404
            await next();
        }
    });

}

On the controller,

[Authorize]
[Route("/api/service/testWinAuth")]
[EnableCors("CorsPolicy")]
public class TestWinAuth : Controller
{
 ....
}

In the method within this controller I have,

[Route("/{id}/{withVoids?}")]
[HttpGet]
[Authorize]
public Object testMethod(Int64? id, String withVoids)
{
    var userId = HttpContext.User.Identity.Name;

}

launchSettings.json

{
  "iisSettings": {
    "windowsAuthentication": true,
    "anonymousAuthentication": false,
    "iisExpress": {
      "applicationUrl": "http://localhost:5000/",
      "sslPort": 0
    }
  },
  "profiles": {
    "IIS Express": {
      "commandName": "IISExpress",
      "launchBrowser": true,
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    },
    "webapi": {
      "commandName": "Project",
      "launchBrowser": true,
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      },
      "applicationUrl": "http://localhost:5001/"
    }
  }
}

On the front-end side, I have the following settings:

proxy.conf.json

{
  "/api": {
    "target": "http://localhost:5000",
    "secure": false
  }
}

The http request is set up to use the option withCredentials = true

get(url: string, options?: RequestOptionsArgs): Observable<Response> {
  if (!options) {
    const headers = new Headers({ 'Content-Type': 'application/json' });
    options = new RequestOptions({ headers: headers, withCredentials: true });
  }
  // noinspection TypeScriptUnresolvedFunction
  return super.get(url, options)
    .map(r => r)
    .catch((error: any): Observable<Response> =>
      this.errorDisplayAndRedirect(error));
}

When accessing the URL localhost:4200, it asks for username and password and displays the page after authenticating. When I click on a button which sends a Get request, it asks for authentication again and this time it does not authenticate and I get 401 Unauthorized error. But, When I access the back-end directly using the URL, it asks for username and password and authenticates as expected. How do I correctly pass authentication information from front-end to back-end?

Upvotes: 3

Views: 6620

Answers (1)

DBallantyne
DBallantyne

Reputation: 41

After having read in the related article: implementing windows authentication in an angular application and a stand-alone-web-api - that "You can’t use SupportsCredentials as true along with * for the origins.", I notice that the following line of code contradicts the above point.

services.AddCors(options =>
{
    options.AddPolicy("CorsPolicy",
        builder => builder.AllowAnyOrigin()
        .AllowAnyMethod()
        .AllowAnyHeader()
        .AllowCredentials());
});

I.e. while you are allowing credentials, you are also allowing any origin.

Have you considered replacing the .AllowAnyOrigin(), with say, .WithOrigins("http://localhost:4200"), or something similar?

Keep in mind that I did not use your code, but was having the same issue, but after providing a specific origin, it started working for me.

Good luck.

Upvotes: 2

Related Questions