How to give Owin the user identity? - asp.net

tl;dr: What is the Owin equivalent of the HttpApplication.AuthenticateRequest event?
Background
When running an ASP.net site on IIS, the global System.Web.HttpApplication object raises an AuthenticateRequest event during each request.
Various http modules (such as the built-in FormsAuthentication) can attach to the event. The event handlers are called in the order in which they are registered. The first handler to set HttpContext.Current.User is the authentication used.
The job of the modules that are subscribed to this event is to set HttpContext.Current.User to to some Principal:
IIdentity identity = new GenericIdentity("MBurns", "ContosoAuthentcation");
IPrincipal principal = new GenericPrincipal(identity, null);
HttpContext.Current.User = principal;
Once HttpContext.Current.User is assigned, ASP.net knows that the user has been authenticated. (And once a user has been authenticated, they are no longer anonymous).
Any Module Can Do It
Anyone can use web.config to register their own IHttpModule with ASP.net:
web.config
<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
<add name="MySuperCoolAuthenticationModule" type="ContosoAuthModule" />
</modules>
</system.webServer>
The module is easy enough to write. You implement the lone Init method of the IHttpModule interface. For us, we add ourself as an AuthenticateRequest event handler:
public class ContosoAuthModule : IHttpModule
{
public void Init(HttpApplication httpApplication)
{
// Register event handlers
httpApplication.AuthenticateRequest += OnApplicationAuthenticateRequest;
}
}
And then you can do what is needed to authenticate the user, and if they are a valid user, set the HttpContext.Current.User:
private void OnApplicationAuthenticateRequest(object sender, EventArgs e)
{
var request = HttpContext.Current.Request;
String username = SomeStuffToFigureOutWhoIsMakingTheRequest(request);
if (String.IsNullOrWhiteSpace(username))
{
//I don't know who they are :(
return;
}
//I know who they are, they are [username]!
IIdentity identity = new GenericIdentity(username, "ContosoSuperDuperAuthentication");
HttpContext.Current.User = new GenericPrincipal(identity, null);
}
That's all HttpApplication
MSDN documents the various events that are thrown by HttpApplication, and in what order:
ASP.NET Application Life Cycle Overview for IIS 7.0 (archive.is)
Validate the request, which examines the information sent by the browser and determines whether it contains potentially malicious markup. For more information, see ValidateRequesta and Script Exploits Overviewa.
Perform URL mapping, if any URLs have been configured in the UrlMappingsSectiona section of the Web.config file.
Raise the BeginRequest event.
Raise the AuthenticateRequesta event.
Raise the PostAuthenticateRequest event.
Raise the AuthorizeRequest event.
Raise the PostAuthorizeRequest event.
Raise the ResolveRequestCache event.
And that's all great when it's ASP.net and HttpApplication. Everything's well understood, easy enough to explain (in the half-screenful above), and works.
But HttpApplication is old and busted.
Owin is the new hotness
Everything is supposed to be Owin now. HttpApplication lives in System.Web. People want to be isolated from System.Web. They want this thing called Owin to be in charge now.
To further that goal, they (i.e. any new ASP.net MCV, web-forms, or SignalR web-site) disables the authentication system of ASP.net completely:
<system.web>
<authentication mode="None" />
</system.web>
So no more HttpApplication.AuthenticateRequest event. :(
What is the Owin equivalent?
What is the Owin equivalent of HttpApplication.AuthenticateRequest?
It's safe to say that no matter where my code is called from, my job is still to set HttpContext.Current.User to an identity.
Is it safe to say that no matter where my code is called form, my job is still to set HttpContext.Current.User to an identity?
What is the Owin equivalent of HttpApplication.AuthenticateRequest?
Attempt that doesn't work
Nothing of it is ever called:
using System;
using System.Threading.Tasks;
using Microsoft.Owin;
using Owin;
using System.Web;
using System.IO;
using Microsoft.Owin.Extensions;
using System.Security.Claims;
using System.Security.Principal;
[assembly: OwinStartup("AnyStringAsLongAsItsNotBlank", typeof(BasicAuthOwin))]
public class BasicAuthOwin
{
public void Configuration(IAppBuilder app)
{
app.Use((context, next) =>
{
System.Diagnostics.Trace.WriteLine("They did their best, shoddily-iddly-iddly-diddly");
OnAuthenticateRequest(context);
return next.Invoke();
});
app.UseStageMarker(PipelineStage.Authenticate);
app.Run(context =>
{
return context.Response.WriteAsync("Hello world");
});
}
private void OnAuthenticateRequest(IOwinContext context)
{
var request = context.Request;
String username = SomeStuffToFigureOutWhoIsMakingTheRequest(request);
if (String.IsNullOrWhiteSpace(username))
{
//I don't know who they are :(
return;
}
//I know who they are, they are [username]!
IIdentity identity = new GenericIdentity(username, "ContosoSuperDuperOwinAuthentication");
context.Authentication.User = new ClaimsPrincipal(identity);
}
private string SomeStuffToFigureOutWhoIsMakingTheRequest(IOwinRequest request)
{
//if ((System.Diagnostics.Stopwatch.GetTimestamp % 3) == 0)
// return "";
return "MBurns";
}
}

Check out the blog post from this website Jwt Authentication in ASP.NET WEB API AND MVC. It explains how to solve to issue of "Authorization has been denied for this request" using OWIN.
The JWTHandler class
public static void OnAuthenticateRequest(IOwinContext context)
{
var requestHeader = context.Request.Headers.Get("Authorization");
int userId = Convert.ToInt32(JwtDecoder.GetUserIdFromToken(requestHeader).ToString());
var identity = new GenericIdentity(userId.ToString(), "StakersClubOwinAuthentication");
//context.Authentication.User = new ClaimsPrincipal(identity);
var token = requestHeader.StartsWith("Bearer ") ? requestHeader.Substring(7) : requestHeader;
var secret = WebConfigurationManager.AppSettings.Get("jwtKey");
Thread.CurrentPrincipal = ValidateToken(
token,
secret,
true
);
context.Authentication.User = (ClaimsPrincipal) Thread.CurrentPrincipal;
//if (HttpContext.Current != null)
//{
// HttpContext.Current.User = Thread.CurrentPrincipal;
//}
}
The Startup class
public class Startup
{
public void Configuration(IAppBuilder app)
{
var config = new HttpConfiguration();
app.Use((context, next) =>
{
JwtAuthHandler.OnAuthenticateRequest(context); //the new method
return next.Invoke();
});
app.UseStageMarker(PipelineStage.Authenticate);
WebApiConfig.Register(config);//Remove or comment the config.MessageHandlers.Add(new JwtAuthHandler()) section it would not be triggered on execution.
app.UseWebApi(config);
}
}

Related

Adding AD authentication to OWIN Server Middleware pipeline

I have inherited a project that has been developed using OWIN, Server Middleware that manages a kind-of WebApi in order to communicate with mobile devices using ApiKeys. The Server side has a small web interface (which really is a set of test pages) but did not have authentication added. I am trying to wrap my head around the different frameworks being used and the ways one can authenticate around these OWIN techniques.
Let me show what I have first:
public class Startup
{
public void Configuration(IAppBuilder app)
{
log.Info("RT Server app starting up ...");
// Initialize the ApiKey Needed for ApiClient Library
ApiClient.ApiKey = Globals.ApiKey;
// Initialize the Services Library
Services.Startup.Initialize();//creates a configuration map of values for devices
// Setup Server Middleware
app.Use(typeof(ServerMiddleware), "RTrak.Server", "RTrak.Server");
app.Use(typeof(ServerMiddleware), "RTrak.Server.Pages", "RTrak.Server");
// HttpListener listener = (HttpListener)app.Properties["System.Net.HttpListener"];//throws an KeyNotFoundException
// listener.AuthenticationSchemes = AuthenticationSchemes.IntegratedWindowsAuthentication;
//ConfigureAuth(app)
}
public void ConfigureAuth(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = MyAuthentication.ApplicationCookie,
LoginPath = new PathString("/Login"),
Provider = new CookieAuthenticationProvider(),
CookieName = "RTrakCookie",
CookieHttpOnly = true,
ExpireTimeSpan = TimeSpan.FromHours(12), // ...
});
}
the ServerMiddleware
public ServerMiddleware(OwinMiddleware next, string baseNamespace, string defaultClass) : base(next)
{
BaseNamespace = baseNamespace;
DefaultClass = defaultClass;
}
public override async Task Invoke(IOwinContext owinContext)
{
var absolutePath = owinContext.Request.Uri.AbsolutePath;
string serverNamespace = BaseNamespace;
Type type;
string classToLoad = "";
if (absolutePath == "/")
classToLoad = DefaultClass;
else
{
classToLoad = absolutePath.Substring(1).Replace('/', '.');
if (classToLoad.EndsWith("."))
classToLoad = classToLoad.Substring(0, classToLoad.Length - 1);
}
type = Type.GetType($"{serverNamespace}.{classToLoad}, {serverNamespace}", false, true);
if (type == null)
{
classToLoad += ".Default";
type = Type.GetType($"{serverNamespace}.{classToLoad}, {serverNamespace}", false, true);
}
if (type != null)
{
try
{
object objService = Activator.CreateInstance(type);
((Resource)objService).Execute(owinContext);
}
catch (System.MissingMethodException)
{
//"403 INVALID URL");
}
}
else
await Next.Invoke(owinContext);
}
}
That ServerMiddleware is first calling Default Pages class that is HTML markup which links to the other test Pages
A thought was to add an MVC LoginController with AdAuthenticationService managing cookies Model to manage login that is configured as part of the Startup noted in the line ConfigAuth(app), but the middleware is ignoring the controller. Is MVC appropriate here?
then, I am looking at this ServerMiddleware and trying to understand how to intercept the Default page browser call with ActiveDirectory authentication.
I know that I may be overlooking something. Many thanks for anything (suggestions or resources) you can offer to help clear up this confusion for me.
What I did to resolve this was to leave the OWIN Middleware objects alone except for Startup.cs had to define CookieAuthentication route
app.UseCookieAuthentication(new Microsoft.Owin.Security.Cookies.CookieAuthenticationOptions
{
AuthenticationType = "ApplicationCookie",
LoginPath = new Microsoft.Owin.PathString("/Auth/Login")
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
for the pages that were built as OWIN Resources. These OWIN Resource "Pages" then check
if (!this.Context.Authentication.User.Identity.IsAuthenticated)
I then implement an MVC controller that uses the AdAuthenticationService as above and UserManager to manage the AD credentials and Identity where the OWIN resource redirects to the MVC view+controller for authentication. That controller handles the login page actions. Upon authentication, the MVC redirects to the OWIN resource pages.
Thus, OWIN Middleware and MVC can live side-by-side so long as OWIN does not try to define the routes that MVC wants to use. Owin can maintain its own authentication as well.

.Net MVC 4 CAS Authentication

I need to authenticate User from a central CAS. The assumption are these:
The UserId for authentication is in a Request Header
The roles for authorization are given by a web service.
The application must cache the authorization phase.
I've tried this:
In the Global.asax:
protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
const string SiteMinderHeaderToken = "SM_USER";
if (HttpContext.Current.User == null || !HttpContext.Current.User.Identity.IsAuthenticated)
{
var userSSO = HttpContext.Current.Request.Headers[SiteMinderHeaderToken];
GenericIdentity webIdentity = new GenericIdentity(userSSO, "SiteMinder");
string[] roles = { "ROLE1", "ROLE2" };
GenericPrincipal principal = new GenericPrincipal(webIdentity, roles);
HttpContext.Current.User = principal;
// System.Web.Security.FormsAuthentication.SetAuthCookie(userSSO, true);
}
}
In the Web.config
<authentication mode="None" />
<authorization>
<deny users="?" />
</authorization>
The problem is that for every request, the HttpContext.Current.User is always null, and every time all the authentication and authorization phase are done.
If I uncomment
System.Web.Security.FormsAuthentication.SetAuthCookie(userSSO, true);
All is fine, after the first request the User is authenticated.
My questions are:
Is it correct to call System.Web.Security.FormsAuthentication.SetAuthCookie even if there isn't FormAuthentication?
Is there a way to do it better?
Are there some security issues doing this way?
Thanks
FormsAuthentication in this case is just storing the users session.
Here's some more info: Understanding the Forms Authentication Ticket and Cookie https://support.microsoft.com/en-us/kb/910443/
You can set the auth cookie this way, but if would like to store more of the users info within the session, I would recommend using claims.
FormsAuthentication cookie is secure. "The ticket is encrypted and signed using the configuration element of the server's Machine.config file. ASP.NET 2.0 uses the decryptionKey and the new decryption attribute of the element to encrypt forms authentication tickets." There's more information in the link I gave above. I guess the only security issue would be how the user is accessing your code "FormsAuthentication.SetAuthCookie(userSSO, true);"
Try looking into Owin Authentication to store the users session.
http://www.asp.net/aspnet/overview/owin-and-katana/owin-oauth-20-authorization-server
Here's an example of how I use it in my MVC project.
Installed nugget packages:
Microsoft.Owin.Host.SystemWeb
Microsoft.Owin.Security.OAuth
Microsoft.Owin.Security.Cookies
Created OwinConfig.cs file (in my App_Start folder):
using Microsoft.Owin;
[assembly: OwinStartup(typeof(MyProject.OwinConfig))]
namespace MyProject
{
using System.Web.Security;
using Microsoft.Owin.Security.Cookies;
using Owin;
/// <summary>
/// The startup.
/// </summary>
public class OwinConfig
{
/// <summary>
/// The configuration.
/// </summary>
/// <param name="app">
/// The app.
/// </param>
public void Configuration(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = FormsAuthentication.FormsCookieName,
Provider = new CookieAuthenticationProvider()
});
}
}
}
Code I use to log a user in:
var claims = new[]
{
new Claim(ClaimTypes.NameIdentifier, 1)),
new Claim("CustomClaim", "My bio or something"),
new Claim(ClaimTypes.Name, "set_username")
};
var userWithClaims = new ClaimsIdentity(claims, FormsAuthentication.FormsCookieName);
var owinContext = HttpContext.Current.Request.GetOwinContext();
var authenticationManager = owinContext.Authentication;
authenticationManager.SignIn(userWithClaims);
And then I access the stored claims by creating IdentityExtensions.cs class (also in my app_start folder):
public static class IdentityExtensions
{
public static string GetCustomClaim(this IIdentity identity)
{
if (identity == null)
{
throw new ArgumentNullException("identity");
}
var ci = identity as ClaimsIdentity;
return ci != null ? ci.FindFirstValue("CustomClaim") : null;
}
private static string FindFirstValue(this ClaimsIdentity identity, string claimType)
{
if (identity == null)
{
throw new ArgumentNullException("identity");
}
var claim = identity.FindFirst(claimType);
return claim != null ? claim.Value : null;
}
}
So in my controller I could just call:
string customClaim = this.HttpContext.User.Identity.GetCustomClaim();
Then finally, to logout I would use:
var owinContext = HttpContext.Current.Request.GetOwinContext();
var authenticationManager = owinContext.Authentication;
authenticationManager.SignOut();

SignalR and ASP.NET Identity ExpireTimeSpan

I am using ASP.NET Identity with cookie based authentication. I am setting the ExpireTimeSpan on the CookieAuthenticationOptions class to control how much time of inactivity is allowed before the user has to log in again.
This all works fine, but when I add SignalR to the application the user no longer has to log-in after a period of inactivity. SignalR does a "ping" request periodically and I presume it is this that causes the cookie expiry to be extended.
I am looking for a way to not renew the cookie expiry for the SignalR URLs.
I have looked into some of the code in Microsoft.Owin.Security.Cookies and the CookieAuthenticationHandler class in particular. There is logic in the AuthenticateCoreAsync method to decide if to renew the cookie. However, the CookieAuthenticationHandler class in internal so I can't override this method.
Any ideas if there is a hook I can use to do this?
We solved at my company by removing the cookies from the signalr response, using an HttpModule.
public class NoFormsAuthenticationModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.PreSendRequestHeaders += OnPreSendRequestHeaders;
}
protected void OnPreSendRequestHeaders(object sender, EventArgs e)
{
var httpContext = ((HttpApplication)sender).Context;
var path = httpContext.Request.Path;
var noAuthentUrls = new string[] { "/signalr/" };
foreach (var url in noAuthentUrls)
{
var noAuthentication = path.IndexOf(url, StringComparison.OrdinalIgnoreCase) > -1;
if (noAuthentication)
httpContext.Response.Cookies.Remove(FormsAuthentication.FormsCookieName);
}
}
}
Hope it helps you.
Dont forget to add the entries on the web.config:
< system.web>
< httpModules>
< add name="NoFormsAuthenticationModule" type="Site.Components.HttpModules.NoFormsAuthenticationModule"/>
< system.webServer>
< modules runAllManagedModulesForAllRequests="true">
< add name="NoFormsAuthenticationModule" type="Site.Components.HttpModules.NoFormsAuthenticationModule"/>
...

SignalR ISAuthenticated using Headers

My goal is:
To use custom headers with my own token to authenticate a user or machine against my signalr service.
We've been using this methodology succesfully under ASP.net WEB API to perform our own custom claims based authentication and authorization.
Our Web Api was as follows:
protected void Application_Start()
{
GlobalConfiguration.Configuration.MessageHandlers.Add(new AuthorizationHeaderHandler());
}
Then we would have a AuthorizationHandler that would overwrite the Thread.CurrentPrincipal = principal; and we would be done.
Within SignalR I have tried to implement:
1. Mark our hub using Authorize
2. Implemented custom authorize atributes
3. Tried A Custom Module. But besides returning true if the correct headers we're send I still do not get the Context.User to change to the claims based principal that we generate.
But never can we get the Context.User to show the actual user that's being used to connect to the hub.
Any suggestions are Welcome.
Main reason why we want to achieve this is because we have a couple of different user/machine types that connect to our system.
Anybody any suggestions.
Finally found the solution.
I added my own owin security middleware allowing me to handle customer header based authentication.
This could be easily expanded allowing you to combine multiple authenitication scheme's within on service.
First Create Custom Authentication Middleware:
public class AuthenticationMiddleware : OwinMiddleware
{
public AuthenticationMiddleware(OwinMiddleware next) :
base(next) { }
public override async Task Invoke(IOwinContext context)
{
var request = context.Request;
var value = request.Headers["Phocabby-MachineKey"];
var username = value;
var usernameClaim = new Claim(ClaimTypes.Name, username);
var identity = new ClaimsIdentity(new[] { usernameClaim }, "ApiKey");
var principal = new ClaimsPrincipal(identity);
principal.Identities.First().AddClaim(new Claim("CanGetApiKey", "False"));
principal.Identities.First().AddClaim(new Claim("Cabinet", "True"));
request.User = principal;
await Next.Invoke(context);
}
}
Then register it in the startup class
public void Configuration(IAppBuilder app)
{
app.Use(typeof(AuthenticationMiddleware));
app.MapSignalR();
}

Nancy and SignalR Authentication

I think I am missing something here because this shouldn't be this difficult.
I have a nancy IIS hosted application that uses authentication which works (Sets a cookie _nca). Then I have a signalR hub that needs to get the username of the logged in person so I can then pull the "Group" that the user belongs to.
I thought I would just grab the username from Context.User.Identity.Name but that is empty and the hub appears to think I am not authorized. I am guessing that there is something else I need to do to tell signalR how to authenticate.
I thought that maybe setting HttpContext.Current.User would help but that apparently didn't do anything either.
I did this by the following
protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines)
{
base.ApplicationStartup(container, pipelines);
pipelines.AfterRequest.AddItemToStartOfPipeline(SetUpContext);
//pipelines.AfterRequest.AddItemToEndOfPipeline(SetUpContext);
CookieBasedSessions.Enable(pipelines);
RouteTable.Routes.MapHubs();
}
private void SetUpContext(NancyContext ctx)
{
if (ctx.CurrentUser == null)
return;
string[] Roles = { "Recipient" };
var userIdent = new UserIdent();
userIdent.IsAuthenticated = true;
userIdent.Name = ctx.CurrentUser.UserName;
GenericPrincipal principal = new GenericPrincipal(userIdent, Roles);
IPrincipal Identity = (IPrincipal)principal;
HttpContext.Current.User = Identity;
}
I also tried pipelines.BeforeRequest.AddItemToEndOfPipeline. Still not having any luck.
Here is a sample using SignalR 2.0 and Cookie Authentication

Resources