I am having below situation : I am using web api core 3.1 framework (c#)
I am using typed httpclient registered in the startup . while registering typed client on startup, i am not able to provide the base URL and credentials because I am getting thru a service called configread and it reads the data from the header , which will be only available when one of our middle ware runs and sets it.
in my case base address, user id and passwords are coming from a service call but service calls depends on the request header (httpContext object). in the configureService methods , request context is not available.
Right now i am having trouble to get the httpClient from the startup.
Any guidance would be appreciated.
Update1:
I am adding a typed client as below
service.AddHttpClient<IAgencyServiceAgent,AgencyServiceAgent> (GetAgencyAgentHttpClient()).
ConfigurePrimaryHttpMessageHandler(GetAgencyHttpMessageHandler()) private Action<HttpClient> GetAgencyAgentHttpClient ()
{
var configUrl = Environment.GetEnvironmentVariable(ConfigConstants.CONFIGSERVICE URL)
return httpClient => {
// Here the base address is availble thru another service // which accept the data from the httpContext and based on the values / It pulls the base address and request header etc...
}
}
Update2:
I am having difficulty in setting this httpclient in the startup beacuse baseUrl and other info depends on the request object. For ex: i am reading a request header called DEV1 and passing it to another service , then it will return me the base address and credentials needed then after i can set the http client My questions are how do go about it . When httpClient configurations are depend on the httpContext object .. then how we should register and use it Thanks
According to your description, I suggest you could try to build ServiceProvider inside your GetAgencyAgentHttpClient method and use GetService method to get which service you want to use.
More details, you could refer to below codes:
services.AddHttpClient("hello", c =>
{
//Build service provider
ServiceProvider serviceProvider = services.BuildServiceProvider();
//Get the ICurrentUserService
var currentUserService = serviceProvider.GetService<ICurrentUserService>();
//Use ICurrentUserService GetIPaddress method
var re= currentUserService.GetIPaddress();
c.BaseAddress = new Uri("http://localhost:5000");
}).AddTypedClient(c => Refit.RestService.For<IHelloClient>(c)); ;
Result:
If you want to check get the http header from current httprequest, you could try to get the httpcontext accessor in the service.
More details, you could refer to below codes:
services.AddHttpClient("hello", c =>
{
ServiceProvider serviceProvider = services.BuildServiceProvider();
//var currentUserService = serviceProvider.GetService<ICurrentUserService>();
//var re= currentUserService.GetIPaddress();
var httpcontext = serviceProvider.GetService<IHttpContextAccessor>();
var re = httpcontext.HttpContext.Request.Headers.ToList();
c.BaseAddress = new Uri("http://localhost:5000");
}).AddTypedClient(c => Refit.RestService.For<IHelloClient>(c));
Result:
Related
I would like to pass user information to service layer like this (in net core web app):
services.AddScoped<RequestInfo>(provider =>
{
var context = provider.GetRequiredService<IHttpContextAccessor>();
var userJson = ((ClaimsIdentity)context.HttpContext.User.Identity).FindFirst("LoggedInUser").Value;
var user = JsonConvert.DeserializeObject<User>(userJson);
return new RequestInfo(int.Parse(user.UserId), user.ClientId);
});
This should work for controllers and services, but I also have BackgroundService to handle messages. I'd like to use user information from message before I resolve service to handle message. Is it possible to achieve or any workaround? Something like (pseudo code)
using var scope = serviceProvider.CreateScope();
SetupUserInfo(..have no access to ServiceCollection .., contract user info);
var service = scope.ServiceProvider.GetRequiredService(..my type);
Update
My best idea
services.AddScoped<RequestInfo>();
var info = provider.GetRequiredService<RequestInfo>();
info.UserId = .. get info from http context somewhere in middleware (for API)
info.UserId = .. get info from message before resolve of main dependency (for Background service)
I am trying to Azure AD authentication with gRPC-Web in a blazor webassembly app. I am using protobuf-net to help me with the serialization. I am not sure how to pass the token to have the server side recognize it. this is what I have:
var headers = new Metadata
{
{ "Authorization", $"Bearer {Token}" }
};
and, I am sending that as a parameter in the method I want to consume
var result = await Client.CreateCustomer(this.customer, headers);
This is how the service is injected:
builder.Services.AddTransient(services =>
{
var httpClient = new HttpClient(new GrpcWebHandler(GrpcWebMode.GrpcWeb, new HttpClientHandler()));
var channel = Grpc.Net.Client.GrpcChannel.ForAddress("****", new GrpcChannelOptions { HttpClient = httpClient });
return channel.CreateGrpcService<Application.Services.ICustomerService<ServerCallContext>>();
});
This is how the service is published:
endpoints.MapGrpcService<CustomerService>().RequireAuthorization().EnableGrpcWeb()
and, this is the implementation:
public class CustomerService : ICustomerService<ServerCallContext>
{
[Authorize]
public async ValueTask<Customer> CreateCustomer(Customer customerDTO, ServerCallContext context)
{****}
}
the error I am getting is cannot convert from 'Grpc.Core.Metadata' to 'Grpc.Core.ServerCallContext' which is kind of obvious.
The reference I have found uses Metadata but is ServerCallContext the one I am supposed to use https://learn.microsoft.com/en-us/dotnet/architecture/grpc-for-wcf-developers/metadata so what I am missing, what I am doing wrong, how to properly use both using protobuf-net?
It looks like the problem here is that you're using ServerCallContext in the method signature; the underlying gRPC core has separate client/server context APIs, but this is not amenable to use on an agnostic interface, and as such, protobuf-net.Grpc unifies these two APIs, via CallContext. So: instead of:
async ValueTask<Customer> CreateCustomer(Customer customerDTO, ServerCallContext context)
for the signature, consider:
async ValueTask<Customer> CreateCustomer(Customer customerDTO, CallContext context)
or
async ValueTask<Customer> CreateCustomer(Customer customerDTO, CallContext context = default)
The CallContext API exposes the common server-side and client-side APIs (headers, cancellation, etc) in a single way, or you can use (for example) context.ServerCallContext to get the server-specific API if needed (this will throw an exception if used on a client-context). For client-side usage, a CallContext can be constructed from a CallOptions, which is the core gRPC client-side API, for example:
var result = await service.CreateCustomer(customer, new CallOptions(headers));
I'm open to the idea of allowing CallContext to be created directly from Metadata / CancellationToken etc (allowing var result = await service.CreateCustomer(customer, headers);) - but it doesn't seem essential.
We're evaluating service stack v.4.5.6.0 for a Web API and we want clients to be able to authenticate using basic auth or credentials but we do not want them to be able to provide a basic auth header in place of a JWT token or session cookie when using our services. While I realize this is somewhat arbitrary, is there a way to exclude routes from specific providers or force the use of a token/cookie to authenticate once they've logged in?
Auth config from AppHost:
private void ConfigureAuth(Container container)
{
var appSettings = new AppSettings();
this.Plugins.Add(new AuthFeature(() => new CustomAuthUserSession(),
new IAuthProvider[]
{
new CredentialsAuthProvider(),
new BasicAuthProvider(),
new JwtAuthProvider(appSettings)
}) { IncludeAssignRoleServices = false, MaxLoginAttempts = 10} );
var userRepository = new CustomUserAuthRepository(container.TryResolve<IDbConnectionFactory>());
container.Register<IAuthRepository>(userRepository);
}
ServiceStack lets you decide which AuthProviders you want your Services to be authenticated with, but it doesn't let you individually configure which adhoc AuthProviders applies to individual Services. Feel free to add this a feature request.
However if you want to ensure that a Service is only accessed via JWT you can add a check in your Services for FromToken which indicates the Session was populated by a JWT Token, e.g:
[Authenticate]
public class MyServices : Service
{
public object Any(MyRequest request)
{
var session = base.SessionAs<AuthUserSession>();
if (!session.FromToken)
throw HttpError.Unauthorized("Requires JWT Authentication");
//...
}
}
From v4.5.7 that's now available on MyGet you can also use the new session.AuthProvider property which indicates what AuthProvider was used to Authenticate the user, e.g:
public object Any(MyRequest request)
{
var session = base.SessionAs<AuthUserSession>();
if (session.AuthProvider != JwtAuthProvider.Name)
throw HttpError.Unauthorized("Requires JWT Authentication");
//...
}
Refer to the docs for different AuthProvider names for each AuthProvider.
I have a WCF client used in MVC application which can get data from multiple WCF services, the services are configured the same way and Implement the same Interface the only difference is the address of the exposed endpoint.
This is what I tried:
builder.Register(c => new ChannelFactory<IService>(
new BasicHttpBinding(),
new EndpointAddress("http://service.com/Service")))
.InstancePerHttpRequest();
builder.Register(c => c.Resolve<ChannelFactory<IService>>().CreateChannel())
.UseWcfSafeRelease();
The thing here is that IService will always get data from http://service.com/Service since the address is hardcoded somewhere in the Application_Start method of the MVC application.
Then i tried using metadata:
builder.Register(c => new ChannelFactory<IService>(
new BasicHttpBinding(),
new EndpointAddress("http://foo.com/Service")))
.SingleInstance().WithMetadata("name", "fooservice");
builder.Register(c => new ChannelFactory<IService>(
new BasicHttpBinding(),
new EndpointAddress("http://bar.com/Service")))
.SingleInstance().WithMetadata("name", "barservice");
builder.Register(c => c.Resolve<ChannelFactory<IService>>().CreateChannel())
.UseWcfSafeRelease();
But this way I will have to edit the code every time I want to add the same WCF service
implemented on a different server.Instead I want to get the address from the database.
Is there any way I can change the address per service call or at least when the instance of the client is created.
Additional explanation:
Lets say I have five exact copies of a website each with it's own domain name and database I want to be able to do the following:
foreach(Provider provider in providers)
{
SetServiceAddress(provider.Address);//how can i do that
_service.GetData()
}
Under the assumptions that:
The binding doesn't change when the address changes (e.g., it doesn't switch from HTTP to HTTPS)
The address might change on a per-request basis
Then I'd probably solve it with a combination of lambdas and a small interface.
First, you'd want something that retrieves the address from your data store:
public interface IAddressReader
{
Uri GetAddress();
}
The implementation of that would read from the database (or environment, or XML config, or whatever).
Then I'd use that in my registrations:
builder
.RegisterType<MyDatabaseAddressReader>()
.As<IAddressReader>();
builder
.Register(c => new ChannelFactory<IService>(new BasicHttpBinding()))
.SingleInstance();
builder
.Register(c =>
{
var reader = c.Resolve<IAddressReader>();
var factory = c.Resolve<ChannelFactory<IService>();
var endpoint = new EndpointAddress(reader.GetAddress());
return factory.CreateChannel(endpoint);
})
.As<IService>()
.UseWcfSafeRelease();
That way you can just take in an IService (or Func<IService>) as a constructor parameter and your calling class won't know about Autofac, service location, or endpoints.
If the binding also changes, it gets a little more complicated. You probably don't want a brand new channel factory spun up for every channel, so you'd want to have some sort of caching mechanism where you:
Get the settings from the configuration source.
Compares those settings against the settings currently in use.
If the settings don't match...
Dispose of the previous channel factory.
Create a new channel factory with the new settings.
Cache the channel factory for later reuse.
Return the current channel factory.
If you can use cache dependencies on the settings, all the better, but not every configuration source supports that, so YMMV. I'd probably implement a custom module for that to encapsulate the logic, but I won't write all that out here.
If you want to set the endpoint just before the call each time, you can do this:
containerBuilder
.Register(c => new ChannelFactory<IService>(new BasicHttpBinding()))
.SingleInstance();
containerBuilder.Register((c, p) =>
{
var factory = c.Resolve<ChannelFactory<IService>>();
var endpointAddress = p.TypedAs<string>();
return factory.CreateChannel(new EndpointAddress(endpointAddress));
})
.As<IService>()
.UseWcfSafeRelease();
Then you inject this:
Func<string, IService> getService
Then call it like this:
string endpoint = getDataDependentEndpointFromSomewhere();
var service = getService(endpoint);
I have a service that is running on multiple sites, and at start-up the app needs to determine at which site it is running. It does so using a start-up parameter, and based on this the endpoint address can be set dynamically in a property or method, like GetEndPointAddressForService().
In your case it seems that you need to call n services at different sites consecutively. You could definitely configure these in a database or a simple configuration file on disk, load the service definitions including their endpoint addresses at start-up, keep them in a list and do a foreach when collecting data from all existing servers.
The key part of your logic is in the following part of your code:
new EndpointAddress("http://bar.com/Service")
Do a
foreach (ServiceDefinition sd in ServiceDefinitions)
{
builder.Register(c => new ChannelFactory<IService>(
new BasicHttpBinding(),
new EndpointAddress(sd.EndPointAddress)))
.InstancePerHttpRequest();
builder.Register(c => c.Resolve<ChannelFactory<IService>>().CreateChannel())
.UseWcfSafeRelease();
GoGetTheData();
}
At the end I have used the following implementation:
On application start I register the ChannelFactory type without the endpoint address.
And I use named parameter to register the client so i can be able to assign the address later when I actually call the service.
builder.RegisterType<ChannelFactory<IService>>(new BasicHttpBinding())
.SingleInstance();
builder.Register((c, p) => c.Resolve<ChannelFactory<IService>>().CreateChannel(p.Named<EndpointAddress>("address")))
.UseWcfSafeRelease();
and then I use the service client at runtime like this:
public Data GetData(string url)
{
EndpointAddress address = new EndpointAddress(url);
NamedParameter parameter = new NamedParameter("address", address);
var service = _autofacContainer.Resolve<IService>(parameter);//this is what I have been looking for
Response response = service.GetData();
return CreateDataFromResponse(response);
}
this way I can call the GetData method for each address in the database. And I'm going to able to add more addresses at runtime without code or configuration editing.
Can anyone point me to a suitable WCF Extension Point for hooking into the WCF Pipeline to extract credentials for UserNamePasswordValidator from the headers of an incoming HTTP REST Request?
Yes I know about all the funky stunts with Http Handlers etc. you can pull to somehow get Basic/Digest Auth working but since the client I'm working on will be strictly Javascript based I've opted for a simple model where the credentials are passed using two custom headers over an SSL pipe.
Update: I've managed to improve on this by using the approach described here. While this does not solves the problem described in my question, it gets rid of having to authenticate in a authorization policy since authentication is now handled by a custom AuthenticationManager, bypassing the UsernamePasswordValidator alltogether.
For the time being I've solved the problem by combining Authentication and Authorization in a custom Authorization Policy. I'd still rather find a way to hook into the normal UserNamePasswordValidator authentication scheme because an Authorization Policy is supposed to to Authorization not Authentication.
internal class RESTAuthorizationPolicy : IAuthorizationPolicy
{
public RESTAuthorizationPolicy()
{
Id = Guid.NewGuid().ToString();
Issuer = ClaimSet.System;
}
public bool Evaluate(EvaluationContext evaluationContext, ref object state)
{
const String HttpRequestKey = "httpRequest";
const String UsernameHeaderKey = "x-ms-credentials-username";
const String PasswordHeaderKey = "x-ms-credentials-password";
const String IdentitiesKey = "Identities";
const String PrincipalKey = "Principal";
// Check if the properties of the context has the identities list
if (evaluationContext.Properties.Count > 0 ||
evaluationContext.Properties.ContainsKey(IdentitiesKey) ||
!OperationContext.Current.IncomingMessageProperties.ContainsKey(HttpRequestKey))
return false;
// get http request
var httpRequest = (HttpRequestMessageProperty)OperationContext.Current.IncomingMessageProperties[HttpRequestKey];
// extract credentials
var username = httpRequest.Headers[UsernameHeaderKey];
var password = httpRequest.Headers[PasswordHeaderKey];
// verify credentials complete
if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))
return false;
// Get or create the identities list
if (!evaluationContext.Properties.ContainsKey(IdentitiesKey))
evaluationContext.Properties[IdentitiesKey] = new List<IIdentity>();
var identities = (List<IIdentity>) evaluationContext.Properties[IdentitiesKey];
// lookup user
using (var con = ServiceLocator.Current.GetInstance<IDbConnection>())
{
using (var userDao = ServiceLocator.Current.GetDao<IUserDao>(con))
{
var user = userDao.GetUserByUsernamePassword(username, password);
...