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();
Related
I have a Web API application with MVC. When a user is using the website, the authentication and authorization is currently automatically handled by the global forms authentication I use, configured in the Web.config like so:
<authentication mode="Forms">
<forms loginUrl="~/Login" slidingExpiration="true" timeout="1800" defaultUrl="/"></forms>
</authentication>
<authorization>
<deny users="?" />
</authorization>
This makes sure only logged in users can access the site and call the API.
But I also have an external Windows client for which I would like to use another authentication method. In a test without the forms auth, I set up a custom AuthorizeAttribute that I can use in my controllers like this:
[ApiAuth]
public IEnumerable<string> Get() {
// Return the resource
}
The AuthorizeAttribute looks something like this:
public class ApiAuthAttribute : AuthorizeAttribute {
public override void OnAuthorization(HttpActionContext context) {
// Authenticate the request with a HMAC-based approach
}
}
This works fine in isolation but I cannot figure out how to allow both auth methods. I would like to the ApiAuth as a fallback if the form auth doesn't work (or the reverse, whatever works), but if I apply the [ApiAuth] attribute, only that will be used and normal users cannot access the api.
So, how can I use multiple auth methods, either by using one of them as a fallback if the other one fails, or configuring the server so the Windows client can call the API some other way then the MVC app, while still keeping the same API calls available to both type of clients?
Thank you.
Edit: One approach that I could probably take, is to let the Windows client authenticate using the forms auth (something like this), but it seems very much like a hack and I would much rather use some other approach.
FormAuthentication can be achieve multiple way. In old day, we use FormAuthentication Ticket.
Now, you can use claim-based authentication with Owin Middleware which basically is a strip down version of ASP.Net Identity.
After you authenticate a user inside ApiAuthAttribute, you create Principal object.
Web.config
You should not use <authorization> tag in ASP.Net MVC. Instead, you want to use Filter.
<authentication mode="Forms">
<forms loginUrl="~/Account/Login" timeout="2880" />
</authentication>
ApiAuthAttribute
public class ApiAuthAttribute : AuthorizeAttribute
{
public override void OnAuthorization(HttpActionContext context)
{
// Authenticate the request with a HMAC-based approach
// Create FormAuthentication after custom authentication is successful
if (!HttpContext.Current.User.Identity.IsAuthenticated)
{
User user = new User {Id = "1234", UserName = "johndoe",
FirstName = "John", LastName = "Doe"};
// This should be injected using IoC container.
var service = new OwinAuthenticationService(
new HttpContextWrapper(HttpContext.Current));
service.SignIn(user);
}
}
}
Authentication
public class User
{
public string Id { get; set; }
public string UserName { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public interface IAuthenticationService
{
void SignIn(User user);
void SignOut();
}
public class OwinAuthenticationService : IAuthenticationService
{
private readonly HttpContextBase _context;
private const string AuthenticationType = "ApplicationCookie";
public OwinAuthenticationService(HttpContextBase context)
{
_context = context;
}
public void SignIn(User user)
{
IList<Claim> claims = new List<Claim>
{
new Claim(ClaimTypes.Sid, user.Id),
new Claim(ClaimTypes.Name, user.UserName),
new Claim(ClaimTypes.GivenName, user.FirstName),
new Claim(ClaimTypes.Surname, user.LastName),
};
/*foreach (Role role in user.Roles)
{
claims.Add(new Claim(ClaimTypes.Role, role.Name));
}*/
ClaimsIdentity identity = new ClaimsIdentity(claims, AuthenticationType);
IOwinContext context = _context.Request.GetOwinContext();
IAuthenticationManager authenticationManager = context.Authentication;
authenticationManager.SignIn(identity);
}
public void SignOut()
{
IOwinContext context = _context.Request.GetOwinContext();
IAuthenticationManager authenticationManager = context.Authentication;
authenticationManager.SignOut(AuthenticationType);
}
}
Startup.cs
[assembly: OwinStartup(typeof(YOUR_APPLICATION.Startup))]
namespace YOUR_APPLICATION
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "ApplicationCookie",
LoginPath = new PathString("/Account/Login")
});
}
}
}
I implemented something similar a while back. You may want to look at third party auth providers (as they have been tested). If you create your own mechanism make sure that whatever data you store to identify an authenticated user session will be removed based on some expiration value.
When I refer to a token below, please note that I am refering to a hash using a combination of :
Some user data
Some dynamic data such as tick count
Some data that represents what resource is being requested
Maybe the parameters
For example. You could hash the username/hh:mm:ss:ms/fully qualified path/enpoint/enpoint parameters into your user's token. Then you have to decide if the token will be valid on a sliding expiration, 30 minutes, or is it only valid per request.
I would add an anonymous endpoint for your test application to authenticate against. This endpoint should accept user credentials and return a token that matches an entry in Ticket table that represents the user with an expiration. Essentially, since you are not attaching a ticket to each request you will have to manage this yourself in some fashion as I have suggested using the http authorization header.
public ActionResult GetAuthententicationToken(Credentials credentials)
{
//Authenticate the user
//Insert a record into the Ticket database table and return hash key as token.
//Return the token to the client.
}
Now the client ,your testing app, has been authenticated against an existing set of credentials and has a token representing that handshake.
Your test app now only has to sign the authorization http header with the value returned from get GetAuthententicationToken().
Now you can implement your AuthorizeAttribute in which case you want to validate the authorization header token with what was previously stored with a successful call to your anonymous GetAuthententicationToken method.
public class ApiAuthAttribute : AuthorizeAttribute {
public override void OnAuthorization(HttpActionContext context) {
//Get authorization token from header
//if caching then get associated Ticket from cache else lookup in database
//if not valid throw security exception
//Apply principal to current user based on lookup above
}
}
So how to handle FormsAuthentication with the above scheme in mind?
Since Forms Authentication is handled earlier in the request processing than the MVC Authorize you have a perfect opportunity to add your custom authorization header to the incoming request when the user is authenticated via your forms method.
In the same place that you authenticate your forms authentication add something similar to below.
public FormsAthentication.CreateAuthenticationTicket()
{
//Authenticate user
//Insert a record into the Ticket database table and return hash key as token.
//Add that token to ticket's data
}
Next, you need to make sure the custom authorization header is applied per request. The best place to do this would be the Application_AuthenticateRequest in the Global.asax file.
protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
//if FormsAuthentication.IsAuthenticated
//Get the token saved in the ticket data
//Save the token value in the http authorization header
}
NOTE : The Ticket database table mention above should save a valid authentication request with a datetime stamp for expiration date. You must ensure that you have a process that runs in the background to enforce the timeout by removing expired session records.
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);
}
}
I'm creating an MVC5 app. It is an intranet app. All users are already authenticated to the local Active Directory Domain.
We have an existing database that is currently used for a Windows app.
I want take the User's domain login name and use it to look up the roles and claims that are already configured in that database.
I will assume the the base project of ASP.NET / MVC5 / Authentication "Individual User Accounts" would be the starting point.
Please point me in the right direction.
Thanks
You do not want entire ASP.Net Identity. Instead, you can just use OWIN cookie authentication middleware.
Validate Credential via Active Directory
public bool ValidateCredentials(string userName, string password)
{
using (var context = new PrincipalContext(ContextType.Domain))
{
return context.ValidateCredentials(userName, password);
}
}
Authorize via Old Database
Once authenticated, you want to retrieve authorized role from old Database, and create claims.
private readonly HttpContextBase _context;
private const string AuthenticationType = "ApplicationCookie";
public OwinAuthenticationService(HttpContextBase context)
{
_context = context;
}
public void SignIn(User user)
{
IList<Claim> claims = new List<Claim>
{
new Claim(ClaimTypes.Sid, user.Id.ToString()),
new Claim(ClaimTypes.Name, user.UserName),
new Claim(ClaimTypes.GivenName, user.FirstName),
new Claim(ClaimTypes.Surname, user.LastName),
};
// Get authorized roles from old database
foreach (Role role in user.Roles)
{
claims.Add(new Claim(ClaimTypes.Role, role.Name));
}
ClaimsIdentity identity = new ClaimsIdentity(claims, AuthenticationType);
IOwinContext context = _context.Request.GetOwinContext();
IAuthenticationManager authenticationManager = context.Authentication;
authenticationManager.SignIn(identity);
}
public void SignOut()
{
IOwinContext context = _context.Request.GetOwinContext();
IAuthenticationManager authenticationManager = context.Authentication;
authenticationManager.SignOut(AuthenticationType);
}
Startup.cs
You also need to configure Startup for all those to happen.
[assembly: OwinStartup(typeof(YourApplication.Startup))]
namespace YourApplication
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "ApplicationCookie",
LoginPath = new PathString("/Account/Login")
});
}
}
}
I hope you get the starting point.
The .NET OWIN Identity classes require that you authenticate through the CheckPasswordAsync() method of the ApplicationUserManager class. This can be done by overriding the CheckPasswordAsync() method of class ApplicationUserManager. In your override you will need to call the ValidateCredentials() method of class System.DirectoryServices.AccountManagement to authenticate via Active Directory. This will require the user to login to the application with their Windows username and password. There are a few steps to get that to work though.
As you said, you start with a base project with "Individual User Accounts" authentication.
Step 1 - Update the ConfigureAuth() method in file App_Start\Startup.Auth.cs by adding the code below to the ConfigureAuth() method.
using System.DirectoryServices.AccountManagement;
//Add an Owin context for Active Directory principals
app.CreatePerOwinContext(() => new PrincipalContext(ContextType.Domain));
The rest of the updates are done in file App_Start\IdentityConfig.cs
Step 2 - Update the constructor for class ApplicationUserManager.
using System.DirectoryServices.AccountManagement;
//Add a PrincipalContext parameter to the constructor
public ApplicationUserManager(IUserStore<ApplicationUser> store, PrincipalContext principal) : base(store)
{
this.principal = principal;
}
Step 3 - In the Create() method update the call to the constructor for class ApplicationUserManager.
//Add the PrincipalContext parameter
var manager = new ApplicationUserManager(new UserStore<ApplicationUser>(context.Get<PortalIdentityDbContext>()), context.Get<PrincipalContext>());
Step 4 - Override the CheckPasswordAsync() method of class ApplicationUserManager.
//Override CheckPasswordAsync to login via Active Directory.
public override async Task<bool> CheckPasswordAsync(ApplicationUser user, string password)
{
return await Task.FromResult(this.principal.ValidateCredentials(user.UserName, password, ContextOptions.Negotiate));
}
As for using your existing database, you will have to incorporate the OWIN Identity tables in it or vice-versa. The Identity functionality requires those tables and you can't change that. I would create a test project and get familiar with those tables. Then figure out how you want to incorporate them into your existing database or vice-versa. I heavily modify those tables for my custom functionality. But the core tables and columns have to exist.
Using the [Authorize] attribute on an ASP.Net Web API method causes a "401 Unauthorized" response.
I have an Http Module that handles the context.AuthenticateRequest event in which I examine the Authorization header (Basic authorization) of the request, and, if valid, set the System.Threading.Thread.CurrentPrincipal to a new GenericPrincipal containing a new GenericIdentity based on the info in the Authorization header. I also set the HttpContext.Current.User to the same instance of GenericPrincipal.
At this point, the IsAuthenticated property of the IIdentity is true. However, by the time the action method in the controller is invoked, System.Threading.Thread.CurrentPrincipal has been set to a System.Security.Claims.ClaimsPrincipal containing a System.Security.Claims.ClaimsIdentity with IsAuthenticated = false.
So... somewhere in the pipeline between the point where I set the CurrentPrincipal and when it reaches the action method, the CurrentPrincipal and the Identity is getting replaced.
Some of the methods of the API access ASP.Net Identity users (for a related website, the API itself does not use ASP.Net Identity for authentication/authorization), so the API project is set up with all the relevant ASP.Net Identity NuGet packages, etc.
I've used the same Http Module in other API projects that DON'T have all the ASP.Net Identity NuGet packages, etc. and it works like a champ.
I suspect that the ASP.Net Identity configuration is causing my Basic Authentication System.Security.Claims.ClaimsPrincipal to be replaced.
Any help would be much appreciated!
Here's my code:
Http Module - at the end of this method, System.Threading.Thread.CurrentPrincipal and HttpContext.Current.User are correctly set.
public class FsApiHttpAuthentication : IHttpModule, IDisposable {
public void Init( HttpApplication context ) {
context.AuthenticateRequest += AuthenticateRequests;
context.EndRequest += TriggerCredentials;
}
private static void AuthenticateRequests( object sender, EventArgs e ) {
string authHeader = HttpContext.Current.Request.Headers["Authorization"];
if ( authHeader != null ) {
System.Net.Http.Headers.AuthenticationHeaderValue authHeaderVal = System.Net.Http.Headers.AuthenticationHeaderValue.Parse(authHeader);
if ( authHeaderVal.Parameter != null ) {
byte[] unencoded = Convert.FromBase64String(authHeaderVal.Parameter);
string userpw = System.Text.Encoding.GetEncoding("iso-8859-1").GetString(unencoded);
string[] creds = userpw.Split(':');
CredentialCache.Credential cred = CredentialCache.GetCredential(creds[0], creds[1]);
if ( cred != null ) {
System.Security.Principal.GenericIdentity identity = new System.Security.Principal.GenericIdentity
(cred.Username);System.Threading.Thread.CurrentPrincipal = new System.Security.Principal.GenericPrincipal(identity, roles);
if ( string.IsNullOrWhiteSpace(cred.RolesList) ) {
System.Threading.Thread.CurrentPrincipal = new System.Security.Principal.GenericPrincipal(identity, null);
} else {
System.Threading.Thread.CurrentPrincipal = new System.Security.Principal.GenericPrincipal(identity, cred.RolesList.Split(','));
}
HttpContext.Current.User = System.Threading.Thread.CurrentPrincipal;
}
}
}
}
Api Controller - when the Post action in this controller is reached, System.Threading.Thread.CurrentPrincipal and HttpContext.Current.User have been set to a System.Security.Claims.ClaimsPrincipal containing a System.Security.Claims.ClaimsIdentity with IsAuthenticated = false.
public class ConsumerAccountController : ApiController {
private ApplicationUserManager _userManager;
private ApplicationUserManager UserManager {
get {
return _userManager ?? Request.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
set {
_userManager = value;
}
}
public async System.Threading.Tasks.Task<IHttpActionResult> Post( API.FinancialSamaritan.com.ViewModels.UserCredentials creds ) {
API.FinancialSamaritan.com.ViewModels.CreateUserResult cccur = null;
try {
string username = creds.Username;
string password = creds.Password;
var user = new API.FinancialSamaritan.com.Models.ApplicationUser {
UserName = username,
Email = username,
SecurityQuestion = creds.SecurityQuestion,
SecurityAnswer = UserManager.PasswordHasher.HashPassword(creds.SecurityAnswer),
IsPasswordChangeRequired = true,
EmailConfirmed = true
};
IdentityResult userResult = await UserManager.CreateAsync(user, password);
...
The [Authorize] attribute was deriving from System.Web.Http.AuthorizeAttribute instead of System.Web.Mvc.AuthorizeAttribute. I had to fully qualify the namespace of the attribute so that MVC version of [Authorize] would be used. Thanks to #RonBrogan for pointing me in the right direction!
Well, this was a mistake.
I decided to migrate my MVC5 application to MVC6 and things were going fine until I needed to migrate my authentication.
My MVC application was logging in using an external Web Api 2 application which returns a token.
I built a filter to handle that very simply like this:
/// <summary>
/// Uses the session to authorize a user
/// </summary>
public class SimpleAuthorize : AuthorizeAttribute
{
/// <summary>
/// Authorizes the user
/// </summary>
/// <param name="httpContext">The HTTP Context</param>
/// <returns></returns>
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var accessToken = httpContext.Session["AccessToken"];
if (accessToken == null)
return false;
return true;
}
}
which was applied to all controllers.
Now, it appears that you can't do that anymore as mentioned here.
So, how can I get my application to work with the API?
I have tried searching and found nothing that helps me with my situation. Does anyone know how I can solve this or could point me in the direction of some decent documentation?
You'd approach it by writing Authorization middleware which creates an identity out of the access token. Having a valid identity is enough for authorization to succeed, and then you can delve into policy should you need something more detailed. Something like
public class SessionAuthenticationHandler :
AuthenticationHandler<SessionAuthenticationOptions>
{
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
var sessionToken = Request.HttpContext.Session.GetString("Token");
if (sessionToken == null)
{
return AuthenticateResult.Failed("No session token");
}
// Construct principal here
var principal =
new ClaimsPrincipal(new ClaimsIdentity(new[] {
new Claim("SessionToken", sessionToken) }, Options.AuthenticationScheme));
var ticket = new AuthenticationTicket(principal, new AuthenticationProperties(),
Options.AuthenticationScheme);
return AuthenticateResult.Success(ticket);
}
}
Having said that the reason ASP.NET has never used session to hold authentication details is due to session fixation attacks.