Reputation: 41
I have a .NET 7 Web API which has authentication/authorization via JWT. Currently the app correctly blocks API requests without a JWT and allows them with it as tested via SwaggerUI and Postman; however, I am unable to send the request correctly via React.
Method sending request:
const getTopics = () => {
const url = API_ROUTES.TOPICS_GET_ALL_TOPICS;
fetch(url, {
method: "GET",
headers: {
'Authorization': `Bearer ${jwt}`
}
})
.then(res => res.json())
.then(topicsFromServer => {
console.log(topicsFromServer);
setTopics(topicsFromServer);
})
.catch((error => {
console.error(error);
}))
}
If I console log the response above, I receive the following:
Response {type: 'cors', url: 'https://localhost:7174/Account/Login?ReturnUrl=%2Ftopic%2Fget-all-topics', redirected: true, status: 405, ok: false, …}
body
:
(...)
bodyUsed
:
false
headers
:
Headers {}
ok
:
false
redirected
:
true
status
:
405
statusText
:
""
type
:
"cors"
url
:
"https://localhost:7174/Account/Login?ReturnUrl=%2Ftopic%2Fget-all-topics"
[[Prototype]]
:
Response
The response url is not an existing endpoint, but I'm guessing it is auto-generated by .NET [Authorize]
attribute.
Cors Policy in .NET:
builder.Services.AddCors(options =>
{
options.AddPolicy("CORSPolicy", builder =>
{
builder
.AllowAnyMethod()
.AllowAnyHeader()
.WithOrigins("http://localhost:3000", "https://localhost:3000");
});
});
Not sure if this helps, but this is my Swagger Gen configuration:
builder.Services.AddSwaggerGen(options =>
{
options.AddSecurityDefinition("auth0", new OpenApiSecurityScheme
{
In = ParameterLocation.Header,
Name = "Authorization",
Type = SecuritySchemeType.ApiKey
});
options.OperationFilter<SecurityRequirementsOperationFilter>();
});
If there is any other info needed please let me know. Thank you!
Edit:
After further testing, it appears that the jwt is still not being utilized in anyway. Once a user logs into their account, a cookie is then stored in the browser (SwaggerUI) or Postman, allowing them access to the API without the JWT.
The cookie being utilized:
.AspNetCore.Identity.Application=ReallyLongString; Path=/; Secure; HttpOnly; Expires=Fri, 31 Mar 2023 23:07:00 GMT;
Here is Program.cs
in case there is an issue there:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("CORSPolicy", builder =>
{
builder
.AllowAnyMethod()
.AllowAnyHeader()
.WithOrigins("http://localhost:3000", "https://localhost:3000", "https://appname.asurestaticapps.net");
});
});
// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(options =>
{
options.AddSecurityDefinition("auth0", new OpenApiSecurityScheme
{
In = ParameterLocation.Header,
Name = "Authorization",
Type = SecuritySchemeType.ApiKey
});
options.OperationFilter<SecurityRequirementsOperationFilter>();
});
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = false,
ValidateAudience = false,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidAudience = builder.Configuration["Jwt:Audience"],
ValidIssuer = builder.Configuration["Jwt:Issuer"],
IssuerSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"])
)
};
});
builder.Services.AddAuthorization();
builder.Services.AddDbContext<SdeResearchDbContext>();
builder.Services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<SdeResearchDbContext>()
.AddDefaultTokenProviders();
builder.Services.AddScoped<JwtService>();
builder.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= 8;
options.Password.RequireLowercase= true;
options.Password.RequiredUniqueChars = 6;
// User settings
options.User.AllowedUserNameCharacters =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+!";
options.User.RequireUniqueEmail= true;
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseCors("CORSPolicy");
app.UseAuthentication();
app.UseAuthorization();
app.MapControllerRoute(
name: "Topic",
pattern: "topic/*{action}"
);
app.Run();
TopicController has [Authorize]
at the top of the controller.
Upvotes: 0
Views: 129
Reputation: 9546
Here is an example of react using the axios package to do the API call against a C# API endpoint using what seem to be your variable names and endpoint routes.
import axios from 'axios';
const getTopics = () => {
return axios.get(API_ROUTES.TOPICS_GET_ALL_TOPICS, {
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Authorization: `Bearer ${jwt}`
}
}).then(response => {
console.log(response.data);
setTopics(response.data);
}).catch(error => console.error(error));
};
Upvotes: 0