Identityserver4, parameter question. Authorization code flow - c#

I'm implementing a straight out of the box solution using IDserver4(2.3) targeting .netcore 2.2 that communicates with a FHIR client by calling:
Url/api/openid/openapp?launch=12345t6u-34o4-2r2y-0646-gj6g123456t5&iss=myservice&launchOrganization=tilt
with some HL7 simulated scopes etc. The flow is okay all the way to the token endpoint serving access and id tokens using the quickstart on an IIS with certificates and all the bezels.
My problem lies in that the client requires a parameter to be passed to the external client pointing to a file or something on the server where I have some test patient data stored/or served as Json.
Any competent way to pass a parameter with the body or the header for example? And do you do it at the authorization or the authentication, or along with the tokens? Lets call it context. The service shut me down when i reach it. Says this on their side 'TypeError: Parameter "url" must be a string, not undefined'
Thanks in advance.
Got it using:
public class CustomClaimInjection : ICustomTokenRequestValidator
{
private readonly HttpContext _httpContext;
public CustomClaimInjection(IHttpContextAccessor contextAccessor)
{
_httpContext = contextAccessor.HttpContext;
}
public Task ValidateAsync(CustomTokenRequestValidationContext context)
{
var client = context.Result.ValidatedRequest.Client;
//client.Claims.Add(new Claim("sub", sub)); // this will be [client_sub]
context.Result.CustomResponse = new Dictionary<string, object>
{
{"example-launchcontext", "https://url/" }
};
return Task.CompletedTask;
//return Task.FromResult(0);
}
}

I think I understand your problem now, and I think you would like a successful authentication to return additional information about where the patient's file is stored. I would store this in the token as a claim since it can be expressed as a statement about the subject (the user). This can be done in the registered (through dependency injection) implementation of the IProfileService. In the implementation of 'GetProfileDataAsync' you can set the issued claims using the 'ProfileDataRequestContext' parameter's property 'IssuedClaims'. These claims will be used to populate the id token which is what you should be looking to do.

Related

Signalr User never shows authenticated

Added the latest SignalR (6.0.3) to my .net 6 API. Everything works there until I try to grab the user identity - it is always unauthorized. I'm really just trying to get a claim from the token, so maybe I'm looking in the wrong place.
The entire setup is localhost API supplying localhost Vue3 SPA.
Authorization works as intended for all API controller actions, following this tutorial.
SignalR Hub communicates as intended with front-end otherwise - receiving and sending.
The SignalR Hub app.MapHub<ChatHub>("/chat"); is the last line in Program.cs before app.Run();
I tried updating the connection from front-end to include accessTokenFactory function, and I can confirm token is correctly supplied here. However, this should not be necessary with cookie authentication.
In my SignalR hub class is a simple method for getting a particular claim: (the code is rough, just trying to get it to work)
public int GetPlayerId()
{
int id = 0;
try
{
var identity = (ClaimsIdentity)Context.User.Identity;
id = Int32.Parse(identity.FindFirst("playerId").ToString());
} catch (Exception ex)
{
return 0;
}
return id;
}
Context.User looks like this regardless of what I do:
I'm not sure where to even begin to debug, as authorization is working as intended across the entirety of the application otherwise. That would seem to point to a SignalR issue, but the few posts I could find about this were mostly severely outdated and made no discernable impact. According to the documentation, this should "just work" with the application's existing authorization.
Any insight on what to check into or additional details to provide is deeply appreciated.
Edit: Additional information
Adding the [Authorize] decorator to my hub class itself does not appear to work. I am able to send and receive regardless. Authorization continues to work as intended elsewhere.
The JwtMiddleware from the affore-linked authentication scheme did not affect the Context object of the SignalR hub.
Instead of just the accountId, I took the validated JWT token and added an identity to the HttpContext User. This is probably not perfect but I hope it help someone in the future:
var jwtToken = jwtUtils.ValidateJwtToken(token);
if (jwtToken != null)
{
int? accountId = int.Parse(jwtToken.Claims.First(x => x.Type == "id").Value);
if (accountId != null)
{
// attach account to context on successful jwt validation
context.Items["Account"] = await dataContext.Accounts.FindAsync(accountId);
context.User.AddIdentity(new ClaimsIdentity(jwtToken.Claims));
}
}

SignalR get bearer token on Server

For a project, I have a C# asp.net core server and an Angular web client application. I was following the SignalR documentation on bearer tokens. It states that in order to give the authentication token, you can pass it as follows (copy-paste of the documentation).
let connection = new signalR.HubConnectionBuilder()
.withUrl("/chathub", {
accessTokenFactory: () => {
// Get and return the access token.
// This function can return a JavaScript Promise if asynchronous
// logic is required to retrieve the access token.
}
})
.build();
But now, I would like to extract this specific token on my C# server. The problem is, I can't find it anywhere. only the Context of type HubCallerContext is provided when a user connects in the method public override async Task OnConnectedAsync() or when a user invokes a method himself. But this Contextdoes not supply the given authentication token. How could I extract this?
As far as I know, if you want to get the token in the hub method, you could refer to below codes to get it.
var accessToken = Context.GetHttpContext().Request.Query["access_token"];

How to create custom authentication mechanism based on HTTP header?

I'm leaving old version of question on a bottom.
I'd like to implement custom authentication for SignalR clients. In my case this is java clients (Android). Not web browsers. There is no Forms authentication, there is no Windows authentication. Those are plain vanilla http clients using java library.
So, let's say client when connects to HUB passes custom header. I need to somehow authenticate user based on this header. Documentation here mentions that it is possible but doesn't give any details on how to implement it.
Here is my code from Android side:
hubConnection = new HubConnection("http://192.168.1.116/dbg", "", true, new NullLogger());
hubConnection.getHeaders().put("SRUserId", userId);
hubConnection.getHeaders().put("Authorization", userId);
final HubProxy hubProxy = hubConnection.createHubProxy("SignalRHub");
hubProxy.subscribe(this);
// Work with long polling connections only. Don't deal with server sockets and we
// don't have WebSockets installed
SignalRFuture<Void> awaitConnection = hubConnection.start(new LongPollingTransport(new NullLogger()));
try
{
awaitConnection.get();
Log.d(LOG_TAG, "------ CONNECTED to SignalR -- " + hubConnection.getConnectionId());
}
catch (Exception e)
{
LogData.e(LOG_TAG, e, LogData.Priority.High);
}
P.S. Original question below was my desire to "simplify" matter. Because I get access to headers in OnConnected callback. I thought there is easy way to drop connection right there..
Using Signal R with custom authentication mechanism. I simply check if connecting client has certain header passed in with connection request.
Question is - how do I DECLINE or NOT connect users who don't pass my check? Documentation here doesn't really explain such scenario. There is mentioning of using certificates/headers - but no samples on how to process it on server. I don't use Forms or windows authentication. My users - android java devices.
Here is code from my Hub where I want to reject connection..
public class SignalRHub : Hub
{
private const string UserIdHeader = "SRUserId";
private readonly static SignalRInMemoryUserMapping Connections = new SignalRInMemoryUserMapping();
public override Task OnConnected()
{
if (string.IsNullOrEmpty(Context.Headers[UserIdHeader]))
{
// TODO: Somehow make sure SignalR DOES NOT connect this user!
return Task.FromResult(0);
}
Connections.Add(Context.Headers[UserIdHeader], Context.ConnectionId);
Debug.WriteLine("Client {0}-{1} - {2}", Context.Headers[UserIdHeader], Context.ConnectionId, "CONNECTED");
return base.OnConnected();
}
So I just created a custom Authorization Attribute and overrode the AuthorizeHubConnection method to get access to the request and implemented the logic that you were trying to do with the Header and it appears to be working.
using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Hubs;
namespace SignalR.Web.Authorization
{
public class HeadersAuthAttribute : AuthorizeAttribute
{
private const string UserIdHeader = "SRUserId";
public override bool AuthorizeHubConnection(HubDescriptor hubDescriptor, IRequest request)
{
if (string.IsNullOrEmpty(request.Headers[UserIdHeader]))
{
return false;
}
return true;
}
}
}
Hub
[HeadersAuth]
[HubName("messagingHub")]
public class MessagingHub : Hub
{
}
Which yields this in the console (if the picture doesn't show up, it's a [Failed to load resource: the server responded with a status of 401 (Unauthorized)]):
In fact, accepted answer is wrong. Authorization attribute, surprisingly, shall be used for authorization (that is, you should use it for checking whether requesting authenticated user is authorized to perform a desired action).
Also, since you using incorrect mechanics, you don't have HttpContext.Current.User.Identity set. So, you have no clear way to pass user info to your business / authorization logic.
And third, doing that you won't be able to use Clients.User() method to send message to specific user, since SignalR will be not able to map between users and connections.
The correct way is to plug in into OWIN authentication pipeline. Here is an excellent article explaining and demonstrating in detail how to implement custom authentication to be used in OWIN.
I not going to copy-paste it here, just follow it and make sure you implement all required parts:
Options
Handler
Middleware
After you have these, register them into OWIN:
app.Map("/signalr", map =>
{
map.UseYourCustomAuthentication();
var hubConfiguration = new HubConfiguration
{
Resolver = GlobalHost.DependencyResolver,
};
map.RunSignalR(hubConfiguration);
});

C# OAuth OWIN: return custom response when invalid credentials provided to Token endpoint?

I have a Web API project which implements authentication via OAuthAuthorizationServerProvider. I've subclassed the provider and have implemented methods as necessary to implement my own authentication system.
I also have figured out how to override the return value provided for unauthenticated requests (you have to subclass the AuthorizeAttribute class and then use your custom attribute instead of Authorize on endpoints you intend to secure).
I can also override the TokenResponse method in my OAuth auth server provider in order to alter the response containing the token.
Now what I'm trying to do is to override the response provided by the token endpoint when a user provides incorrect credentials to the token endpoint. Right now, I simply get this:
{"error":"invalid_grant","error_description":"The user name or password is incorrect."}
I know where this text is coming from - in my GrantResourceOwnerCredentials method I do the following if a request is not authenticated:
if (!isValidUser)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
Instead though, I want to be able to fully manipulate the Response object that is returned when the user provides incorrect credentials.
As an example, I might want to set the return to look like this:
{"error":401,"timestamp":1234567890,"message":"Those credentials are wrong. Try again."}
Is there a way to override the response that the server provides upon failed authentication?
You cannot change this behaviour. You only can change the fields in context.SetError() method.
In this case the response, including the status code, is composed in SendErrorAsJsonAsync() private method, inside OAuthAuthorizationServerHandler internal class, in Microsoft.Owin.Security.OAuth dll.
You can revise the code in OAuthAuthorizationServerHandler class for more details.
Try the below code, refer here
public class CustomAccessTokenProvider : AuthenticationTokenProvider
{
public override void Receive(AuthenticationTokenReceiveContext context)
{
context.DeserializeTicket(context.Token);
var expired = context.Ticket.Properties.ExpiresUtc < DateTime.UtcNow;
if (expired)
{
//If current token is expired, set a custom response header
context.Response.Headers.Add("X-AccessTokenExpired", new string[] { "1" });
}
base.Receive(context);
}
}
Register it when setting up OWIN OAuth:
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions
{
AccessTokenProvider = new CustomAccessTokenProvider()
});

SignalR - Send message to user using UserID Provider

Using SignalR, I believe I should be able to send messages to specific connected users by using UserID Provider
Does anyone have an example of how this would be implemented? I've searched and searched and can not find any examples. I would need to target a javascript client.
The use case is, users to my site will have an account. They may be logged in from multiple devices / browsers. When some event happens, I will want to send them a message.
I have not looked into SignalR 2.0 but I think this is an extension of what the previous versions of SignalR used to have. When you connect to the hub you can decorate it with an Authorize attribute
[HubName("myhub")]
[Authorize]
public class MyHub1 : Hub
{
public override System.Threading.Tasks.Task OnConnected()
{
var identity = Thread.CurrentPrincipal.Identity;
var request = Context.Request;
Clients.Client(Context.ConnectionId).sayhello("Hello " + identity.Name);
return base.OnConnected();
}
}
As you can see you are able to access the Identity of the user accessing the Hub. I believe the new capability would be nothing more than an extension of this. Since the connection is always kept alive between the client and the hub you will always have the principal identity which will give you the UserId.
I believe this can help you: (linked from here)
A specific user, identified by userId.
Clients.User(userid).addContosoChatMessageToPage(name, message);
The userId can be determined using the IUserId interface:
public interface IUserIdProvider
{
string GetUserId(IRequest request);
}
The default implementation of IUserIdProvider is PrincipalUserIdProvider. To use this default implementation, first register it in GlobalHost when the application starts up:
var idProvider = new PrincipalUserIdProvider();
GlobalHost.DependencyResolver.Register (typeof(IUserIdProvider), () => idProvider);
The user name can then be determined by passing in the Request object from the client.

Categories