Access SignalR connection context in authorization attribute handler code - asp.net-core-2.0

I can't get the SignalR connection context when using the Authorize attribute on a hub method.
I'm able to access the HttpContext from my custom Authorization attribute on my Hub:
[Authorize("MyAuthorizationPolicy")]
public class ChatHub : Hub
In my AuthorizationHandler I can inject IHttpContextAccessor to get to the HttpRequest, which gives me access to the token (which is in the header).
But because method invocation doesn't use the HttpRequest, I need to get to the SignalR request context, when I apply an Authorization attribute to my method:
[Authorize("MyAuthorizationPolicy")]
public async Task Join(Guid roomGuid)
Obviously, my instance of IHttpContextAccessor gives me a null HttpContext. How can I inject a 'SignalRConnectionContextAccessor'? :)
(https://github.com/aspnet/Docs/issues/11331)

Per discussion here, SignalR is decoupled with HTTP so you may not want to access HTTP context in SignalR contexts.
I do, however, have a not elegant solution for this. SignalR hub Context has a property Features of type IFeatureCollection, which is a dict. Do a LINQ on it:
var contextFeature = hub.Context.Features.SingleOrDefault(f => f.Key == typeof(IHttpContextFeature)).Value as IHttpContextFeature;
var httpContext = contextFeature?.HttpContext;
Remind to check null for httpContext.
I don't recommend this solution though. You should pass your authorization info in User as claims, which is accessible in hub's Context.

Related

Identity Server 4 asp.net 2.1 - IProfileService or alternative

I will buy anyone a beer who can solve my problem!
As a piece of work I need to update our Identity Server to use an implicit login flow, it was currently using Bearer Token access only. As a part of our architecture we are using Multi-tenancy.
For security reaosns we need to check the tenant header to verify that the user is not impersonating another tenant. To do this from a client perspective we use a custom IProfileService. This gets triggered in the middleware of Identity Server, meaning all is good!
However if I was a user and I wanted to use some form of functionality on Identity Server itself and not an actual client of it, then IProfileService will not be triggered. An example of this would be to revoke access to clients, or even log out.
The GetProfileDataAsync Method on IProfileService is Invoked when the client request additional claims for the user.
germansak on Github Issue here had a similar issue and it was never quite answered (https://github.com/IdentityServer/IdentityServer4/issues/1643)
Leading to my question, how has anyone been able to verify a Tenant on Identity Server itself when they are not going through a Client, but instead Identity Server. If I can't trigger IProfileService I feel as if I'm beat!
Both logout and grants functionality is not part of the identity server 4 core package and they are simply implemented as ASP.NET Core razor views outside of the oauth2 flows.
There are few ways to validate headers therefore, I guess the easiest in my opinion would be to add another middleware.
public void Configure(IApplicationBuilder app)
{
app.UseMiddleware<TenantHeaderValidationMiddleware>();
...Your other config
}
public class TenantHeaderValidationMiddleware
{
private readonly RequestDelegate _next;
public RequestCultureMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
string tenantId = Request.Headers["YourTenantIdHeader"];
//do the checking
...Validation logic
//pass request further if correct
await _next(context);
}
}

Extending WebSphere SAML Identity propogation

I have an existing financial application which uses an API gateway to authenticate web-based users. This gateway maintains a security session for the user, and proxies SOAP calls on to a WebSphere box. It adds a signed SAML assertion to these SOAP calls.
A series of JAX-WS Services are deployed on WebSphere, and these are protected with WebSphere policies to consume the SAML assertions. The identity and group memberships specified in the SAML assertions are then propagated to the WebSphere security context for the service call. All works very well, all of the security logic is done purely by configuration.
New requirements now require that we propagate the sessionId in the API gateway all the way through to WebSphere , and beyond. This is for reasons of traceability.
Clearly we could change the WSDL for all of the services to include some Meta-data fields, but this is a big change, and would require very extensive testing.
I was hoping there might be a way to map some arbitrary attributes from the SAML assertion (Other than Identity and groupMembership) to the WebSphere security context. Or even to access the SAML XML in the (authenticated) JAX-WS Service.
Has anyone done this?
You can have API gateway to add sessionid as an SAML attribute, then retrieve the attribute from Subject after SAML is processed by WebSphere. Here is sample code to get SAML attribute in WebSphere after SAML is processed.
Subject subject = WSSubject.getRunAsSubject();
SAMLToken samlToken = (SAMLToken) AccessController.doPrivileged(
new java.security.PrivilegedExceptionAction() {
public Object run() throws java.lang.Exception
{
final java.util.Iterator authIterator = subject.getPrivateCredentials(SAMLToken.class).iterator();
if ( authIterator.hasNext() ) {
final SAMLToken token = (SAMLToken) authIterator.next();
return token;
}
return null;
}
});
Map<String, String> attributes = samlToken.getStringAttributes();
List<SAMLAttribute> attributes = samlToken.getSAMLAttributes();
Instead of looping through the creds yourself, you can use the WSSUtilFactory API to do it for you: https://www.ibm.com/support/knowledgecenter/SSAW57_9.0.0/com.ibm.websphere.javadoc.doc/web/apidocs/com/ibm/websphere/wssecurity/wssapi/WSSUtilFactory.html
Assuming that you are using a SAML 2.0 token, you can do:
WSSUtilFactory wssuf = WSSUtilFactory.getInstance();
SAMLToken token = wssuf.getSaml20Token();
The getSaml20Token method was added to WSSUtilFactory in 70043, 80013, 85510 and 9000.

"Permissions for service are set to internal but this request was external" for app in same resource group

The Azure API app documentation briefly describe three methods of protecting the API app. One of them is the internal accessibility settings: “Internal - Only other API apps or web apps in the same resource group are allowed to call the API app.”
I have create another Azure API app in the same resource group and hosting plan. But a get a HTTP 403 authorization failure with the following error message when I try to connect to the interal API app from the Web App:
“Permissions for service are set to internal but this request was external.”
Has anyone been able to use the internal settings between API Apps in the same Resource Group?
We will be documenting this soon. in the meantime, what you need to do is the following. You need to install nuget package Microsoft.Azure.AppService.ApiApps.Service. Then, create a delegating handler as follows:
class InternalCredentialHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
Runtime.FromAppSettings(request).SignHttpRequest(request);
return base.SendAsync(request, cancellationToken);
}
}
Then when you use HttpClient or a generated client to connect to another internal API, simply pass in the delegating handler. For example:
MySampleClient client = new MySampleClient(new DelegatingHandler[] { new InternalCredentialHandler() });
Thanks,
Mohit
Edit: the documentation for this is now available at https://azure.microsoft.com/documentation/articles/app-service-api-dotnet-consume-internal/

How to implement Authorization in ASP.NET Web API using Windows Azure

I have sample ASP.NET Web API with get method, I have prefixed a [Authorize] attribute on top of the method. Can I please know how should I call this method from browser or fiddler? Also, I am hosting these API's on Windows Azure
public class ValuesController : ApiController
{
// GET api/values
[Authorize]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
}
Depending on the type of authorization you are using there might be different ways. But if you are using default routing you could call your method at the following url:
/api/values
You might of course need to pass additional headers depending on the authorization mechanism you choose. The [Authorize] attribute doesn't do anything unless you have configured some authorization. You may take a look at the following article for an example of how you could use tokens to authenticate your users.

Where should I plugin the Authorization in Asp.net WebAPI?

As I see I have 3 possible places to plug my stuff in the pipeline
1) AuthorizationFilters
2) Action Filters
3) DelegatingHandler
The most obvious one is AuthorizationFilters , where I can decorate my actions/ controllers with my custom authorization attribute . say .. MyCustomAuthorizationAttribute .
Since HTTP message handlers are in the first stage in the processing pipeline. Does it make any sense to put it in there ?
Authorization for me right now simply means checking a token in the header which is given to the client after authentication.
Update July 2014
My original answer covered WebApi 1. with WebApi 2 there were some changes i.e. there is now an IAuthenticationFilter meaning you can move authentication logic out of the DelegatingHandler which is a little more elegant.
There is a Nuget project here that offers an implementation of IAuthenticationFilter and also explains some background to its introduction.
OWIN middleware is now perhaps the best place to implement your authentication logic - there is an example of Certificate Authentication here and Basic Authentication OWIN Middleware here in this blog post the former example is the preferred one as it demonstrates the use of the base AuthenticationHandler class.
The advice on AuthorizationFilters remains largely unchanged.
End Update
Typically...
Use DelegatingHandler to carry out Authentication... i.e. who someone is. Use this to set the Principle of the Thread and User context, add claims etc. You can place authorisation logic here too but on a fairly global scale. I would personally always use AuthorizationFilters for authorisation.
Use AuthorizationFilters to restrict controllers and actions to specific people. These are used when you can extrapolate their permission with the information in claims, principal, url or the http request parameters. The default authorisation filter can be used to restrict access to anonymous users or by roles (if set in something like a delegating handler) - obviously you can implement your own AuthorizationFilters too if you need it.
Occasionally use ActionFilters when you need to make the decision over authorisation using the message content e.g. you need access to a property on the entity to decide whether they have access (obviously be careful with this(!)).
Note:
The AuthorizationFilters are called before the content of the body is read therefore they do not have access to the message body to make authorization decisions this is why the ActionFilters specifically the OnActionExecuting is used to occasional raise authentication errors.
So
In your scenario I would put a simple DelegatingHandler to take your header and set the principal.
public class CustomAuthenticationMessageHandler : DelegatingHandler
{
public CustomAuthenticationMessageHandler ()
{
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
CancellationToken cancellationToken)
{
Authenticate(request);
return base.SendAsync(request, cancellationToken);
}
protected virtual void Authenticate(HttpRequestMessage request)
{
var authorisationHeader = request.Headers.Authorization;
if (authorisationHeader == null)
{
return;
}
//Ensure you are happy with the header contents then
{
var principal = new GenericPrincipal(//new Identity , //Roles);
Thread.CurrentPrincipal = principal;
HttpContext.Current.User = principal;
}
}
}
Then use AuthorizationFilters to restrict access:
[Authorize]
public string Get()
{
}
[Authorize(Roles = "Admin")]
public string GetAdminOnly()
{
}
To register the global Authentication
config.MessageHandlers.Add(new CustomAuthenticationMessageHandler());
This will mean that in every request the principal will be set to either null or a valid identity. It won't handle authorisation i.e. wont deny access to any controllers or actions.
To start protecting resources
Either target protected controllers and actions with the standard or custom [Authorize] attributes. Or register globally:
config.Filters.Add(new AuthorizeAttribute());
And only white list the controllers and actions you want unsecured using the [AllowAnonymous] attribute.
If you only want authentication on some routes
Then you can modify your DelegatingHandler a little to set the InnerHandler to route to the correct controller e.g.
public CustomAuthenticationMessageHandler(HttpConfiguration configuration)
{
InnerHandler = new HttpRoutingDispatcher(configuration);
}
And then you can specify this handler on your routes like so:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "myurl",
defaults: new {},
constraints: new {},
handler: new CustomAuthenticationHandler(config)
);

Resources