CORS problem with B2C and Azure App Service - asp.net

I have enabled CORS in my ASP.NET/Angular application by using AddCors and EnableCors in controllers. My application is communicating with AD B2C pages. After build I have this error in the console :
Access to XMLHttpRequest at 'https://XXX.b2clogin.com/ (redirected
from 'https://XXX.azurewebsites.net/User/Info') from origin
'https://XXX.azurewebsites.net' has been blocked by CORS policy: No
'Access-Control-Allow-Origin' header is present on the requested
resource.
Startup.cs :
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
});
Controller :
[EnableCors("CorsPolicy")]
[Authorize]
public class UserEditController : Controller
I have enabled CORS on Azure too by putting : Allowed Origins - * .
UPDATE:
[Route("User/Info")]
[HttpGet]
public async Task<IActionResult> LoggedInUserInfo()
{
if (User.Identity.IsAuthenticated)
{
var user = ((ClaimsIdentity)User.Identity).FindFirst(ClaimTypes.NameIdentifier).Value;
var viewmodel = await userService.GetUserByObjectId(user);
return Json(new { loggedIn = "true", user = viewmodel });
}
return Json(new { loggedIn = "false" });
}

It appears that you're attempting a cross-origin request from the https://xxx.azurewebsites.net/ domain to the https://xxx.b2clogin.com/ domain.
Currently the .b2clogin.com domain doesn't allow any cross-origin requests from any other domain.

Related

ASP.NET Core 6 Web API with firebase auth and react front end bearer auth fails

I'm having some issues with an ASP.NET Core 6 Web API and a react front end using firebase auth. I get a 401 every time the react app requests an authorized endpoint (but 200 with postman).
Using ASP.NET Core 6
I know the token I am using works fine because when I request with postman using the same bearer token I get a 200 response.
I have also tried to set ValidateIssuer = false & ValidateAudience = false & ValidateLifetime = false with no luck
Front end request (when the user logs in via the firebase/auth signInWithEmailAndPassword method
const testFetch = async () => {
getIdToken(auth.currentUser!).then(async (token) => {
const res = await fetch('https://localhost:51437/test/private', {
method: 'GET',
headers: {
Authentication: `Bearer ${token}`,
Accept: 'application/json',
'Content-Type': 'application/json',
},
});
const result = await res.json();
console.log(result);
});
};
I can also request non authorized endpoints from my web app and get them correctly so shouldn't be anything to do with cors
Adding JWT bearer auth scheme:
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(opt =>
{
opt.IncludeErrorDetails = true;
opt.Authority = $"https://securetoken.google.com/{builder.Configuration["Firebase:ID"]}";
opt.TokenValidationParameters = new TokenValidationParameters {
ValidateIssuer = true,
ValidIssuer = $"https://securetoken.google.com/{builder.Configuration["Firebase:ID"]}",
ValidateAudience = true,
ValidAudience = builder.Configuration["Firebase:ID"],
ValidateLifetime = true
};
});
Setup for auth:
app.UseCors(x => x.AllowAnyMethod().AllowAnyHeader().SetIsOriginAllowed(origin => true).AllowCredentials());
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapRazorPages();
app.MapControllers();
app.Run();
Controller:
[ApiController]
[Route("[controller]")]
public class TestController : Controller
{
public IActionResult Index()
{
return Ok("Hello world");
}
[HttpGet("private")]
[Authorize]
public IActionResult Private()
{
return Ok(new
{
Message = "Hello from a private endpoint!"
});
}
}
Request logs
[00:41:14 DBG] AuthenticationScheme: Bearer was not authenticated.
info: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2]
Authorization failed. These requirements were not met:
DenyAnonymousAuthorizationRequirement: Requires an authenticated user.
Microsoft.AspNetCore.Authorization.DefaultAuthorizationService: Information: Authorization failed. These requirements were not met:
DenyAnonymousAuthorizationRequirement: Requires an authenticated user.
[00:41:14 INF] Authorization failed. These requirements were not met:
DenyAnonymousAuthorizationRequirement: Requires an authenticated user.
info: Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler[12]
AuthenticationScheme: Bearer was challenged.
Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler: Information: AuthenticationScheme: Bearer was challenged.
[00:41:14 INF] AuthenticationScheme: Bearer was challenged.
After debugging it looks like my API seems to be removing the Authorization header from my front end app which is an expo web app (react) but not when the request is from postman.
The request is sent at-least in the network tab with the correct bearer

ASP.NET CORS Exception on POST with a react app

I have a react app calling my service with CORS enabled from my local app. This works fine for GET methods but for some reason it throws a CORS exception when calling a POST method. Is there anything I need to add to configure for POST? thanks
in my startup.cs:
services.AddCors(options =>
{
options.AddDefaultPolicy(
builder =>
{
builder.WithOrigins(
"http://localhost:3000"
);
});
});
In my react app the call is pretty basic:
axios({
method: "POST",
url: `https://localhost:44340/patientsearch`,
data: { searchModel },
});
The exception:
Access to XMLHttpRequest at 'https://localhost:44340/patientsearch' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseCors(x =>
{
x.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader().WithOrigins("https://localhost:5002");
});
}

Request ignored because of CORS in IdentityServer4

I have 3 projects:
Client App
ASP.NET API App
IdentityServer4 MVC App
I am able to send a request from API to IDP but trying to send a request from Client to IDP yields
"CORS request made for path: /api/Trial/TrialAction from origin: https://localhost:44389 but
was ignored because path was not for an allowed IdentityServer CORS endpoint"
even though I added the following to the IDP:
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy", policyBuilder => policyBuilder
.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader());
});
and
// ...
app.UseRouting();
app.UseIdentityServer();
app.UseCors("CorsPolicy");
app.UseAuthorization();
// ...
The interesting part is, I can send a request from API to IDP without adding CORS configuration to IDP. What am I doing wrong?
Config.cs:
public static class Config
{
public static IEnumerable<IdentityResource> Ids =>
new IdentityResource[]
{
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
new IdentityResources.Email(),
};
public static IEnumerable<ApiResource> Apis =>
new ApiResource[]
{
new ApiResource("myapi",
"My API",
new [] { "membershipType" }
)
};
public static IEnumerable<Client> Clients =>
new Client[]
{
new Client
{
ClientId = "mywebclient",
ClientName = "My Web Client",
AllowedGrantTypes = GrantTypes.Code, // Authorization code flow with PKCE protection
RequireClientSecret = false, // Without client secret
RequirePkce = true,
RedirectUris = { "https://localhost:44389/authentication/login-callback" },
PostLogoutRedirectUris = { "https://localhost:44389/authentication/logout-callback" },
AllowedScopes = {
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email,
"albidersapi"
},
AllowedCorsOrigins = { "https://localhost:44389" },
RequireConsent = false,
}
};
}
do yo have the client and API in the same project as IdentityServer? I typically recommend that you keep them apart.
A wild guess could be to swap these two lines:
app.UseIdentityServer();
app.UseCors("CorsPolicy");
Because apparently IdentityServer captures the request to the API?
The most likely issue is that your call from your client to your API is not including the access token.
The debug log is coming from this file here. If you look at where your debug statement is originating from you will see that it is checking if the path matches any within IdentityServerOptions.Cors.CorsPaths. Here is an image of what those paths generally are from a debug service I made.
These paths are just the default information and authentication endpoints for IdentityServer4. In other words it thinks your request is unauthenticated because it likely isn't including the access token.
If you are using IdentityServer4's template logging implementation with Serilog, then you can also add this to your appsettings.json to see what the ASP.NET Core CORS middleware has to say. It will be logging after IdentityServer4's log
"Serilog": {
"MinimumLevel": {
"Default": "Debug",
"Override": {
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information",
"Microsoft.AspNetCore.Authentication": "Debug",
"Microsoft.AspNetCore.Cors": "Information",
"System": "Warning"
}
}
}
Here is what my debug log looked like when I made a request to an endpoint with a proper CORS policy, but the request didn't include its access token.
[21:05:47 Debug] IdentityServer.Hosting.CorsPolicyProvider CORS request made for path: /api/v1.0/users/{guid}/organizations from origin: https://localhost:44459 but was ignored because path was not for an allowed IdentityServer CORS endpoint
[21:05:47 Information] Microsoft.AspNetCore.Cors.Infrastructure.CorsMiddleware No CORS policy found for the specified request.
So it's not a CORS issue really. It's an access token or authentication issue. It is also possible, however, that your endpoint isn't being hit properly. However, you should be receiving a 404 on the client in addition to the log seen above.

Windows Authentication throwing off CORS?

I have an issue where I'm using windows authentication that requires a preflight request to log in. However, despite having CORS enable in my startup file the application will fail the preflight "Allow-Access-Control-Origin" requirement.
Failed to load http://localhost:1190/api/test: Response to preflight
request doesn't pass access control check: No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://localhost:8080' is therefore not allowed
access.
I'm running a SPA on localhost:8080
I have an axios POST withCredentials
function identityLogin () {
const url = BASE_URL + 'api/token'
axios.get(url, {withCredentials: true}).then(response => {
if (response.statusText === 'OK') {
.....
} else {
....
}
})
.catch(error => {
console.log(error)
....
})
}
In my startup.cs I have
app.UseCors(builder => builder
.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials()
);
app.UseMvc();
Then when I first get the windows credentials, a previous developer wrote this:
[HttpGet("api/token")]
[Authorize]
public async Task<IActionResult> Get()
{
this.Response.Headers.Add("Access-Control-Allow-Origin", "http://localhost:8080");
this.Response.Headers.Add("Access-Control-Allow-Headers", "Content-Type,Authorization");
this.Response.Headers.Add("Access-Control-Allow-Methods", "GET,PUT,POST,DELETE,OPTIONS");
this.Response.Headers.Add("Access-Control-Allow-Credentials", "true");
.........
}
Is persistent and possibly messing with the UseCORS? Is there a cookie being stored?
All I want the windows credentials for is to check a DB and then respond with a token.
**EDIT **
I specified origins with the same result.
app.UseMvc();
app.UseCors(builder => builder
.WithOrigins("http://localhost:8080")
.AllowAnyMethod()
.AllowAnyHeader(
.AllowCredentials());
ORDER MATTERS in startup.cs
app.UseCors must come before app.UseMvc:
app.UseCors(builder => builder
.WithOrigins("http://localhost:8080")
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
app.UseMvc();

Token Authentication Using Policy Base in Asp.net core / 5

I am sending Token from client side to server as
"Authorization: Bearer eyJhbGciOiJodHR......"
i want to Authorize users who have tokens
here is my code.
services.AddAuthorization(auth =>
{
auth.AddPolicy("Have", new AuthorizationPolicyBuilder()
.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
.RequireAuthenticatedUser().Build());
});
services.AddMvc(config =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
config.Filters.Add(new AuthorizeFilter(policy));
});
app.UseJwtBearerAuthentication(options =>
{
options.AutomaticAuthenticate = false;
});
Even if i turn AutomaticAuthenticate i get 500 error if false then 401 error
[Authorize(Policy ="Have")]
[HttpGet]
[Route("list")]
public IEnumerable<Products> List()
{
.......
}
For this behavior you don't need any special policy or configuration, because it's the default behavior to only allow access to authorized users. Users with no token or expired token are unauthorized and won't be able to access controllers/actions with an [Authorize] attribute.
All you need is
services.AddAuthentication();
and AuthorizeAttributes on your actions/controllers.
Policies are only here to validate conditions of authorized users, for example if the user is at at the age of 18 or older (see this answer for an example), where his birthday is one of the user's claims.
If a user is not authorized, the policy will never be validated. This means Authorize will always fail and deny access.

Resources