Restrict HTTP request based on HTTP Protocol in ASP.NET Core - asp.net-core

I want to restrict the HTTP request based on the HTTP request protocol lower than 2.0 in ASP.NET Core.
Can we add any policy in the startup.cs file that will restrict the HTTP request based on the HTTP protocol?
Thanks

You can use middleware to get the protocol of the current request, and then judge whether it is lower than HTTP/2. For example:
app.Use(async (context, next) =>
{
var Protocol = context.Request.Protocol;
if (float.Parse(Protocol.Remove(0,Protocol.Length - 1)) < 2)
{
//You can use other methods to restrict requests
//If using redirection will initiate another request, please make sure that the protocol used by this request is not lower than HTTP/2
context.Response.Redirect("/Home/Error");
}
await next.Invoke();
});

Related

Get the HTTP request in CORS policy in ASP.net Core 6

I'm writing a web application with .NET 6 and I have the requirement of allowing cross origin requests from certain domains. .NET allows this by providing the AddCors method, which you can configure with something like this:
services.AddCors(x => x.AddPolicy("policy_name", policyBuilder =>
{
policyBuilder.SetIsOriginAllowed(origin =>
{
if(/* domain check logic */)
{
return true;
}
else
{
return false;
}
});
}));
However, I have a problem: there are certain endpoints in my app where I need to check the value of a certain header of the request in order to decide if the origin is allowed or not.
The problem is that in the SetIsOriginAllowed method I only have the origin as a string, I don't have the entire request. Is there a way to get the request when deciding if an origin is allowed?
FURTHER EXPLANATION ON WHY I NEED THIS: the app I'm developing is multi-tenant, meaning users from multiple organizations can call my app's APIs from their organization's domain. I need to allow the cross origin request from a certain domain only if the request is for the tenant associated to that domain. To do this, I need to read the request (there is an HTTP header in the request that specifies the tenant)
Just the SetIsOriginAllowed() server is not send the 'Vary' header. Maybe you should make this things "Allow Any Origin" in your cors policy something like
SetIsOriginAllowed(origin => true)
UPDATE
So you want to developing a multi-tenant application, you can follow like:-
services.AddCors(x => x.AddPolicy("policy_name", policyBuilder =>
{
policyBuilder.SetIsOriginAllowed(IsOriginAllowed)
.WithOrigins(corsOriginAllowed);
}));
IsOriginAllowed Function:-
private static bool IsOriginAllowed(string host)
{
var corsOriginAllowed = new[] { "teacher.com", "student.com","others.com" };
return corsOriginAllowed.Any(origin =>
Regex.IsMatch(host, $#"^http(s)?://.*{origin}(:[0-9]+)?$", RegexOptions.IgnoreCase));
}

OpenIddict support returning authorization code via GET request for postman

I have set up an Authorization Server using OpenIddict 3.1.1 (porting over an existing one that was using the older ASOS package directly). I believe I am most of the way there, because when using the client application, I am able to log in, give consent, redirect back to the client, and exchange the authorization code for an access token.
However, when I try to do the same using Postman's OAuth 2.0 authentication support, I am able to log in (and give consent), but when it completes and returns the authorization code, I receive an HTTP 403 from the https://oauth.pstmn.io/v1/callback that I am redirected to:
403 ERROR
The request could not be satisfied.
This distribution is not configured to allow the HTTP request method that was used for this request. The distribution supports only cachable requests. We can't connect to the server for this app or website at this time. There might be too much traffic or a configuration error. Try again later, or contact the app or website owner.
If you provide content to customers through CloudFront, you can find steps to troubleshoot and help prevent this error by reviewing the CloudFront documentation.
Generated by cloudfront (CloudFront)
Request ID: UAXpago6ISiqbgm9U_SVPwh96qz1qoveZWFd0Cra-2FximeWZiY2aQ==
From what I can tell, this is because OpenIddict is issuing a POST request back to the callback url. This works for my client application, but evidently is not supported by Postman.
What configuration tweak do I need to make to OpenIddict to support this in postman?
OpenIddict related config in Startup.ConfigureServices:
services.AddOpenIddict()
.AddCore(options => {
options.AddApplicationStore<ClientStore>();
options.UseEntityFramework()
.UseDbContext<OAuthServerDbContext>()
.ReplaceDefaultEntities<Client, Authorization, OAuthScope, Token, long>()
;
})
.AddServer(options => {
options.RegisterClaims();
options.RegisterScopes(OpenIddictConstants.Scopes.OpenId,
OpenIddictConstants.Scopes.Email,
OpenIddictConstants.Scopes.OfflineAccess,
OpenIddictConstants.Scopes.Profile,
"user");
// flows
options.AllowAuthorizationCodeFlow();
options.AllowRefreshTokenFlow();
options.AllowPasswordFlow();
options.AllowHybridFlow();
// implicit is used by postman
options.AllowImplicitFlow();
var serviceProvider = options.Services.BuildServiceProvider();
var oauthConstants = serviceProvider.GetRequiredService<IOptions<OAuthConstants>>().Value;
var tokenLifetimes = serviceProvider
.GetRequiredService<IOptions<OpenIdConnectServerTokenLifetimeSettings>>().Value;
// security
options.SetAccessTokenLifetime(tokenLifetimes.AccessTokenLifetime)
.SetAuthorizationCodeLifetime(tokenLifetimes.AuthorizationCodeLifetime)
.SetIdentityTokenLifetime(tokenLifetimes.IdentityTokenLifetime)
.SetRefreshTokenLifetime(tokenLifetimes.RefreshTokenLifetime);
options.SetIssuer(new Uri("https://localhost/oauth/"));
// custom handlers added here
options.AddEventHandlers();
// certificate details hidden
options.AddEncryptionCertificate(certificate);
// endpoints
options.SetAuthorizationEndpointUris("/OpenIdConnect/Authorize");
options.SetLogoutEndpointUris("/OpenIdConnect/Logout", "/Account/Logout");
options.SetRevocationEndpointUris("/OpenIdConnect/Revoke");
options.SetTokenEndpointUris("/OpenIdConnect/Token");
options.SetCryptographyEndpointUris("/OpenIdConnect/JWKDoc");
options.SetUserinfoEndpointUris("/OpenIdConnect/UserInfo");
options.UseAspNetCore()
.EnableStatusCodePagesIntegration()
.EnableAuthorizationEndpointPassthrough()
//.EnableTokenEndpointPassthrough()
.EnableLogoutEndpointPassthrough()
.EnableUserinfoEndpointPassthrough()
;
})
.AddValidation(options => {
options.UseLocalServer();
options.UseAspNetCore();
var serviceProvider = options.Services.BuildServiceProvider();
var config = serviceProvider.GetRequiredService<IConfiguration>();
options.SetClientId(config.GetValue<string>(nameof(Settings.OAuthClientId)));
options.SetClientSecret(config.GetValue<string>(nameof(Settings.ClientSecret)));
// certificate details hidden
options.AddEncryptionCertificate(certificate);
});
Postman details:
Authorization
Token Name: Redacted
Grant Type: Authorization Code
Callback URL: disabled, https://oauth.pstmn.io/v1/callback
Authorize using browser: checked
Auth URL: https://localhost/oauth/OpenIdConnect/Authorize
Access Token URL: https://localhost/oauth/OpenIdConnect/Token
Client ID: redacted, but correct
Client Secret: redacted, but correct
Scope: openid offline_access
State:
Client Authentication: Send client credentials in body
edit: The response that it sends to the postman callback URI does include the authorization code in the body, but because of the 403 response, Postman doesn't parse that out and make the follow-up request to exchange the code for the token.
There is an option that you can set to control if the authorization code is received in the URL as a query string or in the body as a post. The option is response_mode and you control that as a client.
I believe if it is not set to response_mode=form_post, then you will get the code in the URL instead.
See the details about this parameter here.

Include custom WWW-Authenticate header in 401 Unauthorised response when using Microsoft.Identity.Web

Following instructions on making MS Office connect to my Asp.NET Core Web API, I am attempting to present a login redirect to MS Office for failed authentications. Following questions and answers I am attempting to include the login redirect information in the WWW-Authenticate header property. My Web API is protected with Azure AD and the Microsoft.Identity.Web library. When the authentication fails and the middleware returns the 401 Unauthorized response, the header does include the WWW-Authenticate property but it's value is only Bearer.
Q: How can update the header information to include the necessary additional redirect information?
I have tried to implement an attribute on the API, derived from IAsyncAuthorizationFilter and access the response header in that. However the middleware already returns a 401 before this attribute is called.
I have made progress by customizing the JwtBearerOptions configuration. However this approach creates an additional header item, instead of overwriting the standard value. As a result I have now 2 KeyValuePairs for the same key in the response header, which will likely have unexpected outcomes.
In my Startup.cs:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(Configuration)
services.Configure<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme, options =>
{
var existingOnChallengeHandler = options.Events.OnChallenge;
options.Events.OnChallenge = async context =>
{
await existingOnChallengeHandler(context);
string headerInfo = context.Options.Challenge;
headerInfo += " resource=\"https://management.azure.com/\"";
context.Response.Headers.Append(HeaderNames.WWWAuthenticate, headerInfo);
};
});
The original answer put me on the right track. It turned out to be actually quite simple to do this once I knew to configure the JwtBearerOptions.Challenge property:
services.Configure<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme, options =>
{
options.Challenge = $"Bearer authorization_uri=\"{authorizationUri}\"";
}

Nginx reverse proxy - 405 POST

I have Asp.Net Core Web Api application, which uses "x-api-key" http header to authorize a person sending a request. I've setup action filter as
public void OnActionExecuting(ActionExecutingContext context)
{
// Retrieve record with specified api key
var api = dbContext.Apis
.Include(a => a.User)
.FirstOrDefault(a => a.Key.Equals(context.HttpContext.Request.Headers["x-api-key"]));
// Check if record exists
if (api is null)
{
context.Result = new UnauthorizedResult(); // short circuit and return 401
}
}
It is working as expected on both GET and POST requests without nginx proxy, however as soon as I add nginx, I receive 405 Not Allowed on POST request if api key is invalid but 401 on GET (if api key is valid filter works as expected and passes execution to controller). Here is my proxy configuration
server {
listen 80;
location / {
proxy_pass http://ctoxweb:5000;
}
}
(Both nginx and web api are setup using docker). What's the problem and how to fix that?
I managed to fix this problem, however I don't know exactly why this happens, I suppose it's somehow related to nginx not allowing any method (except for GET) on static content. My best guess is that nginx assumes that empty response body (which comes from new UnauthorizedResult()) is static, though it's clearly supplied by backend. The way to fix it is as easy as supply some object to response body, for example
if (api is null)
{
context.HttpContext.Response.ContentType = "application/json";
context.Result = new UnautorizedObjectResult("{\"info\":\"no api key header present\"}");
}

Different mechanism of HttpClient in Blazor server side and Web assembly

I want to get data from ASP.net Core API with HttpClient Factory.
I use Microsoft.Extensions.Http package like this :
// Register service in IOC containter
builder.Services.AddHttpClient<IProductService, ProductService>(option =>
{
option.BaseAddress = new Uri(""/*Base url*/);
});
// Use in service
var stream = await _httpClient.GetStreamAsync("");
When I use code in Blazor server side and works correctly. But when I use code in Blazor wasm throw Exception
Access to fetch at 'http client factory base url' from origin 'blazor wasm app url' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
No change in ASP.net core API and different results.
I use .net core 3.1 in all apps
Thanks
I activate CORS in my API Like this
// In ConfigureServices method
options.AddPolicy("OpenCors", builder =>
{
builder
.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod()
;
});
// In Configure method
app.UseCors("OpenCors");