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

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

Related

.Net 6: Enable Windows and Anonymous authentication for one

I work on a .Net core application and I need to mix windows and anonymous authentication within the same endpoint(s). So the goal is to be able to determine the windows user but the endpoint should also work when no windows user is present (aka windows authentication fails).
My problem is that when I use the Authorize attribe (as shown in the example below), the endpoint will only be called when windows authentication succeded. If I additionaly add the [AllowAnonymous] attribute, the User is never authenticated.
Example: (
[Authorize(AuthenticationSchemes = IISDefaults.AuthenticationScheme)]
public IActionResult Index()
{
_log.LogDebug("IsAuthenticated = " + this.User.Identity.IsAuthenticated.ToString());
_log.LogDebug("Authenticated Name: " + this.User.Identity.IsAuthenticated.Name);
return View();
}
How can this be done in .Net 6.0? It should be really simple as authentication and authorization should be separated but it seems they are quite intertwined. I haven't found a solution after extensive googling, checking the .net core source code and trying out myself.
Is there a good way to solve this?
Remark 1: there are solutions for .Net core 3.1 but then don't work in .Net 6 Enable both Windows authentication and Anonymous authentication in an ASP.NET Core app
Remark 2: we have endpoint that have to work with Windows Authentication only and other with anonyomous authentication. These both work fine within the same application. It is really about being able to detect the windows user in an endpoint that otherwise supports anymous authentication.
I (or better we) have found a solution that works even when Windows authentication is disabled on IIS. It is not very elegant but this is what we came up with. The idea is basically to trigger another call to an endpoint to determine if the user is actually a windows loging or not. If this call is successful, then we know we have a windows user and can act accordingly, for example do a redirect to an endpoint that requires windows authentication.
Remark: If you can control the IIS settings - which probably is often the case - , then I suggest you go with the solution proposed here:
enable-both-windows-authentication-and-anonymous-authentication-in-an-asp-net-co )
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> TestWindowsAuthAsync(CancellationToken cancellationToken)
{
using var client = new HttpClient(new HttpClientHandler()
{
UseDefaultCredentials = true
});
var response = await client.GetAsync($"{HttpContext.Request.Scheme}://{HttpContext.Request.Host}{HttpContext.Request.PathBase}{HttpContext.Request.Path}/HasUserWindowsAuth");
if (response.IsSuccessStatusCode)
{
// Yes, now we know that user indeed has windows authentication and we can act upon it
return RedirectToAction("QuickLogin", input);
}
// No windows credentials have been passed at this point
return View();
}
[HttpGet("HasUserWindowsAuth")]
[Authorize(AuthenticationSchemes = IISDefaults.AuthenticationScheme)]
public IActionResult HasUserWindowsAuth() => Ok();
[HttpGet("QuickLogin")]
[Authorize(AuthenticationSchemes = IISDefaults.AuthenticationScheme)]
public async Task<IActionResult> QuickLoginAsync(LoginModel input, CancellationToken cancellationToken)
{
var user = this.User.Identities.FirstOrDefault(i => i System.Security.Principal.WindowsIdentity && i.IsAuthenticatd);
// do something with that user
}

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

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.

Need to handle Post Authenticate in Asp.Net Core

I'm ready to use Asp.Net core, but here's what I am doing. In MVC 5, I have an Http module that is handling the PostAuthenticate event in order to create the claim where I am doing some stuff to determine roles for the user. I see no way to do this same thing in Core. Note that this is using Windows Authentication so there is no login method to handle.
From the current httpModule that hooks up to the PostAuthenticate because I want to initialize some things for the user.
context.PostAuthenticateRequest += Context_PostAuthenticateRequest;
Note that httpModules no longer exist with Core and that is being moved to middleware.. I don't see how to tap into that event from there though.
I just did this for the first time today. Two basic steps here.
First:
Create a class that implements the IClaimsTransformer interface.
public class MyTransformer : IClaimsTransformer
{
public Task<ClaimsPrincipal> TransformAsync(ClaimsTransformationContext context )
{
//don't run if user isn't logged in
if(context.Principal.Identity.IsAuthenticated)
{
((ClaimsIdentity)context.Principal.Identity)?.AddClaims(...);
}
}
return Task.FromResult(context.Principal);
}
Second:
Add this line to Startup.cs in
public void Configure(IApplicationBuilder app, ..., ...)
{
//app.Use...Authentication stuff above, for example
app.UseOpenIdConnectAuthentication( new OpenIdOptions
{
//or however you like to do this.
});
app.UseClaimsTransformation(o => new MyTransformer().TransformAsync(o));
//UseMvc below
app.UseMvc(...);
}
Keep in mind that TransformAsync is going to run on every request, so you might want to look into using sessions or caching if you're hitting a database with it.
Windows Authentication is performed by the hosts (IIS or HttpSys/WebListener) at the start of your application pipeline. The first middleware in your pipeline is the equivalent of PostAuthenticateRequest in this case. Operate on HttpContext.User as you see fit.

ASP.Net Core OpenIdConnect Steam

I'm trying to implement Steam OpenId into my ASP.Net Core application, and I don't have any previous experience implementing OpenID.
Unfortunately, Steam is massively lacking documentation on their side, and simply state "just download an OpenID library", and provide you with a page to register an API key for a domain name.
There are several implementations available for full ASP.Net, but not for Core, and it seems there are some differences.
I'm trying to use Microsoft.AspNetCore.Authentication.OpenIdConnect, though I am not entirely sure if this is the right library. It seems there is a difference between "OpenID" and "OpenID Connect".
I've set up the authentication in my Startup.cs like so:
app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
{
DisplayName = "Steam",
Authority = "http://steamcommunity.com/openid",
ClientId = "MyClientId",
ClientSecret = "ApiKeyHere",
SignInScheme = "SignInCookie",
CallbackPath = new PathString("/Account/SteamCallback"),
RequireHttpsMetadata = false
});
But as soon as I hit the sign-in page, which consists of an action returning a challenge:
public IActionResult SignIn()
{
return Challenge();
}
I get the error
JsonReaderException: Unexpected character encountered while parsing value: <. Path '', line 0, position 0.
Newtonsoft.Json.JsonTextReader.ParseValue() InvalidOperationException:
IDX10803: Unable to obtain configuration from: 'http://steamcommunity.com/openid/.well-known/openid-configuration'.
When I look at this URL, it seems to return XML data for the configuration of OpenID:
<?xml version="1.0" encoding="UTF-8"?>
<xrds:XRDS xmlns:xrds="xri://$xrds" xmlns="xri://$xrd*($v*2.0)">
<XRD>
<Service priority="0">
<Type>http://specs.openid.net/auth/2.0/server</Type>
<URI>https://steamcommunity.com/openid/login</URI>
</Service>
</XRD>
</xrds:XRDS>
But the OpenID spec states that this info should be in JSON.
Next I tried registering my own OpenIdConnectMiddleware, much like how this ASP.Net implemtation does it, however, this resulted in not being able to be constructed due to missing services that the OpenIdConnectMiddleware class requires:
System.InvalidOperationException: 'A suitable constructor for type
'TestApplication.SteamOpenId.SteamAuthenticationMiddleware' could not be
located. Ensure the type is concrete and services are registered for
all parameters of a public constructor.'
My implementation:
public class SteamAuthenticationMiddleware : OpenIdConnectMiddleware
{
public SteamAuthenticationMiddleware(
RequestDelegate next,
IDataProtectionProvider dataProtectionProvider,
ILoggerFactory loggerFactory,
UrlEncoder encoder,
IServiceProvider services,
IOptions<SharedAuthenticationOptions> sharedOptions,
IOptions<OpenIdConnectOptions> options,
HtmlEncoder htmlEncoder) :
base(
next,
dataProtectionProvider,
loggerFactory,
encoder,
services,
sharedOptions,
options,
htmlEncoder)
{
}
protected override AuthenticationHandler<OpenIdConnectOptions> CreateHandler() => new SteamAuthenticationHandler();
}
I know this question isn't very specific, but can anyone point me in the right direction with this? I am a little stumped.
It seems there is a difference between "OpenID" and "OpenID Connect".
There is: OpenID 1/2 and OpenID Connect are totally different protocols.
Steam is still using OpenID 2.0 so you can't use ASP.NET Core's OpenID Connect middleware to authenticate your users using their Steam account, as the two protocols are not compatible/interoperable.
I know this question isn't very specific, but can anyone point me in the right direction with this? I am a little stumped.
The aspnet-contrib Steam middleware you've mentioned derives from a generic OpenID 2.0 provider specially developed for ASP.NET Core and thus is probably your best option (that said, I'm the guy who's written it, so I'm likely not objective).
You can find the package on NuGet.org: https://www.nuget.org/packages/AspNet.Security.OpenId.Steam/.

Validating a user in WCF using ASP.net Identity 2.0 Framework

I have built a custom Identity.Models framework by simply extending the new asp.net identity framework 2.0 so that I can store the username and other relevant user data in my custom database instead of the default entity database which gets generated and it is working fine.
Now I am building a WCF service from where I would like to authenticate these users and leverage the asp.net identity 2.0 functionalities , but unable to do so.
In my WCF service I made a new Validator class extending UsernamePasswordValidator but it is not working as expected.
public class IdentityValidator : UserNamePasswordValidator
{
public override void Validate(string userName, string password)
{
using (var context = new MyIdentityDbContext())
{
using (var userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(context)))
{
var user = userManager.Find(userName, password);
if (user == null)
{
var msg = String.Format("Unknown Username {0} or incorrect password {1}", userName, password);
Trace.TraceWarning(msg);
throw new FaultException(msg);
}
}
}
}
}
Any suggestions would be highly appreciated.
You are almost there, however, you need one more step to tell the WCF service to be well behaved through introducing service behavior, generally in config. For more details, please read
Authentication and Authorization with ASP.NET Identity 2.0 for WCF Services