Can I add a service info / health check endpoint to my Identity Server 3-based service? - extend

I have a set of AspNet WebApi-based web services and an IdentityServer3-based authentication service. All of the web services support a simple service info endpoint that we use for monitoring and diagnosis. It reports the service version and the server name. The only service that currently does not support the service info endpoint is the IdentityServer3-based authentication service.
Is there a way to add a simple endpoint to an IdentityServer3-based service? In GitHub issue 812 Brock Allen says "We have a way to add custom controllers, but it's undocumented, current unsupported, and not really done." I'd rather not take that indocumented, unsupported route.
Is there a way to modify/extend the discovery endpoint to include additional information?

Here's how I ended up coding this up. At a high level, basically I added a Controllers folder, created a AuthenticationServiceInfoController class with a single GET action method and then registered that controller during Startup. As noted in comment above, my solution had some extra complexity because my AuthenticationServiceInfoController inherited from a base ServiceInfoController defined elsewhere, but I've tried to eliminate that from this sample. So, the controller code looks like this:
[RoutePrefix("api/v1/serviceinfo")]
public class AuthencticationServiceInfoController : IServiceInfoController
{
[Route("")]
[Route("~/api/serviceinfo")]
public IHttpActionResult Get()
{
try
{
ServiceInformation serviceInfo = new ServiceInformation();
serviceInfo.ServiceVersion = Global.serviceVersion;
return Ok(serviceInfo);
}
catch (Exception ex)
{
return InternalServerError(ex);
}
}
}
It implements a simple interface:
public interface IServiceInfoController
{
IHttpActionResult Get();
}
And in my Startup.Configuration method where I configure Identity Server, I've got:
var idSrvFactory = new IdentityServerServiceFactory();
idSrvFactory.Register(new Registration<IServiceInfoController, Controllers.AuthencticationServiceInfoController>());
I think that's all that it took. It's in place and working in my Identity Server 3-based service.

Related

Register dependent services on every request

I am working in Multi-tenant solution primarily there are 2 type of applications
WebAPI
Console app to process message from queue
I have implemented dependency injection to inject all services. I have crated TenantContext class where I am resolving tenant information from HTTP header and it's working fine for API, but console application getting tenant information with every message (tenant info is part of queue message) so I am calling dependency injection register method on every incoming message which is not correct, do you have any suggestion/solution here?
The way I am resolving ITenantContext in API
services.AddScoped<ITenantContext>(serviceProvider =>
{
//Get Tenant from JWT token
if (string.IsNullOrWhiteSpace(tenantId))
{
//1. Get HttpAccessor and processor settings
var httpContextAccessor =
serviceProvider.GetRequiredService<IHttpContextAccessor>();
//2. Get tenant information (temporary code, we will get token from JWT)
tenantId = httpContextAccessor?.HttpContext?.Request.Headers["tenant"]
.FirstOrDefault();
if (string.IsNullOrWhiteSpace(tenantId))
//throw bad request for api
throw new Exception($"Request header tenant is missing");
}
var tenantSettings =
serviceProvider.GetRequiredService<IOptionsMonitor<TenantSettings>>();
return new TenantContext(tenantId, tenantSettings );
});
Create two different ITenantContext implementations. One for your Web API, and one for your Console application.
Your Web API implementation than might look as follows:
public class WebApiTenantContext : ITenantContext
{
private readonly IHttpContextAccessor accessor;
private readonly IOptionsMonitor<TenantSettings> settings;
public WebApiTenantContext(
IHttpContextAccessor accessor,
IOptionsMonitor<TenantSettings> settings)
{
// Notice how the dependencies are not used in this ctor; this is a best
// practice. For more information about this, see Mark's blog:
// https://blog.ploeh.dk/2011/03/03/InjectionConstructorsshouldbesimple/
this.accessor = accessor;
this.settings = settings;
}
// This property searches for the header each time its called. If needed,
// it can be optimized by using some caching, e.g. using Lazy<string>.
public string TenantId =>
this.accessor.HttpContext?.Request.Headers["tenant"].FirstOrDefault()
?? throw new Exception($"Request header tenant is missing");
}
Notice that this implementation might be a bit naive for your purposes, but hopefully you'll get the idea.
This class can be registered in the Composition Root of the Web API project as follows:
services.AddScoped<ITenantContext, WebApiTenantContext>();
Because the WebApiTenantContext has all its dependencies defined in the constructor, you can do a simple mapping between the ITenantContext abstraction and the WebApiTenantContext implementation.
For the Console application, however, you need a very different approach. The WebApiTenantContext, as shown above, is currently stateless. It is able to pull in the required data (i.e. TenantId) from its dependencies. This probably won't work for your Console application. In that case, you will likely need to manually wrap the execution of each message from the queue in a IServiceScope and initialize the ConsoleTenantContext at the beginning of that request. In that case, the ConsoleTenantContext would look merely as follows:
public class ConsoleTenantContext : ITentantContext
{
public string TenantId { get; set; }
}
Somewhere in the Console application's Composition Root, you will have to pull messages from the queue (logic that you likely already have), and that's the point where you do something as follows:
var envelope = PullInFromQueue();
using (var scope = this.serviceProvider.CreateScope())
{
// Initialize the tenant context
var context = scope.ServiceProvider.GetRequiredService<ConsoleTenantContext>();
content.TenantId = envelope.TenantId;
// Forward the call to the message handler
var handler = scope.ServiceProvider.GetRequiredService<IMessageHandler>();
handler.Handle(envelope.Message);
}
The Console application's Composition Root will how have the following registrations:
services.AddScoped<ConsoleTenantContext>();
services.AddScoped<ITenentContext>(
c => c.GetRequiredServices<ConsoleTenantContext>());
With the registrations above, you register the ConsoleTenantContext as scoped. This is needed, because the previous message infrastructure needs to pull in ConsoleTenantContext explicitly to configure it. But the rest of the application will depend instead on ITenantContext, which is why it needs to be registered as well. That registration just forwards itself to the registered ConsoleTenantContext to ensure that both registrations lead to the same instance within a single scope. This wouldn't work when there would be two instances.
Note that you could use the same approach for Web API as demonstrated here for the Console application, but in practice it's harder to intervene in the request lifecycle of Web API compared to doing that with your Console application, where you are in full control. That's why using an ITenantContext implementation that is itself responsible of retrieving the right values is in this case an easier solution for a Web API, compared to the ITenantContext that is initialized from the outside.
What you saw here was a demonstration of different composition models that you can use while configuring your application. I wrote extensively about this in my series on DI Composition Models on my blog.

WCF Task based asynchronous callback

I have created WCF service in VS2015:
[ServiceContract(CallbackContract = typeof(IMyCallback))]
public interface IMyService { }
IMyCallback looks like:
[ServiceContract]
public interface IMyCallback {
[OperationContract]
Task<string> OnServerEvent(UserAppEventData evData);
I've built the server, run it, then added service reference (by right click on solution explorer).
The client object is defined as
[CallbackBehaviorAttribute(
ConcurrencyMode = ConcurrencyMode.Reentrant,
IncludeExceptionDetailInFaults = true,
UseSynchronizationContext = true,
ValidateMustUnderstand = true
)]
public class QMyClient : IMyCallback { }
Automatically generated interface implementation made method in sync manner:
public string OnServerEvent(UserAppEventData evData) { }
This code does't work (and isn't asynchronous) and hangs client at OnServerEvent.
When I changed code manuallly to
public async Task<string> OnServerEvent(UserAppEventData evData)
and have done the same in auto generated "service references\...\Reference.cs, all works fine. But I don't want to change Referenece.cs every time I'm updating Service Reference.
Is there any method to force "Update Service Reference" make TBA OperationContractAttribute on callback?
At ordinary WCF service direction everything works OK, VS generates task based operations.
By default the service reference you've added to solution doesn't have asynchronous operations, but you can enable them and decide which option you use for your async methods - task-based or old-fashion asynchronous. This option is available in Advanced settings for service reference.
If you're using a svcutil tool, it will create the task-based methods by default, however, you can change that behavior by some flags like /async or /syncOnly.
What #VMAtm suggested will work out just fine.
I think, you could also use ChannelFactory for this scenario. It is very flexible and you can then await on the service operations from client side. Additional benefit, you don't need to modify client when there are these kind of changes on service side.
Something like:
var channelFactory = new ChannelFactory<IService>(
"WSHttpBinding_IService" // endpoint name
);
IService channel = channelFactory.CreateChannel();
string result = await channel.OnServerEvent();
Console.WriteLine(result);
Please note that for this scenario, you will have to import common interface library to client side as dll because then it will need to know about contracts and data contracts.

How should I consume a WCF service using MVC3

I am writing a fairly simple MVC3 application that allows a user to retrieve and modify some configuration data held by a WCF service. The configuration data will not change very often but needs to be maintainable by the user.
Once the user is happy with the configuration, they will launch some processing from the UI or by the scheduled execution of a VB script.
I'm fairly new to WCF and even newer to MVC3 but I'd like to make all the comms to and from the service RESTful. Is this a good thing to do?
I'd been planning to perform the service communications from the MVC controller. This would make a HTTP Get request to retrieve the current configuration and a HTTP Post to apply the modified configuration. I'd also use a Get request to launch the processing.
Could anyone provide an example (or point me in the direction) of how I should be doing this?
Please follow the below links for some sample code to build a RESTful WCF Service and the how the client would access the service.
Link to create a RESTful service: here
Link to create a .NET client that consumes the RESTful service : here
Hope the information helps you out.
If you are the owner of web service, you can directly reference implementation of service in your mvc project and use it without web reference. You can write some like this:
// example of ws
public class Service1 : IService1
{
public string GetData( int value )
{
return string.Format( "You entered: {0}", value );
}
public CompositeType GetDataUsingDataContract( CompositeType composite )
{
if( composite == null )
{
throw new ArgumentNullException( "composite" );
}
if( composite.BoolValue )
{
composite.StringValue += "Suffix";
}
return composite;
}
}
// example of mvc action
public class HomeController : Controller
{
public ActionResult Index()
{
IService1 service = new Service1();
service.GetDataUsingDataContract(....)
ViewBag.Message = "Welcome to ASP.NET MVC!";
return View();
}
}
The simplest answer I've found was here:, using the channel factory.
I still don't know if it's the best way but it left my code looking clean enough. Below is a sample from my controller.
public ActionResult Index()
{
SettingsModel config = null;
// Set up a channel factory to use the webHTTPBinding
using (WebChannelFactory<IChangeService> serviceChannel =
new WebChannelFactory<IChangeService>(new Uri(baseServiceUrl )))
{
IChangeService channel = serviceChannel.CreateChannel();
config = channel.GetSysConfig();
}
ViewBag.Message = "Service Configuration";
return View(config);
}

RESTful framework alternatives to WCF

Looking for alternatives to the WCF REST start kit, ideally OSS frameworks.
Anyone got a list?
Cheers
Ollie
OpenRASTA is the most mature
ASP.NET MVC is a good alternative when it comes to generating REST XML and JSON feeds.
To build a rest architecture in .net you can use GenericHandlers. You can create a GenericHandler that will receive a HTTP message (POST, GET or..) and return a message of the content-type you specify.
For example I create a generic handler on the url:
http://site/getpeople.ashx?gender=female
And call it with the parmeter gender=female, as above the handler will return the following
<people>
<person>...</person>
...
<people>
And the content type would be text/xml.
This is the simplest way to implement REST web services in .NET
I also provide ServiceStack, a modern, code-first, DTO-driven, WCF replacement web services framework encouraging code and remote best-practices for creating DRY, high-perfomance, scalable REST web services.
There's no XML config, or code-gen and your one clean C# web service is enabled on all JSON, XML, SOAP, JSV, CSV, HTML endpoints out-of-the-box, automatically. It includes generic sync/async service clients providing a fast, typed, client/server communication gateway end-to-end.
It also includes generic sync/async service clients providing a fast, typed, client/server communication gateway end-to-end.
This is the complete example of all the code needed to create a simple web service, that is automatically without any config, registered and made available on all the web data formats on pre-defined and custom REST-ful routes:
public class Hello {
public string Name { get; set; }
}
public class HelloResponse {
public string Result { get; set; }
}
public class HelloService : IService<Hello> {
public object Execute(Hello request) {
return new HelloResponse { Result = "Hello, " + request.Name };
}
}
Above service can be called (without any build-steps/code-gen) in C# with the line below:
var response = client.Send<HelloResponse>(new Hello { Name = "World!" });
Console.WriteLine(response.Result); // => Hello, World
And in jQuery with:
$.getJSON('hello/World!', function(r){
alert(r.Result);
});

How can I forms authenticate a user in a custom webservice?

I am working on integrating a silverlight site into our existing application and am trying to get the login functionality working. The Silverlight application needs to have it's own login page, and the login needs to utilize the existing ASP.NET forms authentication. As part of the login procedure, we are calling some external code, so using the scriptable methods that System.Web.ApplicationServices.AuthenticationService exposes is not an option. I tried to use FormsAuthentication.Authenticate to do this, but it didn't work. Does anyone have any ideas on how to get around this?
It sounds as though you need to create a wrapper websevice which can implement the forms authentication support.
This is something I've done so for example I've created a WCF service with the following interface which is referenced by my Silverlight client:
[ServiceContract]
public interface IAuthenticationService
{
[OperationContract()]
string Login(string username, string password, bool isPersistent);
[OperationContract()]
bool Logout();
[OperationContract()]
string IsLoggedIn();
}
and then in my implementation you can call custom code and also use the forms authentication api, for example to login you could have:
try
{
//Call you external code here
//Then use the membership provider to authenticate
if (Membership.ValidateUser(username, password))
{
FormsAuthentication.SetAuthCookie(username, isPersistent);
}
}
catch (Exception ex)
{
Logging.LogException("Error in Login", ex);
}
Also not you need to include the following attribute above you class definition in your service implementation to have asp.net compat enabled which will give you access to the HttpContext:
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
The solution is simple. Just create a custom membership provider that calls your custom code. See this article on MSDN library for more information. There are also full samples available on 15 seconds and a walkthrough video on the ASP.NET website. Finally, it appears Microsoft has released the source for the built-in Membership Provider