I am struggling to build a secure React web app with Net 5.0 backend (all in the same project) and would like some advice.
All within the same Visual Studio project.
Eg:
Project
ClientApp (React)
Controllers (C# endpoints eg /api/data, api/filtereddata)
Program.cs
Startup.cs
(Implementation #1)
I can make the front-end login using the 'react-google-login' npm package. That works well but it doesn't protect the endpoints in the controllers (/api/data).
<div>
<GoogleLogin
clientId={clientId}
buttonText="Login with Google"
onSuccess={onSuccess}
onFailure={onFailure}
cookiePolicy={'single_host_origin'}
style={{ marginTop: '100px' }}
isSignedIn={true}
/>
</div>
I have also discovered I can verify that google token on the server by using something akin to:
const onSuccess = (res) => {
const tokenBlob = { tokenId: res.tokenId };
axios.post(`/api/auth/google`, tokenBlob)
.then(res => {
})
.catch(function (error) {
console.log("axois error", error);
})
};
[Route("/api/[controller]")]
public class AuthController : Controller
{
[AllowAnonymous]
[HttpPost("google")]
public IActionResult Google([FromBody] GoogleToken t)
{
var payload = GoogleJsonWebSignature.ValidateAsync(t.tokenId, new GoogleJsonWebSignature.ValidationSettings()).Result;
}
}
But it seems awkward to do this for every API call that the UI makes.
(Implementation #2)
I have tried doing this all in the backend (Microsoft.AspNetCore.Authentication.Google), that sort of worked with AddGoogle & AddCookie, that would re-direct to a Google login page when I tried to get data from the backend (via [Authorize]) - but I could not get React to notice that it was logged in/out.
// ConfigureServices
services.AddCors(options =>
{
options.AddDefaultPolicy(builder => {
builder.WithOrigins("https://localhost","https://accounts.google.com")
.AllowAnyHeader().AllowAnyMethod();
});
});
services.AddControllers().AddNewtonsoftJson();
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(o =>
{
o.LoginPath = "/signin";
o.LogoutPath = "/signout"; // ??
o.ExpireTimeSpan = TimeSpan.FromHours(1);
})
.AddGoogle(options =>
{
options.ClientId = "";
options.ClientSecret = "";
options.SaveTokens = true;
options.CallbackPath = "/signin-google";
});
// Configure
app.UseCors();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseSpaStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints => {
endpoints.MapControllers();
});
app.UseSpa(spa => {
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment()) {
spa.UseReactDevelopmentServer(npmScript: "start");
}
});
// Home controller
public class HomeController : Controller {
[Route("/signin")]
public IActionResult SignIn() {
var authProperties = new AuthenticationProperties { RedirectUri = "/"
};
return new ChallengeResult(GoogleDefaults.AuthenticationScheme, authProperties);
}
[Authorize]
[Route("/signout")]
[HttpPost]
public async Task<IActionResult> Logout()
{
await HttpContext.SignOutAsync();
return Ok("Logged out");
}
}
// Api controller
[Authorize]
[Route("api/[controller]")]
public class DataController : Controller
{
private readonly ILogger<DataController> _logger;
public MatchListController(ILogger<DataController> logger)
{
_logger = logger;
}
[HttpGet]
public ResponseViewModel Get([AllowNull] DateTime? d) => new ResponseViewModel(matchDate ?? DateTime.UtcNow)?.Result;
}
The second implementation will re-direct to Google and require the login but React doesn't know the page is logged in. So how can it get the logged info to display the username etc?
So what is the best practice? Am I close? I feel close!
What I'd love to see would be an example of the WeatherForecast React template in visual studio with a working Google login that uses [Authorize] on the API data controller.
Any suggestions welcome.
Edit: Added some code
Related
EDIT (Thank #possum)
The code under work properly.
TIMEWASTER ALERT
In the option of .AddGoogle(), you can specify a custom callback. This is the callback used in the authentication mechanism between .AddGoogle and the frontend.
In other words, this callback is "internal" and not make by us.
The callback enpoint must are specify in ConfigureExternalAuthenticationProperties("Google", callback url, endpoint after user signin)
I have some issue to make functional a very simple function! Signin on my backend with google (and some other provider).
Before start, you have all of my apologize, the solution to my issue is probably evident for anyone with a good experience on react. It's my first project with react.
So I've read some articles on how integrate Google signin/signup on a backend with NET core and React in frontend. And easy I've read issues found here ...
The code in controller :
[HttpPost("google-login")]
[AllowAnonymous]
[OpenApiOperation("Login / Signup by an external provider.", "Connection with Google, Microsoft, etc")]
public IActionResult GoogleLogin()
{
var redirectUrl = $"https://localhost:5001/api/authentication/google-login-callback";
var properties = _authenticationService.ConstructHandleForGoogleLogin("Google", redirectUrl);
return Challenge(
properties,
"Google");
}
[HttpGet("google-login-callback")]
[AllowAnonymous]
[OpenApiOperation("Callback of agent of login provider.", "Connection with Google, Microsoft, etc")]
public async Task<ActionResult> GoogleLoginCallback()
{
var authenticateResult = await HttpContext.AuthenticateAsync(IdentityConstants.ExternalScheme);
throw new Exception();
}
The code in authentication service :
public AuthenticationProperties ConstructHandleForGoogleLogin(string provider, string redirectUrl)
{
if (provider.ToLowerInvariant() != "google")
throw new InvalidOperationException("Bad provider requested, this is only for Google");
var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl);
properties.AllowRefresh = true;
return properties;
}
The code for the configuration :
internal static class Startup
{
private static readonly ILogger _logger = Log.ForContext(typeof(Startup));
internal static IServiceCollection AddGoogleAuthentication(this IServiceCollection services, IConfiguration config)
{
_logger.Information("Third partie authentication enabled : {0}", "Google");
services.AddOptions<AuthenticationGoogleSettings>()
.BindConfiguration($"SecuritySettings:{nameof(AuthenticationGoogleSettings)}")
.ValidateDataAnnotations()
.ValidateOnStart();
services
.AddAuthorization()
.AddAuthentication(authentication =>
{
authentication.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
authentication.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddCookie()
.AddGoogle(googleOptions =>
{
googleOptions.ClientId = config["SecuritySettings:AuthenticationGoogleSettings:ClientId"];
googleOptions.ClientSecret = config["SecuritySettings:AuthenticationGoogleSettings:ClientSecret"];
//googleOptions.CallbackPath = "/api/authentication/google-login-callback"; <- TIMEWAST ALERT
googleOptions.AuthorizationEndpoint += "?prompt=consent";
googleOptions.AccessType = "offline";
googleOptions.Scope.Add("profile");
googleOptions.SignInScheme = IdentityConstants.ExternalScheme;
});
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.Strict;
});
return services;
}
}
The code in Frontend :
<form method='POST' action={`https://localhost:5001/api/authentication/google-login`} >
<IconButton
type='submit'
name='provider'
value='Google'>
<Iconify icon="eva:google-fill" color="#DF3E30" />
</IconButton>
</form>
I have successfully implemented Single Sign on with Azure Ad and fetched profile using MS Graph API but when I try to consume my dot net web API it is showing me error Unauthorized(401)
May be I am missing something in configuration,
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "example.onmicrosoft.com",
"ClientId": "392xxxx2-bxx4-4xxf-axxc-505bd9c6d8b4",
"TenantId": "06xxx2xbe-9xxe-4xx8-bxxd-e1a6ebxxxxd",
"scopes": "api://3xxxxe52-bxx4-4xxf-axx2c-505bxxxxb4/User.Read"
}
here is my Startup.cs code
using AutoMapper;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.OpenApi.Models;
using TMS.API.Configuration;
using TMS.DAL.Configuration;
using TMS.DAL.Mapper;
using Microsoft.Identity.Web;
namespace TMS.API
{
public class Startup
{
public Startup(IConfiguration configuration) => Configuration = configuration;
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddMicrosoftIdentityWebApi(Configuration, "AzureAd");
services.AddControllers();
services.AddSwaggerGen(s =>
{
s.SwaggerDoc("v1", new OpenApiInfo() { Title = "TMS API", Version = "V1" });
});
services.AddAutoMapper(typeof(Startup));
services.Configure<Setting>(Configuration.GetSection("Settings"));
services.RegisterEngineServices();
services.RegisterRepositories();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
app.UseCors(x => x
.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.WithHeaders()
.WithExposedHeaders());
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "TMS v1");
});
}
private static void AddAutoMapper(IServiceCollection services)
{
var mapperConfig = new MapperConfiguration(mc =>
{
mc.AddProfile(new BSRMapperClass());
});
IMapper mapper = mapperConfig.CreateMapper();
services.AddSingleton(mapper);
}
}
}
Controller:
[Route("api/[controller]")]
[ApiController]
[RequiredScope(RequiredScopesConfigurationKey ="AzureAd:scopes")]
public class GeneralController : ControllerBase
{
private readonly IConfiguration _configuration;
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly IGeneralService _generalService;
public GeneralController(IGeneralService generalService, IConfiguration configuration, IHttpContextAccessor httpContextAccessor)
{
_generalService = generalService;
_configuration = configuration;
_httpContextAccessor = httpContextAccessor;
}
[Authorize(Roles ="Admin")]
[Route("[action]")]
[HttpGet]
public async Task<DataTransfer<IEnumerable<MonthResponseModel>>> GetMonthList()
{
return await _generalService.GetMonthList();
}
React config file:
export const msalConfig = {
auth: {
clientId: "ddxxxx8-xxf-4xxd-bxx2-a4xxxxxd6c",
authority: "https://login.microsoftonline.com/061f82be-9xxe-4xx8-bdad-e1xxxxxb6d", // This is a URL (e.g. https://login.microsoftonline.com/{your tenant ID})
redirectUri: "http://localhost:3001",
},
cache: {
cacheLocation: "sessionStorage", // This configures where your cache will be stored
storeAuthStateInCookie: false, // Set this to "true" if you are having issues on IE11 or Edge
}
};
export const apiConfig = {
uri: "https://example.azurewebsites.net/api", // e.g. http://localhost:5000/api
scopes: ["api://3xxxx2-bxx4-4xxf-a72c-505xxxxx8b4/User.Read"] // e.g. ["scp1", "scp2"]
};
// Add scopes here for ID token to be used at Microsoft identity platform endpoints.
export const loginRequest = {
scopes: ["User.Read"]
};
// Add the endpoints here for Microsoft Graph API services you'd like to use.
export const graphConfig = {
graphMeEndpoint: "https://graph.microsoft.com/v1.0/me"
};
Here we have:
Client(React) App Essentials
Server(.net web API) App Essentials
I have exposed my API and added scope and authorized client Application:
Exposed API
I need help as I have been stuck in this issue from a couple of days and Kindly do let me know where to add Users in Client App or in Server app?
Issue Fixed!
In appsetting I changed "scopes": "api://3xxx52-bxx4-40xf-axxc-505xxxx8b4/User.Read" to "scopes": "User.Read"and reorder the middleware in Configure Method (Startup file):
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Loreal TMS v1");
});
app.UseHttpsRedirection();
//app.UseStaticFiles();
app.UseCors(x => x
.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
);
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
I have a blazor webassembly project that gets created from https://github.com/Azure-Samples/ms-identity-blazor-wasm/tree/main/WebApp-graph-user/Call-MSGraph.
Its basically the project that gets created when you use the .net core template for a Blazor application that uses authentication via AD B2B
dotnet new blazorwasm -au SingleOrg --client-id "{CLIENT ID}" -o {APP NAME} --tenant-id "{TENANT ID}"
I was then able to call graph.api when the user logged in. I then tried to call my own Api with that authentication as described in https://learn.microsoft.com/en-us/aspnet/core/blazor/security/webassembly/additional-scenarios?view=aspnetcore-3.1.
I used
builder.Services.AddHttpClient<ITestDataService, TestDataService>(
client => client.BaseAddress = new Uri("https://localhost:44342/"))
.AddHttpMessageHandler(x =>
{
var handler = x.GetRequiredService<AuthorizationMessageHandler>()
.ConfigureHandler(new[] { "https://localhost:44342/" },
scopes: new[] { "https://graph.microsoft.com/User.Read" });
return handler;
});
I can see that a token is attached when calling the Api but authentication fails (401). The api is generated from Visual Studio templates for B2B AD and uses the configuration that is also used for the Blazor application.
This is its Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(AzureADDefaults.BearerAuthenticationScheme)
.AddAzureADBearer(options => Configuration.Bind("AzureAd", options));
services.AddControllers();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseCors(policy =>
policy.WithOrigins("http://localhost:5000",
"https://localhost:5001")
.AllowAnyMethod()
.WithHeaders(HeaderNames.ContentType,
HeaderNames.Authorization,
"x-custom-header")
.AllowCredentials());
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
Do you have any idea what is missing?
The complete source is available at github https://github.com/mathiasfritsch/blazor-calls-api
If you want to call Microsoft graph and your custom API in one blazor webassembly project, we can implement it by creating different HTTP client to call different API
For example
Register a server API app
Register an AAD app for the Server API app
Expose an API
Register a client app
Register a client app
Enable Implicit grant flow
Add API permissions. (Graph API permissions and API app permissions)
Configure API app
Please add the following code in Startup.cs
public void ConfigureServices(IServiceCollection services)
{
JwtSecurityTokenHandler.DefaultMapInboundClaims = false;
services.AddCors(options =>
{
options.AddDefaultPolicy(
builder => builder.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod());
});
services.AddAuthentication(AzureADDefaults.BearerAuthenticationScheme)
.AddAzureADBearer(options => Configuration.Bind("AzureAd", options));
services.Configure<JwtBearerOptions>(AzureADDefaults.JwtBearerAuthenticationScheme, options =>
{
options.Authority += "/v2.0";
options.TokenValidationParameters = new TokenValidationParameters
{
ValidIssuers = new[] {
$"https://sts.windows.net/{Configuration["AzureAD:TenantId"]}/",
$"https://login.microsoftonline.com/{Configuration["AzureAD:TenantId"]}/v2.0"
},
RoleClaimType = "roles",
// The web API accepts as audiences both the Client ID (options.Audience) and api://{ClientID}.
ValidAudiences = new[]
{
options.Audience,
$"api://{options.Audience}"
}
};
});
....
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.OAuthClientId(Configuration["Swagger:ClientId"]);
c.OAuthScopeSeparator(" ");
c.OAuthAppName("Protected Api");
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
});
app.UseHttpsRedirection();
app.UseRouting();
app.UseCors();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
Configure Client APP
Create custom AuthorizationMessageHandler for Graph API and custom API
// custom API
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
public class CustomAuthorizationMessageHandler : AuthorizationMessageHandler
{
public CustomAuthorizationMessageHandler(IAccessTokenProvider provider,
NavigationManager navigationManager)
: base(provider, navigationManager)
{
ConfigureHandler(
authorizedUrls: new[] { "https://localhost:44300/" },
scopes: new[] { "the API app scope" });
}
}
//Graph API
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
public class GraphAuthorizationMessageHandler : AuthorizationMessageHandler
{
public GraphAuthorizationMessageHandler(IAccessTokenProvider provider,
NavigationManager navigationManager)
: base(provider, navigationManager)
{
ConfigureHandler(
authorizedUrls: new[] { "https://graph.microsoft.com/" },
scopes: new[] { "https://graph.microsoft.com/User.Read" });
}
}
Add the following code to the program.cs
public class Program
{
public static async Task Main(string[] args)
{
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("app");
builder.Services.AddScoped<CustomAuthorizationMessageHandler>();
builder.Services.AddScoped<GraphAuthorizationMessageHandler>();
// register HTTP client to call our own api
builder.Services.AddHttpClient("MyAPI", client => client.BaseAddress = new Uri("https://localhost:44300/"))
.AddHttpMessageHandler<CustomAuthorizationMessageHandler>();
// register HTTP client to call graph api
builder.Services.AddHttpClient("GraphAPI", client => client.BaseAddress = new Uri("https://graph.microsoft.com/"))
.AddHttpMessageHandler<GraphAuthorizationMessageHandler>();
builder.Services.AddMsalAuthentication(options =>
{
builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
options.ProviderOptions.DefaultAccessTokenScopes.Add("<the API app scope>");
options.ProviderOptions.AdditionalScopesToConsent.Add("https://graph.microsoft.com/User.Read");
});
await builder.Build().RunAsync();
}
}
Call the api
#inject IHttpClientFactory _clientFactory
var httpClient = _clientFactory.CreateClient("<the client name you register>");
await apiClient.GetStringAsync("path");
I am having trouble getting my .NET Core site to redirect to my identity server for authentication when accessing a page. When I run the site locally this works fine. However, when I deploy the site it no longer works.
Here is the code for the application startup (just for the authentication)
public void ConfigureServices(IserviceCollection services) {
services
.AddAuthentication(options => {
options.DefaultScheme => "Cookies";
options.DefaultChallengeScheme = "oidc";
})
.AddCookie("Cookies")
.AddOpenIdConnect("oidc", options => {
options.Authority = "IdentityServerUrl";
options.ClientId = "clientId";
options.ResponseType = "code";
options.SaveTokens = true;
options.Scope.Add("scope");
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints => endpoints.MapRazorPages().RequireAuthorization());
}
And then in our page:
public class IndexModel : PageModel {
public async Task OnGetAsync() {
var authentication = await this.HttpContext.AuthenticateAsync();
var accessTokenJwt = authentication.Properties.Items[".Token.access_token"];
}
}
This is all working correctly when run locally. It is successfully redirecting to the identity server, logging in, returning to the application, and setting the cookies.
However, when this is deployed to a webserver it is not working at all. The await this.HttpContext.AuthenticateAsync(); is called and just immediately returns null. This results in an exception being thrown on the line below.
Any help with this would be much appreciated. Thanks in advance.
I have setup an dotnet angular project and then implemented authentication as follows in the StartUp.cs file.
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IPasswordHasher<CustomUser>,
PasswordHasherWithOldMembershipSupport<CustomUser>>();
services.AddIdentity<CustomUser, IdentityRole>()
.AddEntityFrameworkStores<AuthenticationContext<CustomUser>>()
.AddDefaultUI()
.AddDefaultTokenProviders();
var connection = configuration.GetConnection("Authentication");
services.AddDbContext<AuthenticationContext<CustomUser>>(options =>
options.UseSqlServer(connection));
services.AddTransient<IEmailSender, AuthMessageSender>();
services.AddTransient<AuthMessageSender.ISmsSender, AuthMessageSender>();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
...
app.UseAuthentication();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller}/{action=Index}/{id?}");
});
...
}
IdentityHostingStartUp.cs file which runs upon startup to configure authentication.
public class IdentityHostingStartup : IHostingStartup
{
public void Configure(IWebHostBuilder builder)
{
builder.ConfigureServices((context, services) =>
{
services.AddAuthentication();
services.Configure<IdentityOptions>(options =>
{
// Password settings.
options.Password.RequireDigit = true;
options.Password.RequireLowercase = true;
options.Password.RequireNonAlphanumeric = true;
options.Password.RequireUppercase = true;
options.Password.RequiredLength = 6;
options.Password.RequiredUniqueChars = 1;
// Lockout settings.
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
options.Lockout.MaxFailedAccessAttempts = 5;
options.Lockout.AllowedForNewUsers = true;
// User settings.
options.User.RequireUniqueEmail = false;
});
services.ConfigureApplicationCookie(options =>
{
// Cookie settings
options.Cookie.HttpOnly = true;
options.ExpireTimeSpan = TimeSpan.FromMinutes(15);
options.LoginPath = "/Identity/Account/Login";
options.AccessDeniedPath = "/Identity/Account/AccessDenied";
options.SlidingExpiration = true;
});
});
}
}
I have a custom redirect in my angular code to go to the authenticate page if the user is not logged in.
import { Inject, Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
#Injectable()
export class AuthService {
constructor(http: HttpClient, #Inject('BASE_URL') baseUrl: string) {
http.get<Boolean>(baseUrl + "api/Home/Status").subscribe((authenticated) => {
if (!authenticated) {
window.location.href = baseUrl + "/Identity/Account/Login";
}
});
}
And finally, my HomeController code to check the authenticated status of the logged in user.
[HttpGet("[action]"), AllowAnonymous]
public Boolean Status()
{
var user = _accessor.HttpContext.User;
return User.Identity.IsAuthenticated;
}
The Status(or any other called api controller actions) action always has a null user name, user claims, and IsAuthenticated always returns false even after logging in.
This is driving me up the wall. I have read as many post and tried as many options as I could find and nothing seems to work.
At some point, I noticed that the user name was filled as expected. I thought it was solved. However, since then it has stopped working even though I haven't changed anything and I can't solve this issue.
This happens when your Debug settings are set to EnableSSL unchecked. This would also become and issue if you were to deploy without SSL enabled. As soon as I enabled SSL as in the picture below the application identity cookie was included in the ajax requests.