I have a ASP.Net WebAPI 2.1 that I have just transitioned across to using Identity 2.0 using bearer tokens. This works fine. Now I am attempting to pull in some MVC code to create a set of login and user management pages. My issue is that I can't seem to get Request.IsAuthenticated to work from my Razor views when I set the WebApi HttpConfigurationto SuppressDefaultHostAuthentication.
Below is my code, and I'm out of ideas as to how I can get this to work for both scenarios :(
Here's my Startup.cs which sets up the Identity OWIN module, and the WebAPI:
public class Startup
{
public void Configure(IAppBuilder app)
{
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);
PublicClientId = "self";
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/token"),
Provider = new ApplicationOAuthProvider(PublicClientId),
AuthorizeEndpointPath = new PathString("/api/account/externalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14)
};
app.UseOAuthBearerTokens(OAuthOptions);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager, DefaultAuthenticationTypes.ApplicationCookie))
}
});
var httpConfiguration = new HttpConfiguration();
// Disable this line to allow Request.IsAuthenticated to work
// But by doing this, it allows the 'redirect' to kick in on unauthenticated API requests, which returns a HTML page for a webapi call, rather than the JSON 'unauthenticated' response
httpConfiguration.SuppressDefaultHostAuthentication();
httpConfiguration.Filters.Add(new HostAuthenticationFilter(DefaultAuthenticationTypes.ApplicationCookie));
httpConfiguration.MapHttpAttributeRoutes();
app.UseWebApi(httpConfiguration);
}
}
Here's my Global.asax.cs, which sets up the MVC side of things (AFAIK OWIN doesn't support any form of app.UseMvc()):
public class WebApiApplication : HttpApplication
{
protected void Application_Start()
{
// pretty much the defaults here for everything, just renamed
AreaRegistration.RegisterAllAreas();
MvcConfig.ConfigureFilters(GlobalFilters.Filters);
MvcConfig.ConfigureRoutes(RouteTable.Routes);
MvcConfig.ConfigureBundles(BundleTable.Bundles);
}
}
Now in my Razor views, I would like to use Request.IsAuthenticated, as used in the Identity samples, but this fails when the httpConfiguration.SuppressDefaultHostAuthentication is enabled. I understand the goal of this extension is to remove the current identity after the Identity middleware has run -- so that the WebAPI authentication filter can do as it pleases. But I am hoping that on the MVC side of things, that the suppression wouldn't occur.
Example Razor view:
#if (Request.IsAuthenticated) // false when using httpConfiguration.SuppressDefaultHostAuthentication
{
<div>User.Identity.Email</div>
}
Can anyone help me? Is this even possible?
Thanks!
Looks like it's all about the ordering of the app builder. If I place the Identity bearer configuration before the WebAPI, then my WebAPI requests still utilize the Identity OWIN module. By placing the Cookie configuration after the WebAPI configuration, the Cookie Identity resolution occurs after the WebAPI identity removal, and before the MVC execution.
Unsure if this is the 'correct' way to go about doing it, but it seems to solve all of the test cases I have open.
public class Startup
{
public void Configure(IAppBuilder app)
{
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);
PublicClientId = "self";
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/token"),
Provider = new ApplicationOAuthProvider(PublicClientId),
AuthorizeEndpointPath = new PathString("/api/account/externalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14)
};
app.UseOAuthBearerTokens(OAuthOptions);
var httpConfiguration = new HttpConfiguration();
httpConfiguration.SuppressDefaultHostAuthentication();
httpConfiguration.Filters.Add(new HostAuthenticationFilter(DefaultAuthenticationTypes.ApplicationCookie));
httpConfiguration.MapHttpAttributeRoutes();
app.UseWebApi(httpConfiguration);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager, DefaultAuthenticationTypes.ApplicationCookie))
}
});
}
}
EDIT
The above works, but it seems to be better to utilize the app.MapWhen() functionality to do this.
public class Startup
{
public void Configure(IAppBuilder app)
{
// setup auth for all requests
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);
PublicClientId = "self";
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/token"),
Provider = new ApplicationOAuthProvider(PublicClientId),
AuthorizeEndpointPath = new PathString("/api/account/externalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14)
};
app.UseOAuthBearerTokens(OAuthOptions);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager, DefaultAuthenticationTypes.ApplicationCookie))
}
});
// setup webapi for only /api requests
app.MapWhen(
context => context.Request.Uri.PathAndQuery.StartsWith("/api"),
newApp => {
var httpConfiguration = new HttpConfiguration();
httpConfiguration.SuppressDefaultHostAuthentication();
httpConfiguration.Filters.Add(new HostAuthenticationFilter(DefaultAuthenticationTypes.ApplicationCookie));
httpConfiguration.MapHttpAttributeRoutes();
app.UseWebApi(httpConfiguration);
}
}
}
Related
I am using ASP.NET MVC, and I am using MS built-in template to allow users to login with Facebook and Google, external ID. The external ID does not work on my Production environment (works on Test) and I believe this is because of the Load Balancer... (I am offloading SSL on load balancer and the backend servers use HTTP).
I have seen This Question and I think this is my problem: The login request contains a returnurl and because the backend server is using http, the return url is also http (not https). This is the method that I am talking about in AccountController:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult ExternalLogin(string provider, string returnUrl)
{
// Request a redirect to the external login provider
return new ChallengeResult(provider, Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl }));
}
When user tries to login, I can see that redirect_uri is http (not https):
I have checked the question mentioned above and also this one. They both suggest that I should use relative path for redirect url in Startup.Auth, so this is what I have added to my code (ConfigureAuth method) :
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser, long>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentityCallback: (manager, user) => user.GenerateUserIdentityAsync(manager),
getUserIdCallback: id => id.GetUserId<long>()),
/* THIS IS THE CHANGE THAT I HAVE ADDED */
OnApplyRedirect = context =>
{
/* I have tried adding a breakpoint here, but it is never hit */
var redirectUri = new Uri(context.RedirectUri, UriKind.Absolute);
if (redirectUri.Scheme == "http" && redirectUri.Host == context.Request.Uri.Host)
{
context.RedirectUri = redirectUri.PathAndQuery;
}
context.Response.Redirect(context.RedirectUri);
}
}
});
But this change has no effect and the redirect_url remains http. If I put a break point on the change that I have added above, the breakpoint never hits... not sure where things are going wrong?
I found a solution using this question & this github issue:
Load Balancer would terminate the SSL and communicate with the backend server using http, but it would forward the original protocol (https, in this case) to backend server. So we could use x-forwarded-proto to detect the original protocol in the backend server:
public partial class Startup
{
public void ConfigureAuth(IAppBuilder app)
{
// Configure the db context, user manager and signin manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
/* I HAVE ADDED THIS CODE */
app.Use((context, next) =>
{
if (context.Request.Headers["x-forwarded-proto"] == "https")
{
context.Request.Scheme = "https";
}
return next();
});
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser, long>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentityCallback: (manager, user) => user.GenerateUserIdentityAsync(manager),
getUserIdCallback: id => id.GetUserId<long>())
}
});
// more code...
}
I'm using oauth and owin for my authentication in my app.
i want to regenerate identity with new claims in an interval but the method assigned to it's function handler is never been called
here is my code:
public static void ConfigureCookieAuthentication(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = Microsoft.AspNet.Identity.DefaultAuthenticationTypes.ApplicationCookie,
ExpireTimeSpan = TimeSpan.FromMinutes(30),
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, PouyaKianParham.ContentProviderSystem.BusinessObjectLayer.Account.Models.ApplicationUser, int>(
validateInterval: TimeSpan.FromSeconds(15),
regenerateIdentityCallback: (a, b) => GenerateUserIdentityAsync(null,null),
getUserIdCallback: (id) => (id.GetUserId<int>()))
},
LoginPath = new PathString("/Account/Login")
});
}
And here is my callback method(which is also in OAuthconfig.cs):
public static async Task<ClaimsIdentity> GenerateUserIdentityAsync(ApplicationUserManager ApplicationUserManager, UserManager manager)
{
//regenrating identity with new claims
return identitywithClaim;
}
Thanks in advance
I've had the same problem, but the following worked for me.
I use a custom implementation of UserStore, UserManager and so on. It turned out, that the UserStore has to implement IUserSecurityStampStore<TUser, in TKey>.
After it was implemented, the regenerateIdentityCallback delegate is called when needed and everything works beautifully.
I've configured OWIN in my ASP.NET MVC application using cookie authentication, but when I attempt to access an ApiController with an Authorize attribute on it, authorization fails and I can't figure out why. Stepping into the IsAuthorized method of the Authorize attribute, I can see that none of the identity properties that are present when accessing an MVC controller are present, so it certainly appears (at least to the authorize attribute) that the user is not authenticated.
The app is configured as follows:
public void ConfigureAuth(IAppBuilder app)
{
app.CreatePerOwinContext(MyAuthContext.Create);
app.CreatePerOwinContext<MyUserManager>(MyUserManager.Create);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<MyUserManager, MyUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
var httpConfig = new HttpConfiguration();
WebApiConfig.Register(httpConfig);
app.UseWebApi(httpConfig);
}
Do I absolutely have to use bearer tokens for WebAPI or is there just something I'm missing.
I have created a simple web service - WebAPI 2 using owin hosted on IIS
in my startup file
public void Configuration(IAppBuilder app)
{
// For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=316888
ConfigureOAuth(app);
var configuration = new HttpConfiguration();
WebApiConfig.Register(configuration);
app.UseWebApi(configuration);
app.UseCors(CorsOptions.AllowAll);
}
public void ConfigureOAuth(IAppBuilder app)
{
var oAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new SimpleAuthorizationServerProvider()
};
// Token Generation
app.UseOAuthAuthorizationServer(oAuthServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
}
and created a simple controller with authorize attribute. in the Identity Model i am adding information which I want to read when the user call the server
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim("sub", context.UserName));
identity.AddClaim(new Claim("role", "user"));
identity.AddClaim(new Claim("session", "50"));
on every request i want to get the session value, is there a way to do it? how can i add middleware to intercept the token authorization process?
You can try something like this within the controller action:
IPrincipal x = ControllerContext.RequestContext.Principal;
ClaimsIdentity ci = x.Identity as ClaimsIdentity;
string session = ci.FindFirst("session").Value;
The ASP.NET Identity 2.0 alpha ships with new middleware to manage getting an instance of the UserManager (app.UseUserManagerFactory to set this up) and getting an instance of the DbContext (app.UseDbContextFactory to set this up). There is an example showing how to get this working with an MVC app, but there is no documentation on how to get this working from the SPA template which uses OAuthBearerTokens, unlike the sample.
I currently am stuck with:
UserManagerFactory = () => new DerivedUserManager(new CustomUserStore(new CustomDbContext()));
OAuthOptions = new Microsoft.Owin.Security.OAuth.OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new MyApp.Web.Api.Providers.ApplicationOAuthProvider(PublicClientId, UserManagerFactory),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
AllowInsecureHttp = true
};
app.UseOAuthBearerTokens(OAuthOptions);
and have no idea how to replace the UserManagerFactory above with calls like these from the 2.0 alpha samples while still working with the OAuthBearerTokens objects used in the SPA template:
app.UseDbContextFactory(ApplicationDbContext.Create);
// Configure the UserManager
app.UseUserManagerFactory(new IdentityFactoryOptions<ApplicationUserManager>()
{
DataProtectionProvider = app.GetDataProtectionProvider(),
Provider = new IdentityFactoryProvider<ApplicationUserManager>()
{
OnCreate = ApplicationUserManager.Create
}
});
Thanks...
-Ben
I am adding stubs here which show you how you can use OAuthBearerTokens... You do not have to use the UserManagerFactory that you were using in SPA. You can switch that to use the PerOWINContext pattern.
Startup.Auth.cs
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(PublicClientId),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
AllowInsecureHttp = true
};
ApplicationOAuthProvider.cs
public ApplicationOAuthProvider(string publicClientId)
{
if (publicClientId == null)
{
throw new ArgumentNullException("publicClientId");
}
_publicClientId = publicClientId;
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager,
OAuthDefaults.AuthenticationType);
ClaimsIdentity cookiesIdentity = await user.GenerateUserIdentityAsync(userManager,
DefaultAuthenticationTypes.ApplicationCookie);
AuthenticationProperties properties = CreateProperties(user.UserName);
AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
context.Validated(ticket);
context.Request.Context.Authentication.SignIn(cookiesIdentity);
}
// namespace below needed to enable GetUserManager extension of the OwinContext
using Microsoft.AspNet.Identity.Owin;
Some New Patterns with ASP.NET Identity 2.0
The ASP.NET Identity includes support for creating a single instance of a the UserManager and the identity DBContext per application request. To support this pattern use the following extension methods per the IAppBuilder object:
app.CreatePerOwinContext<AppUserIdentityDbContext>(AppUserIdentityDbContext.Create);
app.CreatePerOwinContext<AppUserManager>(AppUserManager.Create);
You can find a great example implementing this very pattern below:
ASP.NET Identity 2.0 Cookie & Token Authentication including a sample project.
Here is the AppManager Class:
public class AppUserManager : UserManager<AppUserIdentity>
{
public AppUserManager(IUserStore<AppUserIdentity> store)
: base(store) { }
public static AppUserManager Create(IdentityFactoryOptions<AppUserManager> options, IOwinContext context)
{
var manager = new AppUserManager(new UserStore<AppUserIdentity>(context.Get<AppUserIdentityDbContext>()));
return manager;
}
}
This acticle uses the OWIN Middleware components UseOAuthBearerAuthentication and UseCookieAuthentication to support browser based authentication along with single Owin context IdentityDb Objects and a single AppManager.
Setup Bearer Tokens
Startup.Auth.cs
OAuthBearerOptions = new OAuthBearerAuthenticationOptions();
//This will used the HTTP header: "Authorization" Value: "Bearer 1234123412341234asdfasdfasdfasdf"
app.UseOAuthBearerAuthentication(OAuthBearerOptions);
// Enable the application to use a cookie to store information for the signed in user
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login")
});
HostAuthenticationFilter represents an authentication filter that authenticates via OWIN middleware:
WebApiConfig.cs
config.SuppressDefaultHostAuthentication();
//This will used the HTTP header: "Authorization" Value: "Bearer 1234123412341234asdfasdfasdfasdf"
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
To Generate a Token:
var identity = new ClaimsIdentity(Startup.OAuthBearerOptions.AuthenticationType);
identity.AddClaim(new Claim(ClaimTypes.Name, user));
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, userIdentity.Id));
AuthenticationTicket ticket = new AuthenticationTicket(identity, new AuthenticationProperties());
var currentUtc = new SystemClock().UtcNow;
ticket.Properties.IssuedUtc = currentUtc;
ticket.Properties.ExpiresUtc = currentUtc.Add(TimeSpan.FromMinutes(30));
string AccessToken = Startup.OAuthBearerOptions.AccessTokenFormat.Protect(ticket);
return AccessToken;
Ben, some of these things have changed from the alpha1 to beta1 builds (currently available on the ASP.NET Nightly NuGet Repo at https://aspnetwebstack.codeplex.com/wikipage?title=Use%20Nightly%20Builds). If you upgrade to the latest beta bits, you will not be using this syntax anymore but this instead:
// Configure the db context and user manager to use per request
app.CreatePerOwinContext(ApplicationIdentityContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
Also, notice that HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager> is now moved to Microsoft.AspNet.Identity.Owin.
You can install the `Microsoft.AspNet.Identity.Samples' package (preferably in a new MVC project because it might overwrite files). It helped me learn how they do certain things considering documentation for 2.0 is non-existent at the moment besides a few blog posts (all of which written for the alpha1 builds).