how to add a token head to a request using HttpClient from IHttpClientFactory in Blazor - jwt

I am trying to use JWT in my API, and configuration is completed, can use postman tool to access data from it. However when I use Blazor as front end to access it , the request doesn't have token, so always give a 401 code.
Below is my Blazor code.
program.cs
builder.Services.AddHttpClient<IOptionService, OptionService> ("OptionAPI", (sp, cl) => {
cl.BaseAddress = new Uri("https://localhost:7172");
});
builder.Services.AddScoped(
sp => sp.GetService<IHttpClientFactory>().CreateClient("OptionAPI"));
OptionService.cs
public class OptionService : IOptionService {
private readonly HttpClient _httpClient;
public OptionService(HttpClient httpClient) {
_httpClient = httpClient;
}
public async Task<IEnumerable<OptionOutputDto>> GetOptionsAsync(Guid quizId, Guid questionId) {
_httpClient.DefaultRequestHeaders.Authorization
= new AuthenticationHeaderValue("Bearer", "token");
return await JsonSerializer.DeserializeAsync<IEnumerable<OptionOutputDto>>(
await _httpClient.GetStreamAsync($"api/quizzes/{quizId}/{questionId}/options"),
new JsonSerializerOptions {
PropertyNameCaseInsensitive = true
});
}
I tired use " new AuthenticationHeaderValue("Bearer", "token");" to attach token in header, but its not working, still give 401 code.
And I also tried use
private readonly IHttpClientFactory _httpClient;
public OptionService(IHttpClientFactory httpClient) {
_httpClient = httpClient;
}
public async Task<IEnumerable<OptionOutputDto>> GetOptionsAsync(Guid quizId, Guid questionId) {
var newHttpClient = _httpClient.CreateClient();
newHttpClient.DefaultRequestHeaders.Authorization
= new AuthenticationHeaderValue("Bearer", "token");
return await JsonSerializer.DeserializeAsync<IEnumerable<OptionOutputDto>>(
await newHttpClient.GetStreamAsync($"api/quizzes/{quizId}/{questionId}/options"),
new JsonSerializerOptions {
PropertyNameCaseInsensitive = true
});
}
it's also not working, give me an error,
Unhandled exception rendering component: A suitable constructor for type 'Services.OptionService' could not be located. Ensure the type is concrete and all parameters of a public constructor are either registered as services or passed as arguments. Also ensure no extraneous arguments are provided.
System.InvalidOperationException: A suitable constructor for type .....
Can anyone has a simple way to attach token in request header?
Thanks in advance.

I think the good option is :
builder.Services.AddHttpClient<IOptionService, OptionService> ("OptionAPI", (sp, cl) => {
cl.BaseAddress = new Uri("https://localhost:7172");
});
Could you check if the token is present in header or not?

Your error is most likely related to how the OptionService is being registered in dependency injection. It either needs an empty constructor adding - and/or - you need to ensure that the constructor has all of its dependencies registered correctly in the ServicesCollection too.
The exception is quite explicit:
Ensure the type is concrete and all parameters of a public constructor
are either registered as services or passed as arguments. Also ensure
no extraneous arguments are provided

I gave a similar answer here. Basically you need to include the BaseAddressAuthorizationMessageHandler when defining your httpclients. If you're using a typed httpclient, you can inject the IAccessTokenProvider and get the token from there. Kinda like this:
public class MyHttpClient(IAccessTokenProvider tokenProvider, HttpClient httpClient)
{
_tokenProvider = tokenProvider;
_httpClient = httpClient;
}
private async Task RequestAuthToken()
{
var requestToken = await _tokenProvider.RequestAccessToken();
requestToken.TryGetToken(out var token);
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Value);
}
public async Task<IEnumerable<ReplyDto>> SendHttpRequest()
{
await RequestAuthToken();
return await JsonSerializer.DeserializeAsync<IEnumerable<ReplyDto>>(
await _httpClient.GetStreamAsync("api/getendpoint"),
new JsonSerializerOptions {
PropertyNameCaseInsensitive = true
});
}

Related

Trying to force a user after login to change password

I'm trying to force an authenticated user to change password based on a database Boolean flag. I have used asp.net core resource filter to do that. When ChangePassword flag is on I'm hitting this error. If the ChangePassword flag is is false, application doesn't encounter any error.
InvalidOperationException: If an IAsyncResourceFilter provides a result value by setting the Result property of ResourceExecutingContext to a non-null value, then it cannot call the next filter by invoking ResourceExecutionDelegate.
My codes are as follows -
public class ChangePasswordFilter: IAsyncResourceFilter
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly IUrlHelperFactory _urlHelperFactory;
public ChangePasswordFilter(UserManager<ApplicationUser> userManager, IUrlHelperFactory urlHelperFactory)
{
_userManager = userManager;
_urlHelperFactory = urlHelperFactory;
}
public async Task OnResourceExecutionAsync(ResourceExecutingContext context, ResourceExecutionDelegate next)
{
var HttpContext = context.HttpContext;
var urlHelper = _urlHelperFactory.GetUrlHelper(context);
var redirectUrl = urlHelper.Page("/UserManagement/ChangePassword");
var currentUrl = context.HttpContext.Request.Path;
if (redirectUrl != currentUrl)
{
var user = await _userManager.GetUserAsync(context.HttpContext.User);
if (user?.ChangePassword ?? false)
{
//context.Result = new RedirectResult(redirectUrl);
context.Result = new RedirectToActionResult("ChangePassword", "UserManagement", new { ReturnUrl = HttpContext.Request.Path });
}
}
await next();
}
And in startup.ConfigureServices
services.AddScoped<ChangePasswordFilter>();
services.AddMvc(o =>
{
o.Filters.Add(typeof(ChangePasswordFilter));
});
This example is taken from this answer
Force user change password when loading any webpage
Any help appreciated.
Thanks.
The link you referenced is for a question on asp.net core 2.0. There are many differences between asp.net core 2.0 and asp.net core 5.0.
However, the error message is quite clear. You are invoking the ResourceExecutionDelegate after setting the ResourceExecutingContext Result property to a non-null value.
I'm not too familiar with ResourceFilters but what happens if you replace await next(); with return;?
I was having the same issue, while trying to implement this same code.
I did add return; after context.Result, and my code is working fine.
My code:
context.Result = new RedirectResult(redirectUrl);
return;
Hope you find this helpful.

Return a custom response when using the Authorize Attribute on a controller

I have just implemented the Bearer token and I have added the Authorize attribute to my controller class and that works fine. It looks like this:
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
What I would like to do is to create a more complex response from the server when it fails, rather then the standard 401.
I tried filters but they are not invoked at all.
Any ideas how to do this?
Have a custom scheme, custom authorization handler and poof!
Notice that I injected the Handler in ConfigureServices:
services.AddAuthentication(options =>
{
options.DefaultScheme = ApiKeyAuthenticationOptions.DefaultScheme;
options.DefaultAuthenticateScheme = ApiKeyAuthenticationOptions.DefaultScheme;
})
.AddScheme<ApiKeyAuthenticationOptions, ApiKeyAuthenticationHandler>(
ApiKeyAuthenticationOptions.DefaultScheme, o => { });
ApiKeyAuthenticationOptions
public class ApiKeyAuthenticationOptions : AuthenticationSchemeOptions
{
public const string DefaultScheme = "API Key";
public string Scheme => DefaultScheme;
public string AuthenticationType = DefaultScheme;
public const string HeaderKey = "X-Api-Key";
}
ApiKeyAuthenticationHandler
/// <summary>
/// An Auth handler to handle authentication for a .NET Core project via Api keys.
///
/// This helps to resolve dependency issues when utilises a non-conventional method.
/// https://stackoverflow.com/questions/47324129/no-authenticationscheme-was-specified-and-there-was-no-defaultchallengescheme-f
/// </summary>
public class ApiKeyAuthenticationHandler : AuthenticationHandler<ApiKeyAuthenticationOptions>
{
private readonly IServiceProvider _serviceProvider;
public ApiKeyAuthenticationHandler(IOptionsMonitor<ApiKeyAuthenticationOptions> options, ILoggerFactory logger,
UrlEncoder encoder, ISystemClock clock, IServiceProvider serviceProvider)
: base (options, logger, encoder, clock)
{
_serviceProvider = serviceProvider;
}
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
var token = Request.Headers[ApiKeyAuthenticationOptions.HeaderKey];
if (string.IsNullOrEmpty(token)) {
return Task.FromResult (AuthenticateResult.Fail ("Token is null"));
}
var customRedisEvent = _serviceProvider.GetRequiredService<ICustomRedisEvent>();
var isValidToken = customRedisEvent.Exists(token, RedisDatabases.ApiKeyUser);
if (!isValidToken) {
return Task.FromResult (AuthenticateResult.Fail ($"Invalid token {token}."));
}
var claims = new [] { new Claim ("token", token) };
var identity = new ClaimsIdentity (claims, nameof (ApiKeyAuthenticationHandler));
var ticket = new AuthenticationTicket (new ClaimsPrincipal (identity), Scheme.Name);
return Task.FromResult (AuthenticateResult.Success (ticket));
}
}
Focus on the handler class. Apart from the sample code I've provided, simply utilise the base class properties like Response to set your custom http status code or whatever you may need!
Here's the derived class if you need it.
https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.authentication.authenticationhandler-1?view=aspnetcore-3.1

Automatically request accessToken using Feign

there are numerous posts explaining how to add dynamic header into request using Feign Interceptor
#Bean
public RequestInterceptor requestInterceptor() {
#Override
public void apply(RequestTemplate requestTemplate) {
var jwtToken = refreshAccessTokenClient.refresh();
requestTemplate.header("Authorization", jwtToken);
}
}
is there any possibility to share jwtToken across many threads and refresh in periodically? Current solution is sub-optimal and makes 1 extra call each time.
I came up with solution:
#Bean
public RequestInterceptor requestInterceptor(#Value("${service.login}") String login,
#Value("${service.apiKey}") String apiKey) {
return
requestTemplate -> {
var request = LoginRequest.builder().loginId(login).apiKey(apiKey).build();
var jwtToken = authAuthProvider.login(request).getData().getAccessToken();
requestTemplate.header("Authorization", "Bearer " + jwtToken);
};
}
I blindly request jwtToken for every API call. It doesn't look like effective solution. Should look for some #Cacheable thing with TLL?

How does REST authentication work for client-side apps?

I'm trying to design my first public API, and I'm trying to learn how REST works with authentication, especially in the context of completely client-side apps using js-frameworks, e.g., angularJS.
Say you have a client which is a browser application (i.e., HTML, JS, CSS only) served as static files from something like nginx using a javascript framework to consume a REST service from, e.g. something that requires a secret access key that's used to create a signature for each request to the service, something like Amazon S3.
In terms of authentication in this scenario, where you don't have a server-side application, how would the secret access key be handled, i.e., how do you get it, where do you store it, etc.? It would seem like a horrible security situation to serve the key for each request (even if it only happens once to bootstrap the application).
And even if you do have a light server-side application--how do you securely inform the client (which still calls the authenticated 3rd party API itself) what the signature should be for every request it could possibly make? I'm very confused by how this is supposed to be designed from either end.
I've done a few AngularJS apps and the way that I've found is to use an HttpModule like this one:
using System;
using System.Net.Http.Headers;
using System.Security.Principal;
using System.Text;
using System.Threading;
using System.Web;
namespace YourSolution.WebApp.Modules
{
public class BasicAuthenticationHttpModule : IHttpModule
{
public BasicAuthenticationHttpModule()
{
}
public void Init(HttpApplication context)
{
context.AuthenticateRequest += OnApplicationAuthenticateRequest;
context.EndRequest += OnApplicationEndRequest;
}
private static void SetPrincipal(IPrincipal principal)
{
Thread.CurrentPrincipal = principal;
if (HttpContext.Current != null)
{
HttpContext.Current.User = principal;
}
}
private static bool CheckPassword(
string username, string password)
{
return username == password;
}
private static void AuthenticateUser(string credentials)
{
try
{
var encoding = Encoding.GetEncoding(
"iso-8859-1");
credentials = encoding.GetString(
Convert.FromBase64String(credentials));
var separator = credentials.IndexOf(':');
var name = credentials.Substring(0, separator);
var password = credentials.Substring(separator + 1);
var validated = CheckPassword(name, password);
if (!validated) return;
var identity = new GenericIdentity(name);
SetPrincipal(new GenericPrincipal(identity, null));
}
catch (FormatException)
{
}
}
private static void OnApplicationAuthenticateRequest(
object sender, EventArgs e)
{
var request = HttpContext.Current.Request;
var authHeader = request.Headers["Authorization"];
if (authHeader == null) return;
var authHeaderVal = AuthenticationHeaderValue.Parse(authHeader);
if (authHeaderVal.Scheme.Equals(
"basic",
StringComparison.OrdinalIgnoreCase)
&& authHeaderVal.Parameter != null)
{
AuthenticateUser(authHeaderVal.Parameter);
}
}
private static void OnApplicationEndRequest(
object sender, EventArgs e)
{
var response = HttpContext.Current.Response;
if (response.StatusCode == 401)
{
//response.Headers.Add(
// "WWW-Authenticate",
// string.Format("Basic realm=\"{0}\"", Realm));
}
}
public void Dispose()
{
}
}
}
The most important part is inside CheckPassword method, there is where you should validate the credentials.
Another point is this line response.Headers.Add("WWW-Authenticate", string.Format("Basic realm=\"{0}\"", Realm)); if you don't comment this line, the classic login requested form will show up, and if you do comment this line you have to catch the 401 error in your requests.
If you want to know about realm: What is the “realm” in basic authentication.
Plus, you will need to register the module in your web.config file:
<system.webServer>
<modules>
<add
name="BasicAuthenticationHttpModule"
type="YourSolution.WebApp.Modules.BasicAuthenticationHttpModule" />
</modules>
</system.webServer>
Then I've added these two methods to deal with the authentication token:
// u: username; p: password
CreateBasicAuthenticationToken = function (u, p) {
var t = u + ':' + p;
var hat = btoa(t);
window.sessionStorage.setItem('basicauthtoken', 'basic ' + hat);
};
DestroyBasicAuthenticationToken = function () {
window.sessionStorage.removeItem('basicauthtoken');
};
The btoa method: The btoa() method of window object is used to convert a given string to a encoded data (using base-64 encoding) string.. Taken from: http://www.w3resource.com/javascript/client-object-property-method/window-btoa.php.
And last I've added the authtoken to the request header using the beforeSend:
$.ajax({
type: 'GET',
url: 'your url',
beforeSend: function (xhr) {
window.sessionStorage.getItem('basicauthtoken');
}
}).done(function (data, textStatus, xhr) {
//...
});
Please do note using jQuery outside an angular directive is not recommended, AngularJS best practices dictates jQuery code must be always placed inside a directive.
Hope it helps.

JERSEY - Accessing Generic List in Response

Im facing isssue in getting Jersey Generic List in client response. I need to get it as Entity for some reason.
#XmlRootElement(name="list")
#XmlSeeAlso({RESTDomain.class})
public class JAXBContainer<T> {
private List<T> items = new ArrayList<T>();
public JAXBContainer() { }
public JAXBContainer(List<T> items) {
this.items = items;
}
#XmlElementWrapper(name="items")
#XmlAnyElement(lax=true)
public List<T> getItems() {
return items;
}
public void setItems(List<T> items) {
this.items = items;
}
#XmlAttribute
public int getItemsSize() {
return this.items.size();
}
above is my Generic List to the resopnse
#GET
#Produces({MediaType.APPLICATION_XML})
public Response getREST(){
RESTDomain domain = new RESTDomain();
domain.setName("Adams");
domain.setPlace("Zurich");
List<RESTDomain> restDomains = new ArrayList<RESTDomain>();
restDomains.add(domain);
JAXBContainer<RESTDomain> jAXBContainer= new JAXBContainer<RESTDomain>(restDomains);
GenericEntity<JAXBContainer<RESTDomain>> genericEntity = new GenericEntity<JAXBContainer<RESTDomain>>(jAXBContainer){};
return Response.ok(genericEntity).build();
}
Im returning the container with genericEntity. I know with just List inside genericEntity i can get my Entity at my client but the problem is i need to Use my JAXBContainer for some reason.
#Test
public void restGet() throws JAXBException{
ClientConfig cc = new DefaultClientConfig();
client = Client.create(cc);
String baseURI ="http://localhost:3555/SampleREST/rest/sample";
WebResource webResource = client.resource(baseURI);
JAXBContainer<RESTDomain> jAXBContainer= webResource.get(new GenericType<JAXBContainer<RESTDomain>>(){});
System.out.println("response:: "+jAXBContainer.getItemsSize());
}
My problem is im getting the response as JAXBContainer with GenericType as desired but the size of container is 0. What am i missing here? do i have to Use any marshalling and unmarshalling Mechanisms.
But When i request this URI in browser i get the well formed XML, But it fails in client or do we have any other ways to extract entity in client. Thanks in advance
I don't see that you're setting the accept content type anywhere on the client.
Try with: webResource.accept("application/xml")