User.Identity.Name is always null when using AspNetIdentity with IdentityServer3 - asp.net

I've been trying to setup a new IdentityServer3 with AspNetIdentity for a few days now. I'm able to login using my existing Identity DB and that's all good but I can never get the User.Identity.Name to contain data.
I've tried multiple attempts at adding custom claims & scopes and adding scopes to clients.
Finally, I loaded up the IdentityServer3 Sample repository and tested it out with the webforms client project since it already used the User.Identity.Name in it's About page.
Using WebForms sample client + AspNetIdentity sample server = User.Identity.Name is always null
Using WebForms sample client + SelfHost with Seq sample server = User.Identity.Name with data
I've tried other sample host projects that all populate the User.Identity.Name value just fine.
Now, on the client side I've written a workaround to pull the 'preferred_username' claim value and set the 'name' claim with it.
var id = new claimsIdentity(n.AuthenticationTicket.Identity.AuthenticationType);
id.AddClaims(userInfoResponse.GetClaimsIdentity().Claims);
//set the User.Identity.Name value
var name = id.Claims.Where(x => x.Type == "name").Select(x => x.Value).FirstOrDefault() ??
id.Claims.Where(x => x.Type == "preferred_username").Select(x => x.Value).FirstOrDefault();
id.AddClaim(new Claim("name", name));
My questions are:
Why doesn't the AspNetIdentity package fill this by default?
And what do I need to change on the server side so that I don't need to change the client?

public static IEnumerable<ApiResource> GetApis()
{
return new ApiResource[]
{
new ApiResource("MyApi", "My Admin API")
{
UserClaims = { JwtClaimTypes.Name, JwtClaimTypes.Email }
}
};
}
In Identityserver4 you can add the UserClaims to your resource. Fixed it for me.

On IdentityServer4 you can implement IProfileService on server and add the Claim in GetProfileDataAsync
public class AspNetIdentityProfileService : IProfileService
{
protected UserManager<ApplicationUser> _userManager;
public AspNetIdentityProfileService(UserManager<ApplicationUser> userManager)
{
_userManager = userManager;
}
public Task GetProfileDataAsync(ProfileDataRequestContext context)
{
//Processing
var user = _userManager.GetUserAsync(context.Subject).Result;
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, user.UserName),
};
context.IssuedClaims.AddRange(claims);
//Return
return Task.FromResult(0);
}
public Task IsActiveAsync(IsActiveContext context)
{
//Processing
var user = _userManager.GetUserAsync(context.Subject).Result;
context.IsActive = (user != null) && ((!user.LockoutEnd.HasValue) || (user.LockoutEnd.Value <= DateTime.Now));
//Return
return Task.FromResult(0);
}
}
Then add "AddProfileService()" to your ConfigureServices method.
services.AddIdentityServer(...)
...
.AddProfileService<AspNetIdentityProfileService>();

Related

ASP.NET Core Jwt implement signinmanager claims

I have implemented Jwt as a way to authenticate my user. However, I am stuck on how I can do certain things on my application with regards to roles. Currently my Jwt Token contains the users email, phone , id and a list of roles that they have.
What I do with that token is like this:
[TypeFilter(typeof(ValidateRolesFilter), Arguments = new object[] {
ApplicationGlobals.ApplicationSecretKey, RoleGlobals.SystemAdministrator
})]
public IActionResult Index()
{
return View();
}
My Typefilter contains a rest request that sends the token to another application to verify if my user can access that Function. However,
I am stuck when it comes to the view. I want to segment certain containers to be allowed to be viewed by certain users with certain roles.
I have an idea that if I were to add my users claims to the signinmanager just like a non jwt application, i would be able to get the claims from the httpcontext. However, I don't know if what I have can work with an application that uses jwt.
public async Task SignInUserAsync(TIdentityUser user, bool isPersistent, IEnumerable<Claim> customClaims)
{
var claimsPrincipal = await _signInManager.CreateUserPrincipalAsync(user);
var identity = claimsPrincipal.Identity as ClaimsIdentity;
var claims = (from c in claimsPrincipal.Claims select c).ToList();
var savedClaims = claims;
foreach (var item in claims)
{
identity.RemoveClaim(item);
}
if (customClaims != null)
{
identity.AddClaim(savedClaims[0]);
identity.AddClaim(savedClaims[1]);
identity.AddClaim(savedClaims[2]);
identity.AddClaims(customClaims);
}
await _signInManager.Context.SignInAsync(IdentityConstants.ApplicationScheme,
claimsPrincipal,
new AuthenticationProperties { IsPersistent = isPersistent });
}
I am recently doing a cooperative project on JWT. I wrote a middlware, when ever the user request to the api, It is checked by the Authentication middleware. I read the userRole from db and put it in the identity priciple I am sharing the middleware codes.
In here I read the JWT middle part to extract the user information
public class AuthenticationMiddleware
{
private readonly RequestDelegate _next;
// Dependency Injection
public AuthenticationMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
string authHeader = context.Request.Headers["Authorization"];
if (authHeader != null)
{
int startPoint = authHeader.IndexOf(".") + 1;
int endPoint = authHeader.LastIndexOf(".");
var tokenString = authHeader.Substring(startPoint, endPoint - startPoint).Split(".");
var token = tokenString[0].ToString()+"==";
var credentialString = Encoding.UTF8
.GetString(Convert.FromBase64String(token));
// Splitting the data from Jwt
var credentials = credentialString.Split(new char[] { ':',',' });
// Trim this string.
var userRule = credentials[5].Replace("\"", "");
var userName = credentials[3].Replace("\"", "");
// Identity Principal
var claims = new[]
{
new Claim("name", userName),
new Claim(ClaimTypes.Role, userRule),
};
var identity = new ClaimsIdentity(claims, "basic");
context.User = new ClaimsPrincipal(identity);
}
await _next(context);
}
}
In startup.cs you need to call this middleware in the configure method
app.UseMiddleware<AuthenticationMiddleware>();
In the controller
[HttpGet("GetUsers")]
[Authorize(Roles = "admin")]
public ActionResult GetUsers()
{
var users = _authRepository.GetUsers();
return Ok(users);
}
if You need any help please give a comment. This implementation really worked for me. Check my repositories on the subject: https://github.com/hidayatarg/Asp.net-Core-2.1-Jwt-Authentication-Middleware
https://github.com/hidayatarg/Decode-JWT-Token
JSON Web Tokens consist of three parts separated by dots (.), which are: Header,Payload,Signature .Therefore, a JWT typically looks like xxxxx.yyyyy.zzzzz .The second part of the token is the payload, which contains the claims.
You can decode the access token to get the claims which related to your roles :
How to decode JWT Token? .
Decoding and verifying JWT token using System.IdentityModel.Tokens.Jwt
If you are using Owin OpenID Connect middlerware to autheticate user from identity provider like Azure AD , Idenity server 4.... You can add additional claims to principal under OnTokenValidated event .
Edit :
You can also add the claims(decode and get the claims) to user context before sign- in :
var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme, ClaimTypes.Name, ClaimTypes.Role);
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, loginData.Username));
identity.AddClaim(new Claim(ClaimTypes.Name, loginData.Username));
//add your custom claims
....
var principal = new ClaimsPrincipal(identity);
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal, new AuthenticationProperties { IsPersistent = loginData.RememberMe });
Reference : http://future-shock.net/blog/post/creating-a-simple-login-in-asp.net-core-2-using-authentication-and-authorization-not-identity
Then you can access the claims in view like :
#foreach (var item in Context.User.Claims)
{
<p>#item.Value</p>
};

User.Identity.Name is empty in Asp.NET Core 2.0 API Controller

I am new to ASP.NET core itself. However, I am creating WebAPIs in ASP.NET Core 2.0. I have configured JWT Bearer Token based authentication. Below is my Controller which return token.
[AllowAnonymous]
[Route("api/[controller]")]
public class TokenController : Controller
{
private readonly UserManager<UserEntity> userManager;
private readonly SignInManager<UserEntity> signInManager;
public TokenController(UserManager<UserEntity> userManager, SignInManager<UserEntity> signInManager)
{
this.userManager = userManager;
this.signInManager = signInManager;
}
// GET: api/values
[HttpGet]
public async Task<IActionResult> Get(string username, string password, string grant_type)
{
{
var user = await userManager.FindByEmailAsync(username);
if (user != null)
{
var result =await signInManager.CheckPasswordSignInAsync(user, password, false);
if (result.Succeeded)
{
var claims = new[]
{
new Claim( JwtRegisteredClaimNames.Sub, username),
new Claim( JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim( JwtRegisteredClaimNames.GivenName, "SomeUserID")
};
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("secretesecretesecretesecretesecretesecrete"));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken( issuer: "test",
audience: "test",
claims: claims,
expires: DateTime.Now.AddDays(15),
signingCredentials: creds);
return Ok(new { access_token = new JwtSecurityTokenHandler().WriteToken(token), expires_on=DateTime.Now.AddDays(15) });
}
}
}
return BadRequest("Could not create token");
}
}
But when calling ValuesController API which is decorated with [Authorize] attributes. I am getting User.Identity.Name is empty. I am not getting any information about user. I am not sure, My token controller is correctly written. As long as it is protecting my ValuesController, I assume, it is correct. However, I might be missing something. Please help.
Note: I am developing using Visual Studio 2017 with Mac Community
addition
Yes, you need to specify the claim for the unique name which is translated into the user.identity.name:
new Claim(JwtRegisteredClaimNames.UniqueName, user.UserName)
I've also been having this problem with ASP.Net Core 2, and I'm really surprised no one's discovered the other cause of this problem.
When my webapp is deployed to IIS, "User.Identity.Name" always returns null. The IIS site has anonymous access disabled, and windows authentication is enabled.
BUT.
I didn't realise that my ASP.Net Core 2 has a "launchSettings.json" file, quietly hidden under the Properties folder, and in there, there's also some iisSettings, and in here "windowsAuthentication" was, strangely, set as false by default.
Changing "windowsAuthentication" to true, and "anonymousAuthentication" to false solved the problem for me.
After doing this, "User.Identity.Name" did finally contain the correct username.
But what the heck is this setting ? Why would this get priority over the actual settings we've setup in IIS Manager ?!
Had this problem too (Core 3.1) using the "DefaultIdentity" (Individual User Accounts).
User.Identity.Name is null, User.Identity.IsAuthenticated = true.
By using httpContextAccessor you can get the userId an with that id you can find the user and the UserName.
In your controller add
using System.Security.Claims;
...
private readonly IHttpContextAccessor _httpContextAccessor;
public MyController(MyContext context, IHttpContextAccessor httpContextAccessor)
{
_context = context;
_httpContextAccessor = httpContextAccessor;
}
// Any method username needed
[HttpGet("{id}")]
public async Task<ActionResult<MyInfo>> GetMyInfo(int id)
{
var userId = _httpContextAccessor.HttpContext.User.FindFirst(ClaimTypes.NameIdentifier).Value;
var user = _context.AspNetUsers.Find(userId);
var userName = user.UserName;
...
}
In the Startup.cs add the following line:
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
For Azure OAuth v2, use preferred_username instead of unique_name (see this and this).
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.Extensions.DependencyInjection;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
serviceCollection.Configure<OpenIdConnectOptions>(OpenIdConnectDefaults.AuthenticationScheme, options =>
{
options.TokenValidationParameters.RoleClaimType = "roles";
options.TokenValidationParameters.NameClaimType = "preferred_username";
//options.TokenValidationParameters.NameClaimType = "email"; // or if you want to use user's email for User.Identity.Name
//below lines of code can be removed. just there if you want some code to be executed right after user is validated.
options.Events.OnTokenValidated = async context =>
{
var personFirstName = context.Principal.FindFirstValue("given_name") ?? string.Empty;
var personLastName = context.Principal.FindFirstValue("family_name") ?? string.Empty;
var personEmail = context.Principal.FindFirstValue("email")?.ToLower();
var personName = context.Principal.Identity.Name;
};
});
Then in your controllers, you will get username from User.Identity.Name

Use two way to create bearer token for users in web api 2 with owin context

I have a Asp.net web api 2 project. In this project I use OWIN authentication.
I have two kinds of users.
One type of are those who logs in with user name and password, another type are those who logs in with mobile number and a four character word.
I want both of these users go the address /token to get their token, my implementation so far is like this :
This is start up class :
var provider = new AuthorizationServerProvider();
var options = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = provider
};
public class AuthorizationServerProvider : OAuthAuthorizationServerProvider
{
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
using (DbContext dbContext = new DbContext ())
{
var user = dbContext.User
.Where(a => a.UserName == context.UserName)
.Where(a => a.Password == context.Password)
.Select(a => new UserClaim
{
Id = a.Id,
UserName = a.UserName,
FirstName = a.FirstName,
LastName = a.LastName,
Roles = a.UserInRoles.Select(w => w.Role.Id).ToList()
}).FirstOrDefault();
if (user == null)
{
context.SetError("invalid grant", "Provided username and password is incorrect.");
return;
}
identity.AddUserClaim(user);
context.Validated(identity);
return;
}
}
}
This solution is for users who want to log in with user name , but what about those users who want to log in with mobile number, what should I do ?
You need to provide two instance of OAuthAuthorizationServerOptions one for authorizing with username and password and one for mobileNumber and code, and then add this two options via authorization middleware to your owin pipeline.
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
// rest of your code
var userAndPasswordOptions = new OAuthAuthorizationServerOptions(){ ... };
var mobileAndCodeOptions = new OAuthAuthorizationServerOptions(){ ... };
app.UseOAuthAuthorizationServer(userAndPasswordOptions);
app.UseOAuthAuthorizationServer(mobileAndCodeOptions);
// rest of your code
}
}
but you should know in this case these two providers answers to different request Endpoint.
If you need to have one endpoint to provide both type of authorization you can change your GrantResourceOwnerCredentials method in OAuthAuthorizationServerProvider.
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
var form = await context.Request.ReadFormAsync().Result;
if (form["type"] == "mobile")
{
//validate mobileNumber and code
}
else
{
//validate username and password
}
identity.AddUserClaim(user);
context.Validated(identity);
return;
}

How do I access Microsoft.Owin.Security.xyz OnAuthenticated context AddClaims values?

I'm trying to retrieve user properties that are returned as the OnAuthenticated context and added as a claims following this example: How to access Facebook private information by using ASP.NET Identity (OWIN)?
I can see that data I am expecting is being returned at login and is being added as a Claim within Starup.Auth.cs. But, when I am within the Account Controller, the only claims that appears within the UserManager or UserStore is issued by LOCAL AUTHORITY. No claims can be found for Facebook (or other external providers). Where do the claims added to context end up? (I'm using VS2013 RTM.)
Full source and live site on Azure linked here: https://github.com/johndpalm/IdentityUserPropertiesSample/tree/VS2013rtm
Here is what I have in Startup.Auth.cs:
var facebookOptions = new Microsoft.Owin.Security.Facebook.FacebookAuthenticationOptions()
{
AppId = ConfigurationManager.AppSettings.Get("FacebookAppId"),
AppSecret = ConfigurationManager.AppSettings.Get("FacebookAppSecret"),
Provider = new Microsoft.Owin.Security.Facebook.FacebookAuthenticationProvider()
{
OnAuthenticated = (context) =>
{
const string XmlSchemaString = "http://www.w3.org/2001/XMLSchema#string";
foreach (var x in context.User)
{
var claimType = string.Format("urn:facebook:{0}", x.Key);
string claimValue = x.Value.ToString();
if (!context.Identity.HasClaim(claimType, claimValue))
context.Identity.AddClaim(new System.Security.Claims.Claim(claimType, claimValue, XmlSchemaString, "Facebook"));
}
context.Identity.AddClaim(new System.Security.Claims.Claim("urn:facebook:access_token", context.AccessToken, XmlSchemaString, "Facebook"));
return Task.FromResult(0);
}
}
};
facebookOptions.Scope.Add("email");
app.UseFacebookAuthentication(facebookOptions);
An alternative way to capture the external login properties would be to add a single claim for the access token and populate it with properties:
const string XmlSchemaString = "http://www.w3.org/2001/XMLSchema#string";
var facebookOptions = new Microsoft.Owin.Security.Facebook.FacebookAuthenticationOptions
{
AppId = ConfigurationManager.AppSettings.Get("FacebookAppId"),
AppSecret = ConfigurationManager.AppSettings.Get("FacebookAppSecret"),
Provider = new Microsoft.Owin.Security.Facebook.FacebookAuthenticationProvider()
{
OnAuthenticated = (context) =>
{
var claim = new System.Security.Claims.Claim("urn:facebook:access_token", context.AccessToken, XmlSchemaString, "Facebook");
foreach (var x in context.User)
{
string key = string.Format("urn:facebook:{0}", x.Key);
string value = x.Value.ToString();
claim.Properties.Add(key, value);
}
context.Identity.AddClaim(claim);
return Task.FromResult(0);
}
}
};
NOTE - This sample does not work: Though it would be nice to pass a single claim with properties. The external cookie seems to note honor the claims properties. The properties are empty when retrieving them later from the identity.
I was able to create a working example, using MVC 5 RTM templates, OWIN, and ASP.NET Identity bits. You can find the complete source and a link to a live working example here: https://github.com/johndpalm/IdentityUserPropertiesSample
Here's what worked for me:
Create a new (insert provider name here) AuthenticationOptions object in Startup.ConfigureAuth (StartupAuth.cs), passing it the client id, client secret, and a new AuthenticationProvider. You will use a lambda expression to pass the OnAuthenticated method some code to add Claims to the identity which contain the values you extract from context.Identity.
StartUp.Auth.cs
// Facebook : Create New App
// https://dev.twitter.com/apps
if (ConfigurationManager.AppSettings.Get("FacebookAppId").Length > 0)
{
var facebookOptions = new Microsoft.Owin.Security.Facebook.FacebookAuthenticationOptions()
{
AppId = ConfigurationManager.AppSettings.Get("FacebookAppId"),
AppSecret = ConfigurationManager.AppSettings.Get("FacebookAppSecret"),
Provider = new Microsoft.Owin.Security.Facebook.FacebookAuthenticationProvider()
{
OnAuthenticated = (context) =>
{
context.Identity.AddClaim(new System.Security.Claims.Claim("urn:facebook:access_token", context.AccessToken, XmlSchemaString, "Facebook"));
foreach (var x in context.User)
{
var claimType = string.Format("urn:facebook:{0}", x.Key);
string claimValue = x.Value.ToString();
if (!context.Identity.HasClaim(claimType, claimValue))
context.Identity.AddClaim(new System.Security.Claims.Claim(claimType, claimValue, XmlSchemaString, "Facebook"));
}
return Task.FromResult(0);
}
}
};
app.UseFacebookAuthentication(facebookOptions);
}
NOTE: The Facebook auth provider works with the code used here. If you use this same code with the Microsoft Account provider (or Foursquare provider I created using the MS account code as a model), it fails to login. If you select just the access_token parameter, it works fine. Seems like some parameters break the login process. (An issue has been opened on katanaproject.codeplex.com if progress on this is of interest to you.) I'll update if I find the cause. I didn't do much with Twitter or Google beyond verifying that I could get the access_token.
var msaccountOptions = new Microsoft.Owin.Security.MicrosoftAccount.MicrosoftAccountAuthenticationOptions()
{
ClientId = ConfigurationManager.AppSettings.Get("MicrosoftClientId"),
ClientSecret = ConfigurationManager.AppSettings.Get("MicrosoftClientSecret"),
Provider = new Microsoft.Owin.Security.MicrosoftAccount.MicrosoftAccountAuthenticationProvider()
{
OnAuthenticated = (context) =>
{
context.Identity.AddClaim(new System.Security.Claims.Claim("urn:microsoftaccount:access_token", context.AccessToken, XmlSchemaString, "Microsoft"));
return Task.FromResult(0);
}
}
};
app.UseMicrosoftAccountAuthentication(msaccountOptions);
In AccountController, I extract the ClaimsIdentity from the AuthenticationManager using the external cookie. I then add it to the identity created using the application cookie. I ignored any claims that starts with "...schemas.xmlsoap.org/ws/2005/05/identity/claims" since it seemed to break the login.
AccountController.cs
private async Task SignInAsync(CustomUser user, bool isPersistent)
{
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
// Extracted the part that has been changed in SignInAsync for clarity.
await SetExternalProperties(identity);
AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);
}
private async Task SetExternalProperties(ClaimsIdentity identity)
{
// get external claims captured in Startup.ConfigureAuth
ClaimsIdentity ext = await AuthenticationManager.GetExternalIdentityAsync(DefaultAuthenticationTypes.ExternalCookie);
if (ext != null)
{
var ignoreClaim = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims";
// add external claims to identity
foreach (var c in ext.Claims)
{
if (!c.Type.StartsWith(ignoreClaim))
if (!identity.HasClaim(c.Type, c.Value))
identity.AddClaim(c);
}
}
}
And finally, I want to display whatever values are not from the LOCAL AUTHORITY. I created a partial view _ExternalUserPropertiesListPartial that appears on the /Account/Manage page. I get the claims I previously stored from AuthenticationManager.User.Claims and then pass it to the view.
AccountController.cs
[ChildActionOnly]
public ActionResult ExternalUserPropertiesList()
{
var extList = GetExternalProperties();
return (ActionResult)PartialView("_ExternalUserPropertiesListPartial", extList);
}
private List<ExtPropertyViewModel> GetExternalProperties()
{
var claimlist = from claims in AuthenticationManager.User.Claims
where claims.Issuer != "LOCAL AUTHORITY"
select new ExtPropertyViewModel
{
Issuer = claims.Issuer,
Type = claims.Type,
Value = claims.Value
};
return claimlist.ToList<ExtPropertyViewModel>();
}
And just to be thorough, the view:
_ExternalUserPropertiesListPartial.cshtml
#model IEnumerable<MySample.Models.ExtPropertyViewModel>
#if (Model != null)
{
<legend>External User Properties</legend>
<table class="table">
<tbody>
#foreach (var claim in Model)
{
<tr>
<td>#claim.Issuer</td>
<td>#claim.Type</td>
<td>#claim.Value</td>
</tr>
}
</tbody>
</table>
}
Again, the working example and complete code is on GitHub: https://github.com/johndpalm/IdentityUserPropertiesSample
And any feedback, corrections, or improvements would be appreciated.
So this article explains how this all works pretty well: Decoupling owin external auth
But the short answer is, when you get authenticated from facebook, that is giving you an external identity. You then need to take that external identity and 'sign in' a local app identity, its in that stepthat you need to add any claims you want from the external identity to the ClaimsIdentity that becomes User.Identity.
Edit: To clarify further, you could do it inside of ExternalLoginCallback:
// GET: /Account/ExternalLoginCallback
[AllowAnonymous]
public async Task<ActionResult> ExternalLoginCallback(string returnUrl) {
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
if (loginInfo == null) {
return RedirectToAction("Login");
}
// Sign in this external identity if its already linked
var user = await UserManager.FindAsync(loginInfo.Login);
if (user != null) {
await SignInAsync(user, isPersistent: false);
return RedirectToLocal(returnUrl);
}
private async Task SignInAsync(ApplicationUser user, bool isPersistent) {
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);
}
So you will need to pass in extra data to the SignIn, which will look something like this:
ClaimsIdentity id = await AuthenticationManager.GetExternalIdentityAsync(DefaultAuthenticationTypes.ExternalCookie);
This ClaimsIdentity will have your added claim, and you will need to add that claim to the identity created in the SignInAsync method for it to show up.
In short the line that is required once AddClaim is used is as follows:
Taken from johns answer above.
ClaimsIdentity ext = await AuthenticationManager.GetExternalIdentityAsync(DefaultAuthenticationTypes.ExternalCookie);

Use asp.net authentication with servicestack

I have written a couple of ms lightswitch applications with forms authentication -> this creates aspnet_* tables in sql server.
How can I use the defined users, passwords, maybe even memberships, roles and application rights in a servicestack - application?
I have not tested this but I think it should get you started. Gladly stand corrected on any of my steps.
Things I think you will need to do..
In order to Authenticate against both 'systems' you'll need to set the Forms cookie and save your ServiceStack session.
Instead of calling FormsAuthentication.Authentiate() do something like below. This won't work until you complete all the steps.
var apiAuthService = AppHostBase.Resolve<AuthService>();
apiAuthService.RequestContext = System.Web.HttpContext.Current.ToRequestContext();
var apiResponse = apiAuthService.Authenticate(new Auth
{
UserName = model.UserName,
Password = model.Password,
RememberMe = false
});
Create a subclass of IUserAuthRepository (for retrieving membership/user/roles from aspnet_* tables and filling ServiceStack AuthUser).
CustomAuthRepository.cs (incomplete, but should get you started)
public class CustomAuthRepository : IUserAuthRepository
{
private readonly MembershipProvider _membershipProvider;
private readonly RoleProvider _roleProvider;
public CustomAuthRepository()
{
_membershipProvider = Membership.Provider;
_roleProvider = Roles.Provider;
}
public UserAuth GetUserAuthByUserName(string userNameOrEmail)
{
var user = _membershipProvider.GetUser(userNameOrEmail, true);
return new UserAuth {FirstName = user.UserName, Roles = _roleProvider.GetRolesForUser(userNameOrEmail).ToList() //FILL IN REST OF PROPERTIES};
}
public bool TryAuthenticate(string userName, string password, out UserAuth userAuth)
{
//userId = null;
userAuth = GetUserAuthByUserName(userName);
if (userAuth == null) return false;
if (FormsAuthentication.Authenticate(userName, password))
{
FormsAuthentication.SetAuthCookie(userName, false);
return true;
}
userAuth = null;
return false;
}
//MORE METHODS TO IMPLEMENT...
}
Wire Authentication up for ServiceStack in AppHost configure method.
var userRep = new CustomAuthRepository();
container.Register<IUserAuthRepository>(userRep);
Plugins.Add(
new AuthFeature(() => new AuthUserSession(),
new IAuthProvider[] {
new CredentialsAuthProvider()
}
));

Resources