I'm learning how to put the Asp.Net MVC Identity 2.0 to work.
I have this code that works for OAuth Bearer
[HttpGet]
[ActionName("Authenticate")]
[AllowAnonymous]
public String Authenticate(string user, string password)
{
if (string.IsNullOrEmpty(user) || string.IsNullOrEmpty(password))
{
return "Failed";
}
var userIdentity = UserManager.FindAsync(user, password).Result;
if (userIdentity != null)
{
if (User.Identity.IsAuthenticated)
{
return "Already authenticated!";
}
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(1));
string AccessToken = Startup.OAuthBearerOptions.AccessTokenFormat.Protect(ticket);
return AccessToken;
}
return "Failed in the end";
}
Here is the code for Startup.Auth.cs
//This will used the HTTP header Authorization: "Bearer 1234123412341234asdfasdfasdfasdf"
OAuthBearerOptions = new OAuthBearerAuthenticationOptions();
app.UseOAuthBearerAuthentication(OAuthBearerOptions);
I have looked at the source code for ClaimsIdentity and AuthenticationTicket and I don't see how the ticket is registered for the identity.
My question is how did this ticket get registered with the Owin pipeline?
My aim is to revoke this ticket if possible.
Thanks in advance.
First off, here is a great tutorial on ASP.NET Identity 2 by Taiseer Joudeh.
This is the line that adds Bearer token processing to an OWIN application pipeline.
app.UseOAuthBearerAuthentication(OAuthBearerOptions);
Also, did you write the authorization provider yourself? My startup code looks more like this:
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);
PublicClientId = "self";
OAuthServerOptions = new OAuthAuthorizationServerOptions
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/Token"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(1440), //TODO: change to smaller value in production, 15 minutes maybe
Provider = new SimpleAuthorizationServerProvider(PublicClientId),
RefreshTokenProvider = new SimpleRefreshTokenProvider()
};
app.UseOAuthAuthorizationServer(OAuthServerOptions);
OAuthBearerOptions = new OAuthBearerAuthenticationOptions();
app.UseOAuthBearerAuthentication(OAuthBearerOptions);
My SimpleAuthorizationServerProvider then has a Grant method like this:
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var allowedOrigin = context.OwinContext.Get<string>("as:clientAllowedOrigin") ?? "*";
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { allowedOrigin });
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;
}
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()));
identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
identity.AddClaim(new Claim("sub", context.UserName));
foreach (var role in userManager.GetRoles(user.Id))
{
identity.AddClaim(new Claim(ClaimTypes.Role, role));
}
var props = new AuthenticationProperties(new Dictionary<string, string>
{
{"as:client_id", context.ClientId ?? string.Empty}
});
var ticket = new AuthenticationTicket(identity, props);
context.Validated(ticket);
}
Just about all of this was based on the tutorial mentioned above. Hope it helps.
Update
There is no standard way to revoke a token according to Taiseer on this page.
Revoking access from authenticated users: Once the user obtains long lived access token he’ll be able to access the server resources
as long as his access token is not expired, there is no standard way
to revoke access tokens unless the Authorization Server implements
custom logic which forces you to store generated access token in
database and do database checks with each request. But with refresh
tokens, a system admin can revoke access by simply deleting the
refresh token identifier from the database so once the system requests
new access token using the deleted refresh token, the Authorization
Server will reject this request because the refresh token is no longer
available (we’ll come into this with more details).
However, here is an interesting approach that may accomplish what you need. It will just take a bit of custom implementation.
Related
We are in the process of learning Identity Server with the eventual aim of migrating our existing authentication service to it. For company, logistical and compatibility reasons, we are sticking with IS 3. We're not quite ready to move over to Core.
There are two parts to my question:
1) I have modified the sample app, available here, that uses a custom login page so that the browser prompts the user for their X509Certificate2 (as a partial login). The user enters a password and the certificate is used to call another endpoint which returns user-specific data. At that point, we wish to create custom user claims based on the returned data and then issue the cookie.
This all works fine up until the client receives the cookie. I cannot seem to extract the custom claims added to AuthenticatedLogin's Claims object on the client application. The client is configured to access all scopes.
It seems like I'm missing something very basic. Am I doing something wrong here? Bear in mind, these are just meaningless claims for test purposes.
2) Would this be an acceptable approach to issue claims? We would then likely use the returned cookie in order to call a separate authorisation service, as our roles are quite complex.
I have implemented the custom user service, with PreAuthenticateAsync redirecting to the custom login page:
public override Task PreAuthenticateAsync(PreAuthenticationContext context)
{
var id = ctx.Request.Query.Get("signin");
context.AuthenticateResult = new AuthenticateResult("~/custom/login?id=" + id, (IEnumerable<Claim>)null);
return Task.FromResult(0);
}
The controller method which creates the claims and calls IssueLoginCookie :
[RequireHttps]
[Route("core/custom/login")]
[HttpPost]
public ActionResult Index(string id, string password)
{
var userData = GetUser(password);
var owinEnvironment = Request.GetOwinContext().Environment;
var authenticatedLogin = new AuthenticatedLogin
{
IdentityProvider = Constants.BuiltInIdentityProvider,
Name = userData.UserName,
Subject = userData.EmailAddress,
Claims = GetClaims(userData),
PersistentLogin = false
};
owinEnvironment.IssueLoginCookie(authenticatedLogin);
var msg = owinEnvironment.GetSignInMessage(id);
var returnUrl = msg.ReturnUrl;
owinEnvironment.RemovePartialLoginCookie();
return Redirect(returnUrl);
}
// add our CUSTOM claims
private List<Claim> GetClaims(CustomUser authenticatedUser)
{
List<Claim> claims = new List<Claim>();
claims.Add(new Claim("claim1", authenticatedUser.CustomClaim1));
claims.Add(new Claim("claim2", authenticatedUser.CustomClaim2));
claims.Add(new Claim("claim3", authenticatedUser.CustomClaim3));
claims.Add(new Claim("Claim4", authenticatedUser.CustomClaim4));
return claims;
}
The client controller method with Authorize decorator:
[Authorize]
public ActionResult About()
{
// "CustomClaim1", "CustomClaim2" etc are not there :(
return View((User as ClaimsPrincipal).Claims);
}
The registered in-memory scope:
var scope1 = new Scope
{
Enabled = true,
Name = "user",
Type = ScopeType.Identity,
Claims = new List<ScopeClaim>
{
new ScopeClaim("CustomClaim1", true),
new ScopeClaim("CustomClaim2", true),
new ScopeClaim("CustomClaim3", true),
new ScopeClaim("CustomClaim4", true),
},
IncludeAllClaimsForUser = true
};
And finally the client's Configuration:
public void Configuration(IAppBuilder app)
{
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
AntiForgeryConfig.UniqueClaimTypeIdentifier = Constants.ClaimTypes.Subject;
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap = new Dictionary<string, string>();
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "Cookies"
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
Authority = "https://localhost/idprov/core",
ClientId = "mvc",
RedirectUri = "https://localhost/dummyclient/About",
ResponseType = "id_token",
ClientSecret = "secret",
Scope = "openid partyuser",
SignInAsAuthenticationType = "Cookies",
});
}
Hi Try adding scope in your client like
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
Authority = "https://localhost/idprov/core",
ClientId = "mvc",
RedirectUri = "https://localhost/dummyclient/About",
ResponseType = "id_token",
ClientSecret = "secret",
Scope = "openid partyuser CustomClaim1 CustomClaim2",
SignInAsAuthenticationType = "Cookies",
});
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 have a mvc 5 app that uses forms authentication but the real Authentication of user happens using bearer token in web api . I'm adding token details in the cookie so the website is always authenticated. MVC and Web api are in same project. Web api hosted using Owin.
here is my code snippet.
startup.cs
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
//Configure authorization
ConfigureOAuth(app);
//register WebAPI
app.UseWebApi(ConfigureWebApiRoutes());
}
}
startup.auth.cs
// 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("/Login/Login"),
CookieHttpOnly = true,
//AuthenticationMode = AuthenticationMode.Passive,
CookieName = "YetAnotherTodo.WebApi.Auth",
//#if DEBUG
// CookieSecure = CookieSecureOption.Never
//#endif
});
// Use a cookie to temporarily store information about a user logging in with a third party login provider
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
OAuthBearerOptions = new OAuthBearerAuthenticationOptions();
// using OAuth authentication server as authentication middle ware and Token Generation
app.UseOAuthAuthorizationServer(new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new SimpleAuthorizationServerProvider(),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
#if DEBUG
AllowInsecureHttp = true
#endif
});
app.UseOAuthBearerAuthentication(OAuthBearerOptions);
code in MVC Login Controller
[AllowAnonymous]
[HttpPost]
public async Task<ActionResult> Login(LoginViewModel model, string redirectUrl = null)
{
if (!ModelState.IsValid) return View(model);
try
{
if (string.IsNullOrWhiteSpace(redirectUrl))
{
redirectUrl = "~/Home";
}
var result = await WebApiService.Instance.AuthenticateAsync<LogInResult>(model.UserName, model.Password);
//Let's keep the user authenticated in the MVC webapp.
//By using the AccessToken, we can use User.Identity.Name in the MVC controllers to make API calls.
FormsAuthentication.SetAuthCookie(result.AccessToken, model.RememberMe);
//Create an AuthenticationTicket to generate a cookie used to authenticate against Web API.
//But before we can do that, we need a ClaimsIdentity that can be authenticated in Web API.
var claims = new[]
{
new Claim(ClaimTypes.Name, model.UserName),
//Name is the default name claim type, and UserName is the one known also in Web API.
new Claim(ClaimTypes.NameIdentifier, model.UserName)
//If you want to use User.Identity.GetUserId in Web API, you need a NameIdentifier claim.
};
//Generate a new ClaimsIdentity, using the DefaultAuthenticationTypes.ApplicationCookie authenticationType.
//This also matches what we've set up in Web API.
var claimsIdentity = new ClaimsIdentity(claims,DefaultAuthenticationTypes.ApplicationCookie);
var authProperties = new AuthenticationProperties
{
ExpiresUtc = result.Expires,
IsPersistent = model.RememberMe,
IssuedUtc = result.Issued,
RedirectUri = redirectUrl
};
var authTicket = new AuthenticationTicket(claimsIdentity, authProperties);
//And now it's time to generate the cookie data. This is using the same code that is being used by the CookieAuthenticationMiddleware class in OWIN.
byte[] userData = DataSerializers.Ticket.Serialize(authTicket);
//Protect this user data and add the extra properties. These need to be the same as in Web API!
//byte[] protectedData = MachineKey.Protect(userData, new[] { "Microsoft.Owin.Security.Cookies.CookieAuthenticationMiddleware", DefaultAuthenticationTypes.ApplicationCookie, "v1" });
//base64-encode this data.
string protectedText = TextEncodings.Base64Url.Encode(userData);
//And now, we have the cookie.
Response.SetCookie(new HttpCookie("YetAnotherTodo.WebApi.Auth")
{
HttpOnly = true,
Expires = result.Expires.UtcDateTime,
Value = protectedText
});
Code in my provider that generates token
AuthenticationTicket ticket;
var cookiesIdentity = GenerateCookiesIdentity(context, user, out ticket);
context.Validated(ticket);
context.Request.Context.Authentication.SignIn(cookiesIdentity);
I was able to login and able to get ticket from token server but on subsequent request or redirect to home page after logging in , I'm still getting 401 error.
This is kinda combinations of these two blogs/tutorials : Blog 1 and Blog 2
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).