I am creating a Web Api application and I want to use bearer tokens for the user authentication.
I implemented the token logic, following this post and everything seems to work fine.
NOTE: I am not using the ASP.NET Identity Provider. Instead I have created a custom User entity and services for it.
public class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureOAuth(app);
var config = new HttpConfiguration();
var container = DependancyConfig.Register();
var dependencyResolver = new AutofacWebApiDependencyResolver(container);
config.DependencyResolver = dependencyResolver;
app.UseAutofacMiddleware(container);
app.UseAutofacWebApi(config);
WebApiConfig.Register(config);
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
app.UseWebApi(config);
}
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 this is my implementation of the SimpleAuthorizationServerProvider class
private IUserService _userService;
public IUserService UserService
{
get { return (IUserService)(_userService ?? GlobalConfiguration.Configuration.DependencyResolver.GetService(typeof(IUserService))); }
set { _userService = value; }
}
public async override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
var user = await UserService.GetUserByEmailAndPassword(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim("sub", context.UserName));
identity.AddClaim(new Claim("role", "user"));
context.Validated(identity);
}
}
After I call the /token url, I receive the following error
No scope with a Tag matching 'AutofacWebRequest' is visible from the scope in which the instance was requested. This generally indicates that a component registered as per-HTTP request is being requested by a SingleInstance() component (or a similar scenario.) Under the web integration always request dependencies from the DependencyResolver.Current or ILifetimeScopeProvider.RequestLifetime, never from the container itself
Is there a way to use dependency injection inside this class? I am using a repository pattern to access my entities, so I don't think that it is a good idea to make a new instance of the object context. What is the correct way to do this?
I have had a similar problem.
The problem here is that when you try to inject IUserService in your provider, Autofac detects that it has been registered as InstancePerRequest (that uses the well-known lifetime scope tag 'AutofacWebRequest') but the SimpleAuthorizationServerProvider is registered in the 'root' container scope where the 'AutofacWebRequest' scope is not visible.
A proposed solution is to register dependencies as InstancePerLifetimeScope. This apparently solved the problem but introduces new ones. All dependencies are registered in the 'root' scope, that implies having the same DbContext and services instances for all the requests. Steven explains very good in this answer why is not a good idea to share the DbContext between requests.
After deeper investigation tasks, I've solved the problem getting the 'AutofacWebRequest' from the OwinContext in the OAuthAuthorizationServerProvider class and resolving the services dependencies from it, instead of letting Autofac to inject them automatically. For this I've used the OwinContextExtensions.GetAutofacLifetimeScope() extension method from Autofac.Integration.Owin, see example below:
using Autofac.Integration.Owin;
...
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
...
// autofacLifetimeScope is 'AutofacWebRequest'
var autofacLifetimeScope = OwinContextExtensions.GetAutofacLifetimeScope(context.OwinContext);
var userService = autofacLifetimeScope.Resolve<IUserService>();
...
}
I've made OAuthAuthorizationServerProvider registration and injection inside ConfigureOAuth method in a similar way than proposed by Laurentiu Stamate in another response to this question, as SingleInstance().
I've implemented RefreshTokenProvider in the same way.
EDIT
#BramVandenbussche, this is my Configuration method in the Startup class, where you can see the order of middlewares added to the OWIN pipeline:
public void Configuration(IAppBuilder app)
{
// Configure Autofac
var container = ConfigureAutofac(app);
// Configure CORS
ConfigureCors(app);
// Configure Auth
ConfigureAuth(app, container);
// Configure Web Api
ConfigureWebApi(app, container);
}
To use dependency injection in SimpleAuthorizationServerProvider you have to register IOAuthAuthorizationServerProvider to the Autofac container just like any other type. You can do something like this:
builder
.RegisterType<SimpleAuthorizationServerProvider>()
.As<IOAuthAuthorizationServerProvider>()
.PropertiesAutowired() // to automatically resolve IUserService
.SingleInstance(); // you only need one instance of this provider
You also need to pass the container to the ConfigureOAuth method and let Autofac resolve your instance like this:
var oAuthServerOptions = new OAuthAuthorizationServerOptions
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = container.Resolve<IOAuthAuthorizationServerProvider>()
};
You should always use single instances if your properties within the object don't change via external data (let's say you have a property which you set in the controller which dependents upon some information stored in the database - in this case you should use InstancePerRequest).
I also tried #jumuro answer using the OwinContextExtensions.GetAutofacLifetimeScope that saves my day. Instead of registering the IUserService at runtime, this answer gives an option on validation/creating the instance service after request.
I added some new answer because I can't comment yet because of my low reputations but added additional guide codes to help someone.
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
try
{
if (service == null)
{
var scope = Autofac.Integration.Owin.OwinContextExtensions.GetAutofacLifetimeScope(context.OwinContext);
service = scope.Resolve<IUserService>();
}
var user = await service.FindUserAsync(context.UserName);
if (user?.HashedPassword != Helpers.CustomPasswordHasher.GetHashedPassword(context.Password, user?.Salt))
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
}
catch(Exception ex)
{
context.SetError("invalid_grant", ex.Message);
return;
}
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
AuthenticationProperties properties = CreateProperties(context.UserName);
AuthenticationTicket ticket = new AuthenticationTicket(identity, properties);
context.Validated(ticket);
context.Request.Context.Authentication.SignIn(identity);
}
Related
I was reading article from here
This way we can add claim during login
var user = userManager.Find(userName, password);
identity.AddClaim(new Claim(ClaimTypes.Email, user.Email));
AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = persistCookie }, identity);
This way reading back the value stored in clam
var prinicpal = (ClaimsPrincipal)Thread.CurrentPrincipal;
var email = prinicpal.Claims.Where(c => c.Type == ClaimTypes.Email).Select(c => c.Value).SingleOrDefault();
Now I have few questions
How could I add my custom data to claim. Suppose user role names.
Suppose the things I want to add that is not available in ClaimTypes then how could I add my custom data to claim?
How to read back my custom data stored in claim?
My action is decorated with authorized attribute where role name is specified like below one:
..
public class HomeController : Controller
{
[Authorize(Roles = "Admin, HrAdmin")]
public ActionResult PayRoll()
{
return View();
}
}
Do I need to go for custom authentication to extract roles from claim to set in GenericPrincipal?
Last question: When we go for role based authorization then roles are stored in authorization cookie? Do I need to write code to store roles in authorization cookie or ASP.net engine does it for us?
Same way claims are store in authorization cookie generated by owin cookie?
If you are using Identity than identity have its own method which can handle roles and everything you just have to login with this line.
var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
you will have to add role manager in Identity Config File
public class ApplicationRoleManager : RoleManager<IdentityRole>
{
public ApplicationRoleManager(IRoleStore<IdentityRole, string> roleStore)
: base(roleStore)
{ }
public static ApplicationRoleManager Create(
IdentityFactoryOptions<ApplicationRoleManager> options,
IOwinContext context)
{
var manager = new ApplicationRoleManager(
new RoleStore<IdentityRole>(context.Get<ApplicationDbContext>()));
return manager;
}
}
and register in Startup.Auth.cs
app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);
and you won't have to give roles to the authentication manually. you just have to write
[Authorize(Roles = "Admin, HrAdmin")]
if you want to add that manually without identity given method than use this below
private void IdentityLogin(UserInfo UserInfo)
{
// Waleed: added the role in the claim
var identity = new ClaimsIdentity(new[] {
new Claim(ClaimTypes.Name, UserInfo.Email),
new Claim(ClaimTypes.Sid, UserInfo.UserID),
new Claim(ClaimTypes.Role, UserInfo.Roles)
}, DefaultAuthenticationTypes.ApplicationCookie);
var claimsPrincipal = new ClaimsPrincipal(identity);
// Set current principal
Thread.CurrentPrincipal = claimsPrincipal;
var ctx = Request.GetOwinContext();
var authManager = ctx.Authentication;
authManager.SignIn(identity);
}
Claims are of two types one are in your session and other are stored in db. Session Claims are above in IdentityLogin method and db claims can be written as
UserManager.AddClaim(userId,new Claim())
I've a project using ASPNET MVC5 with WebApi2, OWIN and SimpleInjector as the IoC container.
I've read that if you're using OWIN you should get your HttpConfiguration, you'll create one rather than using GlobalConfiguration.
Like this in StartUp class, Configuration method:
var config = new HttpConfiguration();
config.DependencyResolver = new SimpleInjectorWebApiDependencyResolver(container);
but with that I receive the error that I need a parameterless contructor, and if I use:
GlobalConfiguration.Configuration.DependencyResolver =
new SimpleInjectorWebApiDependencyResolver(container);
combined with (in SimpleInjectorInitializer):
container.RegisterWebApiControllers(GlobalConfiguration.Configuration,
AppDomain.CurrentDomain.GetAssemblies());
It works, but I read on the internet:
A common error in OWIN integration is use of the GlobalConfiguration.Configuration. In OWIN you create the configuration from scratch. You should not reference GlobalConfiguration.Configuration anywhere when using the OWIN integration.
Am I doing it OK for the OWIN and frameworks and technologies being used?
I've tried to follow this implementation of SimpleInjector with WebApi (there they're using MVC so I changed it a little). MVC 5 template (ASP.Net Identity 2.0) combined with Simple Injector
This is my snippet of the StartUp class now:
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
var container = SimpleInjectorInitializer.Initialize(app);
GlobalConfiguration.Configuration.DependencyResolver =
new SimpleInjectorWebApiDependencyResolver(container);
ConfigureAuth(app, container);
}
public void ConfigureAuth(IAppBuilder app, Container container)
{
// Added for IoC usage: See https://simpleinjector.codeplex.com/discussions/564822
app.Use(async (context, next) => {
using (container.BeginExecutionContextScope())
{
await next();
}
});
app.CreatePerOwinContext(() => container.GetInstance<ApplicationUserManager>());
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
// Configure the application for OAuth based flow
PublicClientId = "self";
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(PublicClientId),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
// In production mode set AllowInsecureHttp = false
AllowInsecureHttp = true
};
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptions);
}
SimpleInjectorInitializer class
public static class SimpleInjectorInitializer
{
public static Container Initialize(IAppBuilder app)
{
var container = GetInitializeContainer(app);
container.Verify();
return container;
}
public static Container GetInitializeContainer(IAppBuilder app)
{
var container = new SimpleInjector.Container();
container.Options.DefaultScopedLifestyle = new WebApiRequestLifestyle();
container.RegisterSingleton<IAppBuilder>(app);
#region Webapi Registration
// IoC for ASP.NET Identity
container.RegisterWebApiRequest<ApplicationUserManager>();
container.RegisterWebApiRequest<ApplicationDbContext>(() => new ApplicationDbContext(ConfigurationManager.ConnectionStrings["orgIdentityConnectionString"].ConnectionString));
container.RegisterWebApiRequest<IUserStore<ApplicationUser>>(() => new UserStore<ApplicationUser>(container.GetInstance<ApplicationDbContext>()));
container.RegisterInitializer<ApplicationUserManager>(manager => InitializeUserManager(manager, app));
// Setup for ISecureDataFormat
container.RegisterWebApiRequest<ISecureDataFormat<AuthenticationTicket>, SecureDataFormat<AuthenticationTicket>>();
container.RegisterWebApiRequest<ITextEncoder, Base64UrlTextEncoder>();
container.RegisterWebApiRequest<IDataSerializer<AuthenticationTicket>, TicketSerializer>();
container.RegisterWebApiRequest<IDataProtector>(() => new Microsoft.Owin.Security.DataProtection.DpapiDataProtectionProvider().Create("ASP.NET Identity"));
#endregion
container.RegisterWebApiRequest<IAuthenticationManager>(() =>
AdvancedExtensions.IsVerifying(container)
? new OwinContext(new Dictionary<string, object>()).Authentication
: HttpContext.Current.GetOwinContext().Authentication);
// The initialization runs all the map creation once so it is then done when you come to do your mapping.
// You can create a map whenever you want, but this will slow your code down as the mapping creation involves reflection.
Mapper.Initialize(config =>
{
config.ConstructServicesUsing(container.GetInstance);
config.AddProfile(new WebApiAutomapperProfile());
config.AddGlobalIgnore("Errors");
config.AddGlobalIgnore("IsModelValid");
config.AddGlobalIgnore("BaseValidator");
config.AddGlobalIgnore("AuditInformation");
});
var profiles = Assembly.GetExecutingAssembly()
.GetTypes()
.Where(x => typeof(AutoMapper.Profile).IsAssignableFrom(x));
var config2 = new MapperConfiguration(cfg =>
{
foreach (var profile in profiles)
{
cfg.AddProfile(Activator.CreateInstance(profile) as AutoMapper.Profile);
}
});
container.RegisterSingleton<MapperConfiguration>(config2);
container.Register<IMapper>(() => config2.CreateMapper(container.GetInstance));
#region MyStuff Registrations
container.Register(typeof(IRepository<>), typeof(orgRepository<>), Lifestyle.Scoped);
container.Register(typeof(IDatabaseFactory<>), typeof(DatabaseFactory<>), Lifestyle.Scoped);
var orgCatalogRegistration = Lifestyle.Scoped.CreateRegistration<Catalog>(container);
container.AddRegistration(typeof(IPublicCatalog), orgCatalogRegistration);
container.AddRegistration(typeof(IPrivateCatalog), orgCatalogRegistration);
#endregion
**// TODO: A common error in OWIN integration is use of the GlobalConfiguration.Configuration. In OWIN you create the configuration from scratch. You should not reference GlobalConfiguration.Configuration anywhere when using the OWIN integration.**
//Is the following line OK?
container.RegisterWebApiControllers(GlobalConfiguration.Configuration, AppDomain.CurrentDomain.GetAssemblies());
return container;
}
private static void InitializeUserManager(ApplicationUserManager manager, IAppBuilder app)
{
manager.UserValidator = new UserValidator<ApplicationUser>(manager)
{
AllowOnlyAlphanumericUserNames = false,
RequireUniqueEmail = true
};
//Configure validation logic for passwords
manager.PasswordValidator = new PasswordValidator()
{
RequiredLength = 6,
RequireNonLetterOrDigit = false,
RequireDigit = true,
RequireLowercase = true,
RequireUppercase = true,
};
var dataProtectionProvider =
app.GetDataProtectionProvider();
if (dataProtectionProvider != null)
{
manager.UserTokenProvider =
new DataProtectorTokenProvider<ApplicationUser>(
dataProtectionProvider.Create("ASP.NET Identity"));
}
}
}
}
If you take a look at the same page you got that quote from, you'll see you have to register the Web API configuration into OWIN's IAppBuilder as follows:
app.UseWebApi(config);
I have spent the last week creating an API for an existing MVC application, and am now attempting to secure the API along with reworking the MVC side security as needed.
Currently, the MVC application is set up to use an application cookie via OWIN/OAuth/Identity. I have attempted to incorporate the Bearer token that the Web API is set up to generate whenever making calls to restricted API methods, but have had little success so far - GET requests work just fine, but POST requests are losing the Authorization header when received by the API.
I have created an SDK Client that is being used by the MVC app to make the calls to the API, and have tried a total of three methods of setting the Authorization header for any given call to the API, all of which seem to work just fine for GET requests, but fail completely for any POST requests I need to make...
I can set the Request header in the MVC controller:
HttpContext.Request.Headers.Add("Authorization", "Bearer " + response.AccessToken);
(where response.AccessToken is the token previously retrieved from the API)
I can set the Request header via an extension method on the SDK Client:
_apiclient.SetBearerAuthentication(token.AccessToken)
or I can set the Request header manually on the SDK Client:
_apiClient.Authentication = new AuthenticationHeaderValue("Bearer, accessToken);
(Where accessToken is the token retrieved previously, passed to the Client method being called).
I have very little to go on from this point as to what is causing the issue. The only thing I have been able to glean so far is that ASP.NET causes all POST requests to first send in a request with an Expect header for an HTTP 100-Continue response, after which it will finish the actual POST request. However, it seems that when it does this second request, the Authorization header is no longer present and so the API's Authorize attribute will cause a 401-Unauthorized response instead of actually running the API method.
So, how do I take the Bearer token that I am able to retrieve from the API, and use it on subsequent requests, including the various POST requests that I will need to make?
Beyond that, what is the best way of storing this token on the MVC application itself? I would rather like to avoid having to pass around the string to every method in the application that could need it, but I also have been reading that storing it in a cookie is a very bad idea for security reasons.
A few further points that will be of interest immediately after I get passed this issue:
Does using OAuth Bearer Tokens mean that I can no longer use ApplicationCookies for the MVC application? And/or will it render the following code useless throughout the application?
User.Identity.GetUserId()
Currently I am forced into commenting out my API [Authorize] attributes in order to continue with my work, which obviously isn't ideal but it does allow me to get on with things temporarily.
Startup files:
MVC:
public class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
}
private void ConfigureAuth(IAppBuilder app)
{
app.CreatePerOwinContext(ADUIdentityDbContext.Create);
app.CreatePerOwinContext<ADUUserManager>(ADUUserManager.Create);
app.UseOAuthBearerTokens(new OAuthAuthorizationServerOptions
{
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
//This should be set to FALSE before we move to production.
AllowInsecureHttp = true,
ApplicationCanDisplayErrors = true,
TokenEndpointPath = new PathString("/api/token"),
});
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ExternalBearer,
CookieName = "ADU",
ExpireTimeSpan = TimeSpan.FromHours(2),
LoginPath = new PathString("/Account/Login"),
SlidingExpiration = true,
});
}
}
API
public class Startup
{
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
config.DependencyResolver = new NinjectResolver(new Ninject.Web.Common.Bootstrapper().Kernel);
WebApiConfig.Register(config);
ConfigureOAuth(app);
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
app.UseWebApi(config);
}
public void ConfigureOAuth(IAppBuilder app)
{
app.CreatePerOwinContext(ADUIdentityDbContext.Create);
app.CreatePerOwinContext<ADUUserManager>(ADUUserManager.Create);
OAuthAuthorizationServerOptions oAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/api/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new SimpleAuthorizationServerProvider(),
};
//token generation
app.UseOAuthAuthorizationServer(oAuthServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
}
}
public class SimpleAuthorizationServerProvider : OAuthAuthorizationServerProvider
{
private IUserBusinessLogic _userBusinessLogic;
/// <summary>
/// Creates the objects necessary to initialize the user business logic field and initializes it, as this cannot be done by dependency injection in this case.
/// </summary>
public void CreateBusinessLogic()
{
IUserRepository userRepo = new UserRepository();
IGeneratedExamRepository examRepo = new GeneratedExamRepository();
IGeneratedExamBusinessLogic examBLL = new GeneratedExamBusinessLogic(examRepo);
_userBusinessLogic = new UserBusinessLogic(userRepo, examBLL);
}
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context) { context.Validated(); }
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
//create a claim for the user
ClaimsIdentity identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim("sub", user.Id));
context.Validated(identity);
}
}
After a good deal of time working on other aspects of the project, implementing other features has actually made solving this far easier - there is now a Response Wrapper Handler as part of the API, and part of that Handler saves all Headers from the incoming Requests and adds them to the outgoing Responses. I believe this is allowing the ASP.NET MVC side of the application to send the Authorization header again after the 200-OK request is initially sent.
I have modified my authentication in order to take advantage of Roles, but I will attempt to exclude that code as it should not be relevant here:
MVC Startup.cs:
public class Startup
{
public void Configuration(IAppBuilder app) { ConfigureAuth(app); }
/// <summary>
/// Configures authentication settings for OAuth.
/// </summary>
/// <param name="app"></param>
private void ConfigureAuth(IAppBuilder app)
{
app.CreatePerOwinContext(ADUIdentityDbContext.Create);
app.CreatePerOwinContext<ADUUserManager>(ADUUserManager.Create);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
CookieName = "ADU",
ExpireTimeSpan = TimeSpan.FromHours(2),
LoginPath = new PathString("/Account/Login"),
SlidingExpiration = true
});
}
}
Where it is used (AccountController):
private async Task CreateLoginCookie(AuthorizationToken response, User result)
{
//Create the claims needed to log a user in
//(uses UserManager several layers down in the stack)
ClaimsIdentity cookieIdent = await _clientSDK.CreateClaimsIdentityForUser(response.AccessToken, result, true).ConfigureAwait(false);
if (cookieIdent == null) throw new NullReferenceException("Failed to create claims for cookie.");
cookieIdent.AddClaim(new Claim("AuthToken", response.AccessToken));
AuthenticationProperties authProperties = new AuthenticationProperties();
authProperties.AllowRefresh = true;
authProperties.IsPersistent = true;
authProperties.IssuedUtc = DateTime.Now.ToUniversalTime();
IOwinContext context = HttpContext.GetOwinContext();
AuthenticateResult authContext = await context.Authentication.AuthenticateAsync(DefaultAuthenticationTypes.ApplicationCookie);
if (authContext != null)
context.Authentication.AuthenticationResponseGrant = new AuthenticationResponseGrant(cookieIdent, authContext.Properties);
//Wrapper methods for IOwinContext.Authentication.SignOut()/SignIn()
SignOut();
SignIn(authProperties, cookieIdent);
}
In my SDK layer, I created a method that I call from the various other methods I use to reach my API in order to set the Authorization for each outgoing Request (I'd like to figure out how to make this into an Attribute, but I'll worry about that later):
private void SetAuthentication()
{
ClaimsIdentity ident = (ClaimsIdentity)Thread.CurrentPrincipal.Identity;
Claim claim;
//Both of these methods (Thread.CurrentPrincipal, and ClaimsPrincipal.Current should work,
//leaving both in for the sake of example.
try
{
claim = ident.Claims.First(x => x.Type == "AuthToken");
}
catch (Exception)
{
claim = ClaimsPrincipal.Current.Claims.First(x => x.Type == "AuthToken");
}
_apiClient.SetBearerAuthentication(claim.Value);
}
API Startup.cs
/// <summary>
/// Configures the settings used by the framework on application start. Dependency Resolver, OAuth, Routing, and CORS
/// are configured.
/// </summary>
/// <param name="app"></param>
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
config.DependencyResolver = new NinjectResolver(new Bootstrapper().Kernel);
WebApiConfig.Register(config);
ConfigureOAuth(app);
app.UseCors(CorsOptions.AllowAll);
app.UseWebApi(config);
}
/// <summary>
/// Configures authentication options for OAuth.
/// </summary>
/// <param name="app"></param>
public void ConfigureOAuth(IAppBuilder app)
{
app.CreatePerOwinContext(ADUIdentityDbContext.Create);
app.CreatePerOwinContext<ADUUserManager>(ADUUserManager.Create);
OAuthAuthorizationServerOptions oAuthServerOptions = new OAuthAuthorizationServerOptions
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/api/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new SimpleAuthorizationServerProvider()
};
//token generation
app.UseOAuthAuthorizationServer(oAuthServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
}
SimpleAuthorizationServerProvider.cs:
/// <summary>
/// Creates an access bearer token and applies custom login validation logic to prevent invalid login attempts.
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
// Performs any login logic required, such as accessing Active Directory and password validation.
User user = await CustomLoginLogic(context).ConfigureAwait(false);
//If a use was not found, add an error if one has not been added yet
if((user == null) && !context.HasError) SetInvalidGrantError(context);
//Break if any errors have been set.
if (context.HasError) return;
//create a claim for the user
ClaimsIdentity identity = new ClaimsIdentity(context.Options.AuthenticationType);
//Add some basic information to the claim that will be used for the token.
identity.AddClaim(new Claim("Id", user?.Id));
identity.AddClaim(new Claim("TimeOf", DateTime.Now.ToShortDateString() + " " + DateTime.Now.ToLongTimeString()));
//Roles auth
SetRoleClaim(user, ref identity);
context.Validated(identity);
}
And finally, the apparent key that wraps everything up together:
public class ResponseWrappingHandler : DelegatingHandler
{
/// <summary>
/// Catches the request before processing is completed and wraps the resulting response in a consistent response wrapper depending on the response returned by the api.
/// </summary>
/// <param name="request">The request that is being processed.</param>
/// <param name="cancellationToken">A cancellation token to cancel the processing of a request.</param>
/// <returns></returns>
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
//Calls Wrapping methods depending on conditions,
//All of the Wrapping methods will make a call to PreserveHeaders()
}
/// <summary>
/// Creates a response based on the provided request with the provided response's status code and request headers, and the provided response data.
/// </summary>
/// <param name="request">The original request.</param>
/// <param name="response">The reqsponse that was generated.</param>
/// <param name="responseData">The data to include in the wrapped response.</param>
/// <returns></returns>
private static HttpResponseMessage PreserveHeaders(HttpRequestMessage request, HttpResponseMessage response, object responseData)
{
HttpResponseMessage newResponse = request.CreateResponse(response.StatusCode, responseData);
foreach (KeyValuePair<string, IEnumerable<string>> header in response.Headers)
newResponse.Headers.Add(header.Key, header.Value);
return newResponse;
}
With all of that in place my project is now able to use authorization/authentication without needing client secrets and such (which was one of the goals of my employer).
Situation:
I have a Web API 2 project which acts as an Authorization server (/token endpoint) and a resource server. I am using the template that comes out of box with ASP.Net Web API minus any MVC reference.
The Start.Auth is configured as below:
public void ConfigureAuth(IAppBuilder app)
{
// Configure the db context and user manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
// Configure the application for OAuth based flow
PublicClientId = "self";
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(PublicClientId),
AuthorizeEndpointPath = new PathString("/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
// In production mode set AllowInsecureHttp = false
AllowInsecureHttp = true
};
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptions);
var facebookAuthenticationOptions = new FacebookAuthenticationOptions()
{
AppId = ConfigurationManager.AppSettings["Test_Facebook_AppId"],
AppSecret = ConfigurationManager.AppSettings["Test_Facebook_AppSecret"],
//SendAppSecretProof = true,
Provider = new FacebookAuthenticationProvider
{
OnAuthenticated = (context) =>
{
context.Identity.AddClaim(new System.Security.Claims.Claim("FacebookAccessToken", context.AccessToken));
return Task.FromResult(0);
}
}
};
facebookAuthenticationOptions.Scope.Add("email user_about_me user_location");
app.UseFacebookAuthentication(facebookAuthenticationOptions);
}
The MVC 5 Client (different Project) uses the Web API app for authorization and data. Below is the code to retrieve the Bearer token in case of Username/Password store:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
if (!ModelState.IsValid)
{
model.ExternalProviders = await GetExternalLogins(returnUrl);
return View(model);
}
var client = Client.GetClient();
var response = await client.PostAsync("Token",
new StringContent(string.Format("grant_type=password&username={0}&password={1}", model.Email, model.Password), Encoding.UTF8));
if (response.IsSuccessStatusCode)
{
return RedirectToLocal(returnUrl);
}
return View();
}
Problem
I could retrieve the Bearer token and then add it to the Authorization Header for subsequent calls. I think that would be ok in case of an Angular App or a SPA. But I think there should be something in MVC that handles it for me, like automatically store it in a cookie and send the cookie on subsequent requests. I have searched around quite a lot and there are posts which hint towards this (Registering Web API 2 external logins from multiple API clients with OWIN Identity) but I haven't been able to figure out what to do after I get a token.
Do I need to add something in the MVC app Startup.Auth?
Ideally, I need the functionality which the AccountController in ASP.Net Template (MVC + Web API) gives out of box (Logins, Register, External logins, forget password etc etc...) but with the MVC and Web API in different projects.
Is there a template or a git repo which has this boiler plate code?
Thanks in advance!
Update
Incorporating #FrancisDucharme suggestions, below is the code for GrantResourceOwnerCredentials().
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,
CookieAuthenticationDefaults.AuthenticationType);
AuthenticationProperties properties = CreateProperties(user.UserName);
AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
//Add a response cookie...
context.Response.Cookies.Append("Token", context.Options.AccessTokenFormat.Protect(ticket));
context.Validated(ticket);
context.Request.Context.Authentication.SignIn(cookiesIdentity);
}
But I can't still seem to get that Cookie or figure out what to do next.
Restating Questions:
What would be the correct way to authenticate, authorize and call Web API methods (Auth and Resource server) from an MVC client?
Is there boilerplate code or template for AccountController which does the basic plumbing (Login, register - internal/external, forgot password etc.)?
You could have your Startup class return a response cookie that the client will then return on all subsequent requests, here's an example. I would do it in GrantResourceOwnerCredentials.
public class AuthorizationServerProvider : OAuthAuthorizationServerProvider
{
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
//your authentication logic here, if it fails, do this...
//context.SetError("invalid_grant", "The user name or password is incorrect.");
//return;
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim("sub", context.UserName));
identity.AddClaim(new Claim("role", "user"));
AuthenticationTicket ticket = new AuthenticationTicket(identity);
//Add a response cookie...
context.Response.Cookies.Append("Token", context.Options.AccessTokenFormat.Protect(ticket));
context.Validated(ticket);
}
The Startup class:
public partial class Startup
{
public static OAuthBearerAuthenticationOptions OAuthBearerOptions { get; private set; }
public Startup()
{
OAuthBearerOptions = new OAuthBearerAuthenticationOptions();
}
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
ConfigureOAuth(app);
//I use CORS in my projects....
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
app.UseWebApi(config);
WebApiConfig.Register(config);
}
public void ConfigureOAuth(IAppBuilder app)
{
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true, //I have this here for testing purpose, production should always only accept HTTPS encrypted traffic.
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(30),
Provider = new AuthorizationServerProvider()
};
app.UseOAuthAuthorizationServer(OAuthServerOptions);
app.UseOAuthBearerAuthentication(OAuthBearerOptions);
}
}
That assumes the client has cookies enabled, of course.
Then, modify your MVC headers to add the Authorization header to all requests as such.
In the ActionFilterAttribute, fetch your cookie value (Token) and add the header.
Instead of storing in session, I have added it to the the DefaultRequestHeaders as shown below so I don't need to add this in every call I make to Web API.
public async Task AuthenticateUser(string username, string password)
{
var data = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("grant_type", "password"),
new KeyValuePair<string, string>("username", username),
new KeyValuePair<string, string>("password", password)
});
using (HttpResponseMessage response = await APIClient.PostAsync("/Token", data))
{
if (response.IsSuccessStatusCode)
{
var result = await response.Content.ReadAsAsync<AuthenticatedUser>();
APIClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", result.Access_Token);
}
else
{
throw new Exception(response.ReasonPhrase);
}
}
}
I'm cobbling together snippets of code from blogs and different places to try to get this to work. Normally, I'd refer to the reference documentation, but I can't find it here or anywhere else. It's just videos and demos for specific use cases that include user management or facebook or twitter.
I have a proprietary authentication service that I'm using. User accounts are not managed inside my application. So I need to be able to sign in a user that's completely constructed at run time.
Here's what I'm trying now in my MVC app.
using System.Security.Claims;
public class HomeController : Controller {
public ActionResult Scratch() {
var claims = new Claim[] {
new Claim(ClaimTypes.Name, "somename"),
new Claim(ClaimTypes.NameIdentifier, "someidentifier"),
new Claim("foo", "bar"),
};
var identity = new ClaimsIdentity(claims);
var authenticationManager = HttpContext.GetOwinContext().Authentication;
authenticationManager.SignIn(identity);
return Content(
$"authentication manager type: {authenticationManager.GetType()} \n"
+ $"authenticated: {HttpContext.User.Identity.IsAuthenticated} \n"
+ $"user name: {HttpContext.User.Identity.Name} \n",
"text/plain");
}
}
The output is
authentication manager type: Microsoft.Owin.Security.AuthenticationManager
authenticated: False
user name:
Questions:
Why does the output show that the user has not been authenticated? What more do I have to do to get this user authenticated?
Where is the documentation for this framework?
Update
Startup.cs
public partial class Startup {
public void Configuration(IAppBuilder app) {
ConfigureAuth(app);
ConfigureAnalyticContext(app);
}
}
Startup.Auth.cs:
(there is actually much more, but all the rest has been commented out, in search of finding a minimal configuration that works)
public partial class Startup {
public void ConfigureAuth(IAppBuilder app) {
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
}
}
AnalyticContext.Auth.cs
(this is my Entity Framework context, I doubt it's related to this problem)
public partial class Startup {
public void ConfigureAnalyticContext(IAppBuilder app) {
app.CreatePerOwinContext(() => CentoAnalyticsContext.Create());
}
}
Well, it seems that you are not using ASP.NET Identity. ASP.NET Identity is new membership system of asp.net, which automatically creates database tables for storing users, encrypting password, etc.
What you are trying to do is to use the new authentication system provided by OWIN, which replaces the old FormsAuthentication style.
To make it work, you have to create the cookie authentication. Like this:
public static class AuthConfig
{
public const string DefaultAuthType = "DefaultAppCookie";
public const string LoginPath = "/System/SignIn";
public static void ConfigureAuth(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthType,
LoginPath = new PathString(LoginPath)
});
AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.NameIdentifier; //or whatever
}
}
In the login action:
var claims = new Claim[] {
new Claim(ClaimTypes.Name, "somename"),
new Claim(ClaimTypes.NameIdentifier, "someidentifier"),
new Claim("foo", "bar"),
};
ClaimsIdentity identity = new ClaimsIdentity(claims, AuthConfig.DefaultAuthType);
IAuthenticationManager authManager = Request.GetOwinContext().Authentication;
authManager.SignIn(new AuthenticationProperties() { IsPersistent = true }, identity);
I think that should be enough to make it work in your app. A few days ago I answered a similar question MVC Authentication - Easiest Way, take a look, it might be helpful.
I recently have added Active Directory authentication, constructed ClaimsPrincipal myself and signed-in the same way you do.
And you are indeed missing .UseCookieAuthentication in your ConfigureAuth(IAppBuilder app)
public void ConfigureAuth(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "MyAuthenticationName", // <-- this must match the AuthenticatioType name when you do sign-out
LoginPath = new PathString("/MyLoginPath"),
CookieName = "MyCookieName",
CookieHttpOnly = true,
});
}
And you don't need UseExternalSignInCookie.
Request.IsAuthenticated will be false with in the same request flow.
I think you still need to update the current security principal if you need to check IsAuthenticated for the request as authenticationManager.SignIn only validates the user against data store and sets the OWIN cookie which when sent back in subsequent request sets the security principal , usually a redirect takes care of this as in most cases there will be redirection in home page or something. If you still need to check with in the same request you can do something like below depending on your requirement
var claims = new Claim[] {
new Claim(ClaimTypes.Name, "somename"),
new Claim(ClaimTypes.NameIdentifier, "someidentifier"),
new Claim("foo", "bar"),
};
var identity = new ClaimsIdentity(claims,DefaultAuthenticationTypes.ApplicationCookie,
ClaimTypes.Name, ClaimTypes.Role);
var principal = new ClaimsPrincipal(identity);
System.Threading.Thread.CurrentPrincipal = principal;
if (System.Web.HttpContext.Current != null)
System.Web.HttpContext.Current.User = principal;
Hope this helps.