Get authenticated user from Middleware class in Blazor app - .net-core

I have a server sided Blazor app. Every user must be authenticated when he wants to visit a page. For this the user is redirected to an "identity server" login page. When the user logs in with the correct credentials, he is then redirected back to the Blazor app.
I have setup my Blazor app with CascadingAuthenticationState, so that I can access the User object and its claims inside my Blazor pages.
This goes something like this inside a component:
[CascadingParameter]
private Task<AuthenticationState> AuthenticationStateTask { get; set; }
...
...
var authState = await AuthenticationStateTask;
var claims = authState.User.Claims; // Works fine.
When I do it like this I have access to the users claims.
So far so good.
But I also have a middleware class where I need to access the user claims.
public async Task InvokeAsync(HttpContext context)
{
if (context?.User?.Claims != null && context.User.Claims.Any())
{
Console.WriteLine("aaa");
}
// Call the next delegate/middleware in the pipeline
await _next(context);
}
But for some reason the User claims are always empty.
Why is the User.Claims object from inside my Blazor component filled with all the claims, but are they empty when I access them through the HttpContext object?

The order of registering Middleware is critical. This is something I've been personally running into a lot lately in my own projects as well - And yet, there are no warnings if you configure them out-of-order.
Quote from Microsoft docs on Middleware order:
The order that middleware components are added in the Startup.Configure method defines the order in which the middleware components are invoked on requests and the reverse order for the response. The order is critical for security, performance, and functionality.
It seems you are calling your Middleware before the .NET Core authentication middleware, and therefor your User object is null - or lacks claims.
Place your app.UseMiddleware<T> after app.UseAuthentication() and app.UseAuthorization().

Related

WebAssembly Razor client app return 401 while calling a protected API in Azure with Admin Role

I have API protected by Azure AD where Authentication is required to get access data, where I expose the API with only one simple scope for now.
The API and client app both are registered in Azure AD.
Roles are also configured for the API, only a a user with Admin role can call my API.
Do I need to assign this Admin role as well to the client App? or AccessApi scope is enough?
Scopes Who can consent Admin consent display name User consent display name State
api://xx User AccessApi AccessApi Enable
And a client application build using webassembly blazor also registered in Azure AD, and its configured with Api permission to use delegated access to AccessApi.
API / Permissions name Type Description Admin consent required Status
myApi (1)
AccessApi Delegated AccessApi No
I configured webassembly blazor client application to authenticate against azure and get token and use that token to call myApi, however I keep getting loading but no data is being displayed without any error.
Im not sure what went wrong here ?
program class of client application:
private static string scope = #"api://xxx/AccessApi";
...
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("app");
builder.Services.AddScoped<GraphAPIAuthorizationMessageHandler>();
builder.Services.AddHttpClient("ServerAPI",
client => client.BaseAddress = new Uri("https://localhost:44314"))
.AddHttpMessageHandler<GraphAPIAuthorizationMessageHandler>();
builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>()
.CreateClient("ServerAPI"));
builder.Services.AddMsalAuthentication(options =>
{
builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
options.ProviderOptions.DefaultAccessTokenScopes.Add(scope);
});
At fetch data razor page I imported all necessary libraries
#using Microsoft.AspNetCore.Authorization
#using Microsoft.AspNetCore.Components.WebAssembly.Authentication
#inject IAccessTokenProvider TokenProvider
#attribute [Authorize]
#inject NavigationManager UriHelper
#inject HttpClient Http
...
data = await Http.GetFromJsonAsync<data[]>(#"API-url--runs-locally-on-docker");
The authorization message handler class
private static string scope = #"api://xxx/AccessApi";
public GraphAPIAuthorizationMessageHandler(IAccessTokenProvider provider,
NavigationManager navigationManager)
: base(provider, navigationManager)
{
ConfigureHandler(
authorizedUrls: new[] { "https://localhost:44314" },
scopes: new[] { scope });
}
After authenticated myself with Azure AD account, the client app shows loading.. but no data is being displayed.
at the console level shows this error.
Failed to load resource: the server responded with a status of 401 (Unauthorized)
I followed Microsoft documentation and I'm not sure what I'm missing here.
what could be wrong here ?
Update
The Api expose tab:
Api permission for the client app:
Obviously, your scope is set incorrectly. You should set the scope to the client id of the api application instead of the client id of the client application. In your question, I think your scope should be: api://7b4d6df9-63cd-4ed7-881bxxx/AccessApi.
Parse the token and you should see scp claim and roles claim.

Get session in ASP boilerplate service layer

I am currently use AspNetBoilerplate to implement my services in service layer...
So I can access session in MVC controller like:
Token token = HttpContext.Session["Token"] as Token;
After login Token session be initiated....
I can access thta everywhere in MVC controllers but in ApplicationServiceBase it is null like:
public class AuditAppService : ApplicationServiceBase, IAuditAppService
{
public GetUserActions_Box GetUserActions()
{
var token = HttpContext.Current.Session.GetToken();
return GetUserActions_Box.Empty;
}
}
HttpContext.Current.Session is null why?
that app services are implemented in separate library which is added in main web application.
I followed the documentation and crossed This, I think AbpSession is not my solution which mean by it we can access some basics info about session that initiated by AbpBoilerPlate authentication system not ours.
use IAbpSession .
you can use claims to add your custom values to the AbpSession.
previously answered here about how to extend AbpSession
Extend ClaimsAbpSession
read AbpSession https://aspnetboilerplate.com/Pages/Documents/Abp-Session

IdentityServer 3 refresh user with refresh token

We are trying to set up Identity Server 3 in the right way.
We got authentication working fine and we manage to retrieve the refresh token.
The client application is using Angular.
Now when the acces_token expires any calls to the rest api fails (we managed to get it to return 401) but we are wondering how to re-authenticate the user.
In our tests, any api call made from Javascript is failing (401) but as soon as the page is refreshed the whole mechanism is kicking in. We do see that we are redirected to the identity server but it does not show up the login page, we are sent back to the client application with new tokens apparently.
What I would like to do is to refresh the access token without having to force the user to refresh the page.
What I'm not sure though is whose responsibility is it? Is that the client application (website) or the angular application? In other word, should the application handle this transparently for Angular or should angular do something when it receives a 401, in which case, I'm not too sure how the information will flow back to the web app.
Any clue?
Additional Information: We are using OpenId Connect
I got it working!
As I said in the comments I used this article. The writer is referencing a very nice lib that I am using as well.
Facts:
Identity Server 3 is requesting the client secret upon access token refresh
One should not store the refresh_token or the client_secret on the javascript application as they are considered unsafe (see the article)
So I chose to send the refresh_token as en encrypted cookie sith this class (found of ST BTW, just can't find the link anymore, sorry...)
public static class StringEncryptor
{
public static string Encrypt(string plaintextValue)
{
var plaintextBytes = plaintextValue.Select(c => (byte) c).ToArray();
var encryptedBytes = MachineKey.Protect(plaintextBytes);
return Convert.ToBase64String(encryptedBytes);
}
public static string Decrypt(string encryptedValue)
{
try
{
var encryptedBytes = Convert.FromBase64String(encryptedValue);
var decryptedBytes = MachineKey.Unprotect(encryptedBytes);
return new string(decryptedBytes.Select(b => (char)b).ToArray());
}
catch
{
return null;
}
}
}
The javascript application is getting the value from the cookie. It then deletes the cookie to avoid that thing to be sent over and over again, it is pointless.
When the access_token becomes invalid, I send an http request to the application server with the encrypted refresh_token. That is an anonymous call.
The server contacts the identity server and gets a new access_token that is sent back to Javascript. The awesome library queued all other requests so when I'm back with my new token, I can tell it to continue with authService.loginConfirmed();.
The refresh is actually pretty easy as all you have to do is to use the TokenClient from IdentityServer3. Full method code:
[HttpPost]
[AllowAnonymous]
public async Task<JsonResult> RefreshToken(string refreshToken)
{
var tokenClient = new TokenClient(IdentityServerConstants.IdentityServerUrl + "/connect/token", "my-application-id", "my-application-secret");
var response = await tokenClient.RequestRefreshTokenAsync(StringEncryptor.Decrypt(refreshToken));
return Json(new {response.AccessToken});
}
Comments are welcome, this is probably the best way to do that.
For future reference - using refresh tokens in an angular (or other JS) application is not the correct way as a refresh token is too sensitive to store in the browser. You should use silent renew based on the identityserver cookie to get a new access token. Also see the oidc-client-js javascript library, as this can manage silent renew for you.

Manually associate session with current request asp.net MVC

I have a MVC 5 asp.net website where I need to expose a number of REST APIs to a stand-alone mobile client. The rest of the site is using Forms based security where it sets the ASP.NET_SessionId as a cookie, and that is used to authenticate the user with the request after they log in. With my mobile application, I am not able to use the cookie method because of the cross-doman issue. What I would like to do is add a header "X-SessionId" with the value of the ASP.NET_SessionId, then on the server side, have a filter that looks for that field, and if it is present, associates the request with the given session. (Client will log in with an AJAX POST call which will return the ASP.NET_SessionId upon successful login).
Is this possible?
Something like this?
public sealed class CustomSecurityAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext == null)
throw new ArgumentNullException("filterContext");
if (string.IsNullOrEmpty(filterContext.HttpContext.Request.Headers["X-SessionId"]) && IsAuthenticated(ilterContext.HttpContext.Request.Headers["X-SessionId"]))
filterContext.Result = new HttpNotFoundResult();
}
private bool IsAuthenticated(string sessionId)
{
// get your user details from your database (or whatever)
var user = new UserRepository().Get(sessionId);
if (user == null)
return false;
// build up an identity, use your own or out of the box.
FormsIdentity itentity = new MyIdentity(user);
// Set the user
filterContext.HttpContext.Current.User = new System.Security.Principal.GenericPrincipal(itentity , user.Roles);
return true;
}
}
You are going to have to store current sessions in your database, so for example when a user logs in grab the sessionid and stick it in the db, so you know they have 1..n current sessions.
Then you can look it up as part of your authentication.
Edit:
Let's take a step back, never mind cookies and sessions for the moment.
You have a website and a restful api, they both servce different purposes and clients and have different security requirements.
So what are the most common options for securing your Api?
Basic authentication.
Most restful APIs require a username/password to be sent through with each request, as part of the headers or in the request itself.
An authentication token
You can provide a token associated with a user account (a guid could suffice) when requests are made you check for the token.
Using an existing protocal like OAuth
I would recommend using these common scenarios to be sure you don't miss something and open your self up to security vulnerabilities.
Is there a reason you can't use any of these?

Implementing Authorization in a Self Hosted SignalR Server accessed from Web

I'm looking for some guidance on how to implement authorization security for SignalR on a back end service running in a self-hosted (non-IIS) environment, that is called from a Web application. The backend app is basically a monitor that fires SignalR events back to the HTML based client. This all works fine (amazingly well actually).
However, we need to restrict access to the server for authenticated users from the Web site. So basically if a user is authenticated on the Web site, we need to somehow pick up the crendentials (user name is enough) and validation state in the backend app to decide whether to allow the connection as to avoid unauthorized access.
Can anybody point at some strategies or patterns on how to accomplish this sort of auth forwarding?
I am having similar issues here, as in my web app I use a simple cookie authentication system which uses an AoP style approach to check for any controllers with an attribute, then will get the current context (be it from the static HttpContext.Current or from the target invocation object depending on the type of interceptor) and then verify the cookie exists, it contains right data, then finally verify the token with the db or cache etc.
Anyway this approach can also be used for Signalr, although its a bit more long winded and you are using dependency injection. You would basically wrap the hub calls with the desired attribute, then set up your DI/IoC configuration to intercept these calls, then either get the hub instance within your interceptor and get the cookie (or your custom authentication mechanism) from the request, verify it is all valid or not, and if not then throw a new HttpException("403", "Not authenticated"); which should kick the user out and return back before it even hits your hub method, this way you can put the logic in one place (your interceptor, or a class the interceptor consumes) then just wrap any method that needs to use this authentication using your attribute.
I use Ninject and the interception extension, but most major DI frameworks these days have some form of IoC plugin/extensions, such as Autofac, Windsor, Spring etc.
If you were not happy going down the route of introducing DI and/or AOP to your current project, then maybe you could just create a custom hub instance which contains your authentication logic and then just use that in your hubs, so ok you will still be manually calling some authentication logic from within each hub method you want to protect, but its less code, so something like:
public class AuthorisableHub : Hub
{
private ISomeAuthenticationToken GetSomeAuthenticationTokenFromRequest(Request request) // probably a SignalR specific request object
{
// Get your token from the querystring or cookie etc
}
private bool IsAuthenticationTokenValid(ISomeAuthenticationToken token)
{
// Perform some validation, be it simple or db based and return result
}
protected void PerformUserAuthentication()
{
var token = GetSomeAuthenticationTokenFromRequest(Context.Request);
var isRequestValid = IsAuthenticationTokenValid(token);
if(!isRequestValid)
{ throw new HttpException(403, "<Some forbidden message here>"); }
}
}
public class MyFancyPantsHub : AuthorisableHub
{
public void TellAllClientsSomethingSecret(ISecret secret)
{
PerformUserAuthentication();
// Do stuff with the secret as it should have bombed the user out
// before it reaches here if working correctly
}
}
It is not perfect but would work (I think), also I am sure I once read somewhere that Hubs are newly instantiated for each request, and if this is indeed true, you could possibly just put this logic in your constructor if you want to apply the authentication to every action within the hub.
Hope that helps, or gives you ideas... would be interested in knowing how you did solve it in the end.
SignalR does not provide any additional features for authentication. Instead, it is designed to work with the authentication mechanism of your application.
Hubs
You should do authentication as you normally would and then use the Authorize attribute provided by SignalR to enforce the results of the authentication on the Hubs.
The Authorize attribute can be applied to an entire Hub or particular methods in the Hub. Some examples:
[Authorize] – only authenticated users
[Authorize(Roles = "Admin,Manager")] – only authenticated users in the specified .NET roles
[Authorize(Users = "user1,user2")] – only authenticated users with the specified user names
You can also require all Hubs to require authentication by adding the following method in the Application_Start method:
GlobalHost.HubPipeline.RequireAuthentication();
Persistent Connections
You can use the user object in the request to see if the user is authenticated:
request.User.IsAuthenticated

Resources