WebAPI returning 401 instead of 404 - asp.net-web-api-routing

...
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment() || app.Environment.EnvironmentName.ToLower() == "integration")
{
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "The API");
c.DocumentTitle = "Swagger: The API";
c.OAuthClientId("the_client");
});
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
Have an HTTP API up and running locally with Swagger turned on.
When I go to a non-existant URL I get 401 instead of 404.
I guess this is something to do with the order of the middleware, but I've guessed a number of permutations but nothing changes.
Any ideas?

Related

Exception "System.Security.Cryptography.CryptographicException" after Publishing project

Everytime I publish my Blazor Server-project to my website domain, and opening the website, this exception occurs, and there's little to no help Googling it:
And it says AppState.cs: line 21, so here's the codeline for it:
This exception is not happening under debugging localhost. When I delete localStorage from the browser on my website, and refreshing, then everything works. But I don't want my customers having this exception and having to tell them to delete the localstorage everytime I'm publishing.
My Program.cs if necessary:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor().AddCircuitOptions(options => options.DetailedErrors = true);
builder.Services.AddHttpClient();
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters.ValidateIssuerSigningKey = true; // Validér secret key for JWT
options.TokenValidationParameters.ValidateLifetime = false; // Validér ikke Lifetime på JWT
options.TokenValidationParameters.ValidateAudience = false; // Ikke validér clients(audience), fx BlazorWeb, der skal anvende IdentityServer
options.TokenValidationParameters.ValidateIssuer = false; // Ikke validér IdentityServer(issuer)
options.TokenValidationParameters.IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["JWT:Secret"])); // Sæt secret key for JWT, der bruges som adgangskode til at tilgå JWT
});
builder.Services.AddScoped<AuthService>();
builder.Services.AddScoped<AuthenticationStateProvider, AppState>();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
RewriteOptions options = new RewriteOptions();
options.AddRedirectToWww();
options.AddRedirectToHttps();
app.UseRewriter(options);
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapBlazorHub();
app.MapFallbackToPage("/_Host");
app.Run();
After many hours of research, I managed to fix it. I did the following; Added builder.Services.AddDataProtection().PersistKeysToFileSystem(new DirectoryInfo(#"c:\your\path\to\store\keys"));.
Try to set Load User Profile to true in your IIS app pool in the advanced settings.
see this answer, I hope that will help you!

SSO with openiddict

When a user logs in from site https://www.siteA.com,
an authentication cookie is recorded.
I want to read this authentication cookie from site https://www.siteB.com using User.Identity.Name.
How should I configure Program.cs(ASP.NET CORE 6.0) of Site https://www.siteA.com and Site https://www.siteB.com ?
using AuthorizationServer.Models;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<DbContext>(options =>
{
options.UseInMemoryDatabase(nameof(DbContext));
options.UseOpenIddict();
});
builder.Services.AddOpenIddict()
.AddCore(options =>
{
options.UseEntityFrameworkCore()
.UseDbContext<DbContext>();
})
.AddServer(options =>
{
options
.AllowClientCredentialsFlow();
options
.SetTokenEndpointUris("/connect/token");
options
.AddEphemeralEncryptionKey()
.AddEphemeralSigningKey();
options.RegisterScopes("api");
options
.UseAspNetCore()
.EnableTokenEndpointPassthrough();
});
builder.Services.AddHostedService<TestData>();
builder.Services.AddControllersWithViews();
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
{
options.LoginPath = "/account/login";
});
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
});
app.Run();
you can use ASP.Net Core Data Protection Keys configuring in your client Applications (i.e SiteA and SiteB).
Have a look at this.
https://github.com/openiddict/openiddict-core/issues/1109
and this
https://learn.microsoft.com/en-us/aspnet/core/security/data-protection/configuration/overview?view=aspnetcore-3.1
hope this helps

Protected web calls when token is not valid

I use .NET Core 3
I've downloaded Microsoft.Identity.Web from https://github.com/Azure-Samples/active-directory-aspnetcore-webapp-openidconnect-v2/tree/aspnetcore3
I use Azure AD to get access to my protected web API. Recently I've switch to Core 3.0 where the issue occures (On 2.2 it worked fine).
Currently when I try to call web api with invalid token I get into
JwtBearerMiddlewareDiagnostics class method
private static async Task OnAuthenticationFailedAsync(AuthenticationFailedContext context)
{
Debug.WriteLine($"99. Begin {nameof(OnAuthenticationFailedAsync)}");
// Place a breakpoint here and examine context.Exception
await s_onAuthenticationFailed(context).ConfigureAwait(false);
Debug.WriteLine($"99. End - {nameof(OnAuthenticationFailedAsync)}");
}
which is absolutely right, since the token is invalid. But after that my protected controller methods calls anyway (I call them from postman by add header with Bearer and the token).
Here's my controller:
[Route("api/Points")]
[ApiController]
[Authorize(AuthenticationSchemes = "AzureAD")]
public class InstallationPointController : ControllerBase
{
...
}
Setup for AD authorization in Startup.cs:
services.AddProtectedWebApi(Configuration,subscribeToJwtBearerMiddlewareDiagnosticsEvents:true)
.AddProtectedApiCallsWebApis(Configuration, new []{ "user.read", "offline_access" }).AddInMemoryTokenCaches();
Update
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
The order of your middleware is wrong. Routing needs to be before authentication and authorisation.
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
One observation, I noticed that you are using the scheme value AzureAD. Currently, Microsoft.Identity.Web uses OpenIdConnectDefaults.AuthenticationScheme as a default scheme (which has OpenIdConnect as its value).
If you want to use a different scheme name with Microsoft.Identity.Web, you can use the following method:
services.AddAuthentication("MyScheme")
.AddSignIn(Configuration);

Angular 6 - Add JWT bearer token to header not working

I'm trying to add the auth bearer token header while getting a comment from the asp.net core 2.2 backend in angular 6
getComment(postId: number): Observable<IComment[]>{
let headers = new HttpHeaders();
headers.append('Content-Type', 'application/json');
let authToken = localStorage.getItem('auth_token');
headers.append('Authorization', 'Bearer ' + authToken);
console.log(authToken);
return this.httpClient.get<IComment[]>('api/comment/post/' + postId, { headers });
}
This piece of code is not working. I am getting a value from console.log(authToken). When I copy the token in Postman, everything is working fine.
My login function in a service. This is working fine to, i'm getting the token from the backend.
login(login: ILogin) {
console.log(login);
return this.http
.post('api/auth/login', login)
.pipe(map((res: any) => {
localStorage.setItem('auth_token', res.auth_token);
this.loggedIn = true;
this._authNavStatusSource.next(true);
return true;
}));
}
When I remove authorization from the action in the backend, getting the comments is working fine. As you can see in the image below, the jwt token is just not being add to the header.
Postman:
Header information from chrome
You are not passing the headers in { headers } section.
Change return this.httpClient.get<IComment[]>('api/comment/post/' + postId, { headers }); to return this.httpClient.get<IComment[]>('api/comment/post/' + postId, { headers: headers });
When you say it's working fine via Postman, and that this is not a CORS issue (i.e., either CORS is enabled, or your JS is being served from the same origin as you API), I assume you're already subscribing to the returned Observable<IComment[]>.
The code above won't issue the request until there is a call somewhere that looks like this:
yourService.getComment(postId).subscribe(comments => { ... });
That will begin consuming the Observable and trigger the underlying HTTP request.

Passport and SailsJS, how to get isAuthenticated() test to pass

My code keeps failing here when the user tries to login:
isAuthenticated: function (req, res) {
if (req.isAuthenticated()) { return res.json(req.user); }
else { return res.send(401); }
},
It FAILS and I get GET http://localhost:1337/user/authenticated 401 (Unauthorized) in the console, even though the user has entered in a correct email and password.
Where in the code makes that test pass?
I have the related StackOverflow question with more info HERE.
The problem was that my frontend application has a different origin than my backend application, so the AJAX requests will not include the session cookie and req.isAuthenticated() will never return true.
Use the withCredentials options to force it.
$http({ withCredentials: true, ... })