ASP.NET Core Own middleware component - c#

How to get the second argument from HTTP Context? For example
localhost:45423/?login=login1/?password=password1
Here is what I coded but it doesn't work. It always says that I'm not logged even when I inputted the correct login and password
public class CheckData
{
private readonly RequestDelegate _next;
public CheckData(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
var login = context.Request.Query["login"];
var password = context.Request.Query["password"];
if(login != "log1" && password != "pass1")
{
context.Response.StatusCode = 403;
await context.Response.WriteAsync("Sorry, you're not logged");
}
else
{
await _next.Invoke(context);
}
}
}
P.S: I've began learning ASP.NET Core today, only login works, but not login with password

You are passing the query parameters wrongly, For multiple query parameter you should pass like localhost:45423?login=login1&password=password1

Related

Pass username into headers using httpContext asp.net core mvc

I am wanting to pass a variable into headers in my middleware. In my controller I am using username to hold the value that was entered in View. Then I was thinking if I use TempData I could be pass the value of username to my middleware class and add it to the header. I noticed that after it finally enters the if statement ( if (tempData.ContainsKey("username"))) and adds the header. When I proceed to the next page, it will return back to the middleware... it will not enter the if statement and proceed on the next line correlationId = Guid.NewGuid().ToString();`. Is this the correct way to pass the variable in the middleware and add it to the header?
Controller:
[HttpPost]
public IActionResult AddressValidate(IFormCollection form)
{
// if radio button was checked, perform the following var request.
// username
string username = form["UserName"];
TempData["username"] = username;
TempData.Keep();
string status = form["Status"];
_logger.LogInformation("Current user logged in: {#Username}", username);
......
return RedirectToAction("SecondIndex", "Second")
}
Middleware:
public class CorrelationIdMiddleware
{
private readonly ITempDataDictionaryFactory _tempDataDictionaryFactory;
private const string CorrelationIdHeaderKey = "X-Correlation-ID";
private readonly RequestDelegate _next;
private readonly ILogger _logger;
public CorrelationIdMiddleware(RequestDelegate next, ILoggerFactory loggerFactory, ITempDataDictionaryFactory tempDataDictionaryFactory)
{
_next = next ?? throw new ArgumentNullException(nameof(next));
_logger = loggerFactory.CreateLogger<CorrelationIdMiddleware>();
_tempDataDictionaryFactory = tempDataDictionaryFactory;
}
public async Task Invoke(HttpContext httpContext)
{
string correlationId = null;
string userName;
var tempData = _tempDataDictionaryFactory.GetTempData(httpContext);
if (httpContext.Request.Headers.TryGetValue(
CorrelationIdHeaderKey, out StringValues correlationIds))
{
correlationId = correlationIds.FirstOrDefault(k =>
k.Equals(CorrelationIdHeaderKey));
_logger.LogInformation("CorrelationId from Request Header: {#correlationId} ", correlationId);
}
else
{
if (tempData.ContainsKey("username"))
{
userName = tempData["username"].ToString();
httpContext.Request.Headers.Add("X-username", userName);
}
correlationId = Guid.NewGuid().ToString();
httpContext.Request.Headers.Add(CorrelationIdHeaderKey,
correlationId);
_logger.LogInformation("Generated CorrelationId: {#correlationId}", correlationId);
}
httpContext.Response.OnStarting(() =>
{
if (!httpContext.Response.Headers.
TryGetValue(CorrelationIdHeaderKey,
out correlationIds))
httpContext.Response.Headers.Add(
CorrelationIdHeaderKey, correlationId);
return Task.CompletedTask;
});
await _next.Invoke(httpContext);
}
}
Session as in this?
You should write the logic code you write into the header in httpContext.Response.OnStarting. It works for me.

How do I access browser local storage from .cs files in blazor?

First of all, I can access localstorage data in .razor pages. I mean I cannot access localstorage data in .cs files. How can I access?
_Imports.razor:
#using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage;
#inject ProtectedLocalStorage protectedLocalStorage
anyone .razor file:
await protectedLocalStorage.SetAsync(key, JsonSerializer.Serialize(instance));
Above code works for me but I want to call protectedLocalStorage from .cs files additionally.
P.S sorry for grammar mistakes
Edit:
I am using IHttpClientFactory in startup.cs and I want to add token as a header before api request.
startup.cs
services.AddHttpClient("api", hc =>
{
hc.BaseAddress = new Uri("http://localhost:5000/");
string tokenVal = tokenService.GetToken();
if (!String.IsNullOrEmpty(tokenVal))
hc.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", tokenVal);
});
I want to take token value from local storage from this .cs file
public class TokenService : ITokenService
{
private IHttpContextAccessor httpContextAccessor;
public TokenService(IHttpContextAccessor HttpContextAccessor, IProtected) => httpContextAccessor = HttpContextAccessor;
public string GetToken()
{
return "";
}
}
How do I access browser local storage from .cs files in blazor?
ASP.NET supports injection in most constructors. Expanding OP's example:
// Startup.cs -> ConfigureServices(IServiceCollection services)
// Probably not necessary in your case but, to be thorough:
services.AddScoped<ProtectedLocalStorage>();
// SomeFile.cs
public class TokenService : ITokenService
{
// Ignore for the moment that these are being used in the same context
private IHttpContextAccessor httpContextAccessor;
private readonly ProtectedBrowserStorage _storage;
// Injection can happen here in ASP.NET
public TokenService(
IHttpContextAccessor HttpContextAccessor,
ProtectedBrowserStorage storage)
{
httpContextAccessor = HttpContextAccessor;
// injection works but the PBS service might not: see below
_storage = storage;
}
//..
}
However, I don't recommend this for ProtectedBrowserStorage, since it uses IJSRuntime under the hood. If you try to use this in a non-javascript aware context (e.g. during Startup.Configure where the client is still awaiting a response and there is no way to execute javascript), you will run into errors. In Blazor, ProtectedBrowserStorage should only be called - directly or indirectly - from a Blazor component; to keep it simple, wrap it in a class you only use with components, or keep it in the component itself.
Thus, if you are trying to do this:
I am using IHttpClientFactory in startup.cs and I want to add token as a header before api request.
ProtectedBrowserStorage is not the tool for you. Use cookies or another web server technology.
How I solved in the end:
I have created custom authentication class inherited AuthenticationStateProvider. Then I designed all of check processes to be solved on ProtectedLocalStorage.
AuthenticationService
public class AuthenticationService : AuthenticationStateProvider
{
private const string USER_SESSION_OBJECT_KEY = "user_session_obj";
private const string ACCESS_TOKEN = "accesstoken";
private const string USER_PERMISSIONS = "userpermissions";
private readonly ProtectedLocalStorage _protectedLocalStorage;
private readonly IHttpContextAccessor _httpContextAccessor;
public AuthenticationService(ProtectedLocalStorage protectedSessionStore, IHttpContextAccessor httpContextAccessor)
{
_protectedLocalStorage = protectedSessionStore;
_httpContextAccessor = httpContextAccessor;
}
public string IpAddress => _httpContextAccessor?.HttpContext?.Connection?.RemoteIpAddress?.ToString() ?? string.Empty;
private User User { get; set; }
private List<UserPermission> UserPermissionList { get; set; }
public override async Task<AuthenticationState> GetAuthenticationStateAsync()
{
try
{
User userSession = await GetUserSession();
List<UserPermission> userPermissions = await GetUserPermission();
if (userSession != null)
return await GenerateAuthenticationState(userSession, userPermissions);
return await GenerateEmptyAuthenticationState();
}
catch
{
await LogoutAsync();
return null;
}
}
public async Task LoginAsync(User user,List<UserPermission> userPermissions)
{
await SetUserSession(user);
await SetUserPermissionSession(userPermissions);
NotifyAuthenticationStateChanged(GenerateAuthenticationState(user, userPermissions));
}
public async Task LogoutAsync()
{
//await SetUserSession(null);
RefreshUserSession(null);
await _protectedLocalStorage.DeleteAsync(USER_SESSION_OBJECT_KEY);
await _protectedLocalStorage.DeleteAsync(ACCESS_TOKEN);
await _protectedLocalStorage.DeleteAsync(USER_PERMISSIONS);
NotifyAuthenticationStateChanged(GenerateEmptyAuthenticationState());
}
public async Task<User> GetUserSession()
{
if (User != null)
return User;
//TODO burda localUserJson get yaparken hata alıyor. try catch işi çözmezse buraya tekrardan bakılacak.
try
{
var localUserJson = await _protectedLocalStorage.GetAsync<string>(USER_SESSION_OBJECT_KEY);
if (string.IsNullOrEmpty(localUserJson.Value))
return null;
return RefreshUserSession(JsonConvert.DeserializeObject<User>(localUserJson.Value));
}
catch
{
await LogoutAsync();
return null;
}
}
public async Task<List<UserPermission>> GetUserPermission()
{
if (UserPermissionList != null)
return UserPermissionList;
try
{
var localUserPermissionJson = await _protectedLocalStorage.GetAsync<string>(USER_PERMISSIONS);
if (string.IsNullOrEmpty(localUserPermissionJson.Value))
return null;
return RefreshUserPermissionSession(JsonConvert.DeserializeObject<List<UserPermission>>(localUserPermissionJson.Value));
}
catch
{
await LogoutAsync();
return null;
}
}
private async Task SetUserSession(User user)
{
RefreshUserSession(user);
await _protectedLocalStorage.SetAsync(USER_SESSION_OBJECT_KEY, JsonConvert.SerializeObject(user));
}
private async Task SetUserPermissionSession(List<UserPermission> userPermissions)
{
RefreshUserPermissionSession(userPermissions);
await _protectedLocalStorage.SetAsync(USER_PERMISSIONS, JsonConvert.SerializeObject(userPermissions));
}
private User RefreshUserSession(User user) => User = user;
private List<UserPermission> RefreshUserPermissionSession(List<UserPermission> userPermission) => UserPermissionList = userPermission;
private Task<AuthenticationState> GenerateAuthenticationState(User user, List<UserPermission> userPermission)
{
ClaimsIdentity claimsIdentity = new ClaimsIdentity(new[]
{
new Claim(ClaimTypes.Name, user.Id.ToString()),
new Claim(ClaimTypes.Role, userPermission.ToString()),
}, "auth");
ClaimsPrincipal claimsPrincipal = new ClaimsPrincipal(claimsIdentity);
return Task.FromResult(new AuthenticationState(claimsPrincipal));
}
private Task<AuthenticationState> GenerateEmptyAuthenticationState() => Task.FromResult(new AuthenticationState(new ClaimsPrincipal()));
}
Then I registered this class in startup.cs
Startup
services.AddScoped<AuthenticationStateProvider, AuthenticationService>();
During changing page, authentication system interrupt showing page to check if it is authenticate or not thanks to below code.
_Imports
#attribute [Authorize]
*You can set localstorage at login page. You can create your way to check thanks to this way.

Load JWT into User.Identity automatically (ASP.NET Core 3.1)

I am working on a project with micro-services's architecture. I have one API Rest that works as an API Gateway where I want to get the username from a JSON Web Token. It may be important to note that the authentication and authorization of the system are being dealt with on another part of the system (I just need to get the username).
So far I was able to get the username from the JWT using this extension of HttpContext:
public static class HttpContextExtensions
{
private static JwtSecurityToken GetJwt(this HttpContext httpContext)
{
bool existeToken = httpContext.Request.Headers.TryGetValue("Authorization", out StringValues authorizationValue);
if (!existeToken)
{
return null;
}
string encodedToken = authorizationValue.FirstOrDefault().Split(" ", 2)[1];
return new JwtSecurityTokenHandler().ReadJwtToken(encodedToken);
}
public static string GetUsername(this HttpContext httpContext)
{
JwtSecurityToken jwt = httpContext.GetJwt();
Claim usernameClaim = jwt.Claims.FirstOrDefault(c => c.Type == "preferred_username");
return usernameClaim?.Value;
}
}
And then I can get the username on the controller:
string username = HttpContext.GetUsername();
I was wondering if there is a more elegant way to do this on .Net Core 3.1. I tried to configure my project so I can get all the Claims loaded into User.Identity in my Controller's Action, but I failed miserably. All the documentation I found was about previous versions of .Net Core.
I think I may be doing something wrong on the Startup.cs. If anyone can point me to the right direction I would really appreciate it.
I did an approach by filling the User on HttpContext in a middleware:
public class AuthMiddleware
{
private readonly RequestDelegate _next;
public AuthMiddleware(RequestDelegate next)
{
_next = next;
}
public Task Invoke(HttpContext context)
{
string authHeader = context.Request.Headers["Authorization"];
if (authHeader != null)
{
var jwtEncodedString = authHeader.Substring(7);
var token = new JwtSecurityToken(jwtEncodedString: jwtEncodedString);
var identity = new ClaimsIdentity(token.Claims, "basic");
context.User = new ClaimsPrincipal(identity);
}
return _next(context);
}
}
Then on app setup;
public static IApplicationBuilder UseApiConfiguration(this IApplicationBuilder app, IWebHostEnvironment env)
{
// add middleware reference
app.UseMiddleware<AuthMiddleware>();
return app;
}

How to make sure a user completes their profile in aspnet core 2.0?

I'm new to aspnet core 2.0 and what I would like to accomplish is to redirect a user who has not completed their profile to a page that they can do so. I'm using the default template with user authentication for Identity server.
I have tried using middleware but the page that i redirect to comes out blank. Is this the best approach or can someone help make it work. here is my middleware.
public class FarmProfileMiddleware
{
private readonly RequestDelegate next;
public FarmProfileMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context, UserManager<ApplicationUser> userManager)
{
var user = await userManager.GetUserAsync(context.User);
if (user != null)
{
if (!user.EmailConfirmed && !context.Request.Path.ToString().Contains("profile"))
{
var url = context.Request.PathBase + "/Users/Profile";
context.Response.Redirect(url);
}
}
else
{
await next(context);
}
}
}
Thanks in advance.
Just looking at the current logic in your code I notice that if the user is not null and has its profile completed will short circuit the call. You need to add in that logic.
Try the following
var user = await userManager.GetUserAsync(context.User);
if (user != null && !user.EmailConfirmed && !context.Request.Path.ToString().Contains("profile")) {
var url = context.Request.PathBase + "/Users/Profile";
context.Response.Redirect(url);
} else {
await next(context);
}

Return HTTP 403 using Authorize attribute in ASP.Net Core

When using ASP.Net WebAPI, I used to have a custom Authorize attribute I would use to return either an HTTP 403 or 401 depending on the situation. e.g. if the user is not authenticated, return a 401; if the user is authenticated but doesn't have the appropriate permissions, return a 403. See here for more discussion on that.
It seems now, in the new ASP.Net Core, they don't want you overriding the Authorize attribute anymore instead favoring a policy-based approach. However, it seems Core MVC suffers from the same "just return 401 for all auth errors" approach its predecessors have.
How do I override the framework to get the behavior I want?
After opening an issue here, it appears this actually should work...sort of.
In your Startup.Configure, if you just call app.UseMvc() and don't register any other middleware, you will get 401 for any auth-related errors (not authenticated, authenticated but no permission).
If, however, you register one of the authentication middlewares that support it, you will correctly get 401 for unauthenticated and 403 for no permissions. For me, I used the JwtBearerMiddleware which allows authentication via a JSON Web Token. The key part is to set the AutomaticChallenge option when creating the middleware:
in Startup.Configure:
app.UseJwtBearerAuthentication(new JwtBearerOptions
{
AutomaticAuthenticate = true,
AutomaticChallenge = true
});
app.UseMvc();
AutomaticAuthenticate will set the ClaimsPrincipal automatically so you can access User in a controller. AutomaticChallenge allows the auth middleware to modify the response when auth errors happen (in this case setting 401 or 403 appropriately).
If you have your own authentication scheme to implement, you would inherit from AuthenticationMiddleware and AuthenticationHandler similar to how the JWT implementation works.
I ended up doing it with middleware:
public class AuthorizeCorrectlyMiddleware
{
readonly RequestDelegate next;
public AuthorizeCorrectlyMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context)
{
await next(context);
if (context.Response.StatusCode == (int)HttpStatusCode.Unauthorized)
{
if (context.User.Identity.IsAuthenticated)
{
//the user is authenticated, yet we are returning a 401
//let's return a 403 instead
context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
}
}
}
}
which should be registered in Startup.Configure before calling app.UseMvc().
I followed the guide for Custom Authorization Policy Providers using IAuthorizationPolicyProvider in ASP.NET Core and also wanted to create a custom response.
https://learn.microsoft.com/en-us/aspnet/core/security/authorization/iauthorizationpolicyprovider?view=aspnetcore-5.0
The guide I followed for that was Customize the behavior of AuthorizationMiddleware
https://learn.microsoft.com/en-us/aspnet/core/security/authorization/customizingauthorizationmiddlewareresponse?view=aspnetcore-5.0
My code finally looked like this:
public class GuidKeyAuthorizationMiddlewareResultHandler : IAuthorizationMiddlewareResultHandler
{
private readonly AuthorizationMiddlewareResultHandler
DefaultHandler = new AuthorizationMiddlewareResultHandler();
public async Task HandleAsync(
RequestDelegate requestDelegate,
HttpContext httpContext,
AuthorizationPolicy authorizationPolicy,
PolicyAuthorizationResult policyAuthorizationResult)
{
if (policyAuthorizationResult.Challenged && !policyAuthorizationResult.Succeeded && authorizationPolicy.Requirements.Any(requirement => requirement is GuidKeyRequirement))
{
httpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
return;
}
// Fallback to the default implementation.
await DefaultHandler.HandleAsync(requestDelegate, httpContext, authorizationPolicy,
policyAuthorizationResult);
}
}
Startup.cs:
services.AddSingleton<IAuthorizationMiddlewareResultHandler,
GuidKeyAuthorizationMiddlewareResultHandler>();
You can also edit your AuthorizationHandler and access httpContext
via IHttpContextAccessor. However this feels more like a hack.
internal class GuidKeyAuthorizationHandler : AuthorizationHandler<GuidKeyRequirement>
{
private readonly ILogger<GuidKeyAuthorizationHandler> _logger;
private readonly IHttpContextAccessor _httpContextAccessor;
public GuidKeyAuthorizationHandler(ILogger<GuidKeyAuthorizationHandler> logger, IHttpContextAccessor httpContextAccessor)
{
_logger = logger;
_httpContextAccessor = httpContextAccessor;
}
// Check whether a given GuidKeyRequirement is satisfied or not for a particular context
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, GuidKeyRequirement requirement)
{
var httpContext = _httpContextAccessor.HttpContext; // Access context here
var key = System.Web.HttpUtility.ParseQueryString(httpContext.Request.QueryString.Value).Get("key");
if (!string.IsNullOrWhiteSpace(key))
{
// If the user guid key matches mark the authorization requirement succeeded
if (Guid.TryParse(key, out var guidKey) && guidKey == requirement.Key)
{
_logger.LogInformation("Guid key is correct");
if (requirement.RequireRefererHeader)
{
_logger.LogInformation("Require correct referer header");
httpContext.Request.Headers.TryGetValue("Referer", out var refererHeader);
if (requirement.RefererHeader == refererHeader)
{
_logger.LogInformation("Referer header is correct");
context.Succeed(requirement);
return Task.CompletedTask;
}
else
{
_logger.LogInformation($"Referer header {refererHeader} is not correct");
}
}
else
{
_logger.LogInformation("Correct referer header is not needed");
context.Succeed(requirement);
return Task.CompletedTask;
}
}
else
{
_logger.LogInformation($"Guid key {guidKey} is not correct");
}
}
else
{
_logger.LogInformation("No guid key present");
}
var msg = "Invalid Guid";
var bytes = Encoding.UTF8.GetBytes(msg);
httpContext.Response.StatusCode = 403;
httpContext.Response.ContentType = "application/json";
httpContext.Response.Body.WriteAsync(bytes, 0, bytes.Length);
return Task.CompletedTask;
}
}
Found that solution here:
https://stackoverflow.com/a/61861098/3850405

Categories

Resources