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"];
Related
I have a .NET6 ASP.NET Core application that implements a gRPC service as a server.
Authentication is done using Bearer tokens (JWT). After successful authentication+authorization, a server method is invoked to deal with a request, like so:
[Authorize("MyPolicy", AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public override Task<MyResp> MyCall(MyReq request, ServerCallContext context)
{
// ok, I come here
}
Now I need to access one of the claim values that I can expect to be in the token. And I can access the token and find the value I need like so:
var s = context.RequestHeaders.Single(m => m.Key == "authorization").Value;
if (s.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
s = s.Remove(0, 7);
var t = new JwtSecurityTokenHandler().ReadJwtToken(s.Trim());
// now I can look into t.Claims
Fine.
But is there a better way? Before my handler becomes activated, something in the middleware must have inspected that token anyway. Is the collection of claims already available somewhere? I would expect it to be in the ServerCallContext, but I can't find it.
Background
I'm working on an ASP.NET Core Web API where we within our API call a 3rd party API. This 3rd party API requires every request to contain a cookie with an access token. Our API gets this token from a claim (from the ClaimsPrincipal of the user associated with the request).
Details
This answer shows how to set a cookie on a request, but the example requires that one manually constructs the HttpClient (to be able to inject a HttpClientHandler with a CookieContainer). And since it is not desirable to do manual instantiation of the HttpClient, I would rather get the HttpClient injected through DI, which this example illustrates (usin IHttpClientFactory).
In my service, I have access to a an injected HttpClient instance (_httpClient), and the most simple function looks like this:
public async Task<string> GetStatusAsync(ClaimsPrincipal user)
{
// TODO: Add cookie with access token (from user's claims) before making request
return await _httpClient.GetStringAsync(Endpoints.Status);
}
This GitHub issue asks about how to include authorization cookie when using IHttpClientFactory, and the answer says to use the header propagation middleware. And now to my issue:
From what I can see, you set up the header propagation middleware (with all headers and cookies and whatnot) during service configuration at application startup. In our API however, I do not have the value for the authentication cookie before actually making the request to the 3rd party API.
Question
How can I add a cookie to the request on a HttpClient instance that is injected to my service using IHttpClientFactory, right before making the actual request?
The solution was indeed to use header propagation. I just had to get all the pieces together correctly.
Header propagation is configured inside ConfigureServices (in Startup.cs). And since I want to use data from the current user's claims, the trick is, when adding the cookie header, to use on of the overloads that takes in a function that gives you access to the current context:
services.AddHeaderPropagation(options =>
{
options.Headers.Add("Cookie", context =>
{
var accessToken = context.HttpContext.User.Claims.FirstOrDefault(c => c.Type == "access-token")?.Value;
return accessToken != null ? new StringValues($"token={accessToken}") : new StringValues();
});
});
In addition to this, since I'm using a Typed Service, I also had to configure that that specific service should forward the cookie header (at the same place inside ConfigureServices in Startup.cs):
services.AddHttpClient<IApiService, ApiService>(httpClient =>
{
httpClient.BaseAddress = new Uri("https://httpbin.org/");
}).AddHeaderPropagation(options =>
{
options.Headers.Add("Cookie");
});
And finally, the thing that caused me some trouble for a little while: Since I'm using data from the current users claims, the registration of the header propagation middleware (app.UseHeaderPropagation(); inside Configure in Startup.cs) must happen after adding the authentication middleware (app.UseAuthentication();). If not, the claims haven't been set yet.
And as a final tip: I head great us in https://httpbin.org/ when working on this. The request inspection endpoints there are really useful to see what data you actually pass along in your request.
You can put a static function inside AddHeaderPropagation config function:
services.AddHeaderPropagation(options =>
{
options.Headers.Add(MyCookie());
});
And inside MyCookie function,
get the current context end extract from them all needed data.
If you need something from your current flow or current controller you can add it
inside httpSession and take it inside MyCookie function.
I want to create HealthChecks portal via HealthChecksUI but with limited access with authorization.
Also I using Blazor to accomplish authorization dialog creation and receiving access token.
So I configuring HealthChecksUI:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
//...
app.UseEndpoints(endpointsBuilder =>
{
//...
endpointsBuilder.MapHealthChecksUI(setup =>
{
setup.UIPath = "/portal";
setup.ApiPath = "/portal/api";
setup.WebhookPath = "/portal/webhooks";
setup.ResourcesPath = "/portal/resources";
}).RequireAuthorization(); // This means access to '/portal' route will be limited by authorization.
//...
}
//...
}
I using bearer token in HTTP Authorization header for authorization while performing any request.
Next let's check authorization work:
GET request from POSTMAN with valid bearer token to '/portal' route passing successfully. Also, if I change token then I recieve 401 Unauthorized error. So it's seems authorization system working correctly.
Next step will be using authorization dialog to perform token receiving and redirecting to portal page.
Code below it's just a simple authorize function using in .razor page
private async Task SubmitAsync()
{
var (authorizationSuccessful, accessToken) = await authorizationService.AuthorizeAsync(authorizationData).ConfigureAwait(false);
if (authorizationSuccessful)
{
navigationManager.NavigateTo("/portal", true);
}
else
{
throw new UnauthorizedAccessException("Incorrect login or password");
}
}
So problem is:
When authorization passing (authorizationSuccessful is true) and navigation performing, I get to '/portal' page without any authorization data, so I get 401 Unauthorized error.
The question is:
How can I pass received bearer token (accessToken) through NavigateTo method in Authorization HTTP Header to accomplish authorized access to '/portal' page? Is it even possible?
Unfortunately, it's not possible to accomplish this task that way.
According to tries to do something like this using JS only (this and this), it can't be done with plain JS.
So we have just few options here:
Share authorization token through cookies
Cookies are browser-based shared storage, so check access token from here possible right after navigation.
Send authorization token through query
NavigateTo method can be used with query parameters like this:
navigationManager.NavigateTo($"/portal?token={accessToken}", true);
So we can check access token from query parameters directly.
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.
I'm building an identity server deployment (Identity Server 4, hosted in an ASP.NET Core MVC application). As a part of the new user registration process, I need the identity server application to make a request against another API. I'd like to use, basically, the client credential flow to make this request, but instead of having the identity server make an http request against its own endpoint, would it be possible to just programmatically generate the token in C#?
What I'd like to do would be something like this:
public class AccountController : Controller
{
[HttpPost("register")]
public async Task<IActionResult> Register(UserRegistrationModel model)
{
// do stuff like validate model, create user, update database, etc
// generate access token for other API
var client = identityServer4DbContext.Clients.FirstOrDefault(c => c.Id = "myself");
var token = tokenService.CreateAccessToken(client, StandardScopes.All.Concat(scopeForMyOtherApi));
var httpClient = new HttpClient();
httpClient.BaseAddress = new Uri("https://myotherapi/");
var result = await httpClient.GetAsync("resource/info-i-need");
// do something with result.
}
}
I saw that there is an ITokenService in IdentityServer4, but it requires a TokenCreationRequest populated with stuff you only get when you have an http request (for a token) to handle, so it seems that it is only useful to IdentityServer4 itself.
I also recognize that I could use the IdentityModel client to make a request against my own endpoint, but that would involve a bit more configuration that I'd like to avoid - not to mention that it seems like I shouldn't have to do that from within the identity server application itself.
In IdentityServer 3 it was possible to call IssueClientToken() OWIN extension method.
In IdSrv 4, use IdentityServerTools.IssueJwtAsync() and IssueClientJwtAsync().