I am using itfoxtec-identity-saml2 in my Dotnet 3.1 Project. I am initiating request from server and validating the login till here everything is working fine.
After getting response assertion from server and getting claims transformed and creating a session but still my application is unable to login.
Below are snippets of my code for reference.
AuthController.cs
[Route("AssertionConsumerService")]
public async Task<IActionResult> AssertionConsumerService()
{
try
{
var binding = new Saml2PostBinding();
var saml2AuthnResponse = new Saml2AuthnResponse(config);
binding.ReadSamlResponse(Request.ToGenericHttpRequest(), saml2AuthnResponse);
if (saml2AuthnResponse.Status != Saml2StatusCodes.Success)
{
throw new AuthenticationException($"SAML Response status: {saml2AuthnResponse.Status}");
}
binding.Unbind(Request.ToGenericHttpRequest(), saml2AuthnResponse);
await saml2AuthnResponse.CreateSession(HttpContext, claimsTransform: (claimsPrincipal) => ClaimsTransform.TransformClaims(claimsPrincipal),isPersistent:true, lifetime: new TimeSpan(1, 0, 0));
var auth = HttpContext.User.Identity.IsAuthenticated;
}
catch (Exception ex)
{
}
return Redirect("~/");
}
ClaimsTransform.cs
public static ClaimsPrincipal TransformClaims(ClaimsPrincipal claimsPrincipal)
{
ClaimsIdentity identity = (ClaimsIdentity)claimsPrincipal.Identity;
var tenantId = identity.FindFirst(ClaimTypes.NameIdentifier);
var Name = identity.FindFirst("firstName");
var firstName = identity.FindFirst("firstName");
var Email = identity.FindFirst("Email");
var UserID = identity.FindFirst("UserID");
var claimsToKeep = new List<Claim> { tenantId, Name,firstName, Email, UserID };
var newIdentity = new ClaimsIdentity(claimsToKeep, identity.AuthenticationType, ClaimTypes.NameIdentifier, ClaimTypes.Role);
ClaimsPrincipal newClaims = new ClaimsPrincipal(newIdentity);
return new ClaimsPrincipal(new ClaimsIdentity(claimsToKeep, identity.AuthenticationType, ClaimTypes.Name, ClaimTypes.Role)
{
BootstrapContext = ((ClaimsIdentity)claimsPrincipal.Identity).BootstrapContext
});
//return newClaims;
}
After all this my application is redirecting back to login page instead of home page of the application with logged in user.
Help will be appreciated.
You need to set the users identity claim to a claim which exist in the claim set, otherwise the user is not accepted as being authenticated.
If eg. the tenantId claim is the users identity then the users identity claim is ClaimTypes.NameIdentifier in new ClaimsPrincipal(... ClaimTypes.NameIdentifier, ClaimTypes.Role)
ClaimsTransform.cs
public static ClaimsPrincipal TransformClaims(ClaimsPrincipal claimsPrincipal)
{
ClaimsIdentity identity = (ClaimsIdentity)claimsPrincipal.Identity;
var tenantId = identity.FindFirst(ClaimTypes.NameIdentifier);
var Name = identity.FindFirst("firstName");
var firstName = identity.FindFirst("firstName");
var Email = identity.FindFirst("Email");
var UserID = identity.FindFirst("UserID");
var claimsToKeep = new List<Claim> { tenantId, Name,firstName, Email, UserID };
return new ClaimsPrincipal(new ClaimsIdentity(claimsToKeep, identity.AuthenticationType, ClaimTypes.NameIdentifier, ClaimTypes.Role)
{
BootstrapContext = ((ClaimsIdentity)claimsPrincipal.Identity).BootstrapContext
});
}
Related
I want to know how I can return my generated JWT token to user. I mean I would like this token to be saved e.g. somewhere in the user's browser and sent to my Controller in every query after successfully login.
The token generating functions are already written. Everything works, unfortunately, I do not know how to pass it to the user and then pick it up in other functions / controllers
This is the login controller:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Index(LoginDto dto)
{
if (ModelState.IsValid)
{
string token = _accountService.GenerateJwt(dto); //working
if (token != null)
{
// how can I return token there?
return RedirectToAction("LoginSuccess");
}
else
{
ViewBag.error = "Login failed";
return RedirectToAction("LoginFailed");
}
}
}
This is the function that generates the token:
public string GenerateJwt(LoginDto dto)
{
var user = _context.dnozasg2lp_vip_users.FirstOrDefault(u => u.Email == dto.Email);
if (user is null)
{
throw new BadRequestException("Invalid username or password!");
}
var result = _passwordHasher.VerifyHashedPassword(user, user.Password, dto.Password);
if (result == PasswordVerificationResult.Failed)
{
throw new BadRequestException("Invalid username or password!");
}
var claims = new List<Claim>()
{
new Claim(ClaimTypes.NameIdentifier, user.ID.ToString()),
new Claim(ClaimTypes.Name, $"{user.FirstName} {user.LastName}"),
new Claim(ClaimTypes.Email, $"{user.Email}")
};
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_authenticationSettings.JwtKey));
var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var expiration = DateTime.Now.AddDays(_authenticationSettings.JwtExpireDays);
var token = new JwtSecurityToken(_authenticationSettings.JwtIssuer,
_authenticationSettings.JwtIssuer,
claims,
expires: expiration,
signingCredentials: credentials
);
var tokenHandler = new JwtSecurityTokenHandler();
return tokenHandler.WriteToken(token);
}
And my startup config file:
// JWT Token config below
var authenticationSettings = new AuthenticationSettings();
services.AddSingleton(authenticationSettings);
Configuration.GetSection("Authentication").Bind(authenticationSettings);
services.AddAuthentication(option =>
{
option.DefaultAuthenticateScheme = "Bearer";
option.DefaultScheme = "Bearer";
option.DefaultChallengeScheme = "Bearer";
}).AddJwtBearer(cfg =>{
cfg.RequireHttpsMetadata = false;
cfg.SaveToken = true;
cfg.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
{
ValidIssuer = authenticationSettings.JwtIssuer,
ValidAudience = authenticationSettings.JwtIssuer,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(authenticationSettings.JwtKey)),
};
});
Like I said, the JWT token is successfully generated, but how can I send it to user and then catch it (for example in a "LoginSuccess" action) ?
In a Web API project I am using the following method to sign in my user but at a latter point I want to get my user first name and last name. I am using Web API and .NET 5.
public AuthenticateResponse Authenticate(AuthenticateRequest model, string ipAddress)
{
var user = _userManager.Users
.Where(w => w.UserName == model.Username)
.FirstOrDefault();
// This doesn't count login failures towards account lockout
// To enable password failures to trigger account lockout, set
lockoutOnFailure: true
var result = _signInManager.PasswordSignInAsync(model.Username, model.Password, true, lockoutOnFailure: false);
User users = new User();
users.Username = model.Username;
users.Password = model.Password;
users.FirstName = user.FirstName;
users.LastName = user.LastName;
if (result.Result.Succeeded)
{
// return null if user not found
if (user == null)
return null;
}
// authentication successful so generate jwt and refresh tokens
var jwtToken = generateJwtToken(users);
var refreshToken = generateRefreshToken(ipAddress);
// save refresh token
// users.RefreshTokens.Add(refreshToken);
return new AuthenticateResponse(users, jwtToken, null);
}
I have this method in a UserService class how would one best access the values from
users.FirstName
users.LastName
from the Web API controller which could be say clubs. As you see I am using the signin manager and usermanager should I simply load an instance of that in my ClubController.
My API method is
[HttpPost]
[Route("Clubs/Create")]
public async Task<IActionResult> Create(ClubViewModelApi clubModel)
{
if (ModelState.IsValid)
{
Club _club = new Club();
_club.Name = clubModel.Name;
_club.Description = clubModel.Description;
_club.isActive = clubModel.isActive;
_club.isDeleted = clubModel.isDeleted;
_club.CreatedDate = DateTime.Now;
_club.CreatedBy = insert first lastname here;
_club.CreatedBy = User.Identity.
_context.Clubs.Add(_club);
await _context.SaveChangesAsync();
return Ok();
}
return View(clubModel);
}
I wish to insert the first name and last name here _club.CreatedBy at this point of the above api end point.
I create my token with this code when I authenticate:
private string generateJwtToken(User user)
{
var tokenHandler = new JwtSecurityTokenHandler();
var secret = _configRoot.GetValue<string>("JWTSecret");
_logger.Log(LogLevel.Information, $"JWT Secret from Everleap={secret}");
var key = Encoding.ASCII.GetBytes(secret);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[]
{
new Claim(ClaimTypes.Name, user.Id.ToString())
}),
Expires = DateTime.UtcNow.AddMinutes(15),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
return tokenHandler.WriteToken(token);
}
My details are stored in a jwttoken. Do I go out to that token again and decrypt it on the controller level.
Response body
{
"id": 0,
"firstName": "david",
"lastName": "buckley",
"username": "davidbuckleyweb#outlook.com",
"jwtToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6IjAiLCJuYmYiOjE2MTY0MzAzNzUsImV4cCI6MTYxNjQzMTI3NSwiaWF0IjoxNjE2NDMwMzc1fQ.P8smaC0PAB5uSrBbI8bbHoc2PKbwIj7mI0jLnBuJz4s",
"refreshToken": null
}
I figured out that what I need to do is extend my claims so that its stored in the token it self I use the following to encode the token
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[]
{
new Claim("CreatedBy", user.FirstName.Substring(0,1).ToUpper() + " " + user.LastName.Substring(0,1).ToUpper()),
new Claim(ClaimTypes.Email, user.Username),
new Claim(ClaimTypes.Name, user.FirstName + " " + user.LastName),
new Claim(ClaimTypes.Role,roles)
}),
Expires = DateTime.UtcNow.AddMinutes(15),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
Then to decode it I use the following.
var authorization = Request.Headers[HeaderNames.Authorization];
if (AuthenticationHeaderValue.TryParse(authorization, out var headerValue))
{
// we have a valid AuthenticationHeaderValue that has the following details:
var scheme = headerValue.Scheme;
var JWTToken = headerValue.Parameter;
var token = new JwtSecurityToken(jwtEncodedString: JWTToken);
string name = token.Claims.First(c => c.Type == "CreatedBy").Value;
_club.CreatedBy = name;
}
I'm currently having an issues where a given client that has a valid claim identity specifying that the client is in the given role, not get authorised for actions and controllers that require that role.
I'm using an API to generate the token which the client will use to access both the API and the website, the tokens are generated as JWT.
I have looked on other posts that have this issue, but almost all of the are using identity which isn't required for this project, since we are using JWT.
I am using HttpContext.Signin to sign in the user, passing it the token received from the API which contains the user's roles and other claims.
I have included the code that's dealing with tokens, and the token received from the API -at the very end-. As clearly seen, the user has the role attribute, yet they are will not be authorised by the authentication middleware.
Creating the token
public async Task<string> CreateBearerTokenAsync(User user, string audience)
{
// Create identity for the client
var claims = await _clientManager.CreateUserClaimsIdentityAsync(user.Id);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = claims,
Expires = DateTime.UtcNow.AddHours(2),
IssuedAt = DateTime.UtcNow,
Issuer = "Bikefy Api",
Audience = audience,
SigningCredentials = _encryptionService.TokenSignKey
};
// Create the token
var token = new JwtSecurityTokenHandler().CreateJwtSecurityToken(tokenDescriptor);
// Write the token to string.
return new JwtSecurityTokenHandler().WriteToken(token);
}
Claims identity
public async Task<ClaimsIdentity> CreateUserClaimsIdentityAsync(Guid clientId)
{
if (clientId == null || Guid.Empty == clientId)
throw new ArgumentNullException($"{nameof(clientId)}");
var user = await GetClientByIdAsync(clientId);
var id = new ClaimsIdentity("ApiKey", ClaimsIdentity.DefaultNameClaimType, ClaimsIdentity.DefaultRoleClaimType);
var secKey = await GetSecurityStampAsync(clientId);
// Add default claims
id.AddClaim(new Claim(ClaimTypes.NameIdentifier, clientId.ToString(), ClaimValueTypes.String));
if (user.FirstName != null)
id.AddClaim(new Claim(ClaimTypes.GivenName, user.FirstName, ClaimValueTypes.String));
if (user.LastName != null)
id.AddClaim(new Claim(ClaimTypes.Surname, user.LastName, ClaimValueTypes.String));
id.AddClaim(new Claim(IdentityProviderClaimType, "Bikey Identity", ClaimValueTypes.String));
id.AddClaim(new Claim(SecurityStampClaimType, secKey, ClaimValueTypes.String));
if (user.RoleName != null)
id.AddClaim(new Claim($"{nameof(User.RoleName)}", user.RoleName, ClaimValueTypes.String));
// Get roles
var roles = await GetRolesAsync(clientId);
foreach (var role in roles)
id.AddClaim(new Claim(ClaimTypes.Role, role));
// Add user claims
id.AddClaims(await GetClaimsAsync(clientId));
return id;
}
Cookie token configuration
public static void ConfigureAuthentication(this IServiceCollection services, IConfiguration configuration)
{
var secretKeys = new SecretKeys();
configuration.GetSection("SecretKeys").Bind(secretKeys);
var encryptService = new EncryptionService("00E7EB8C24190E2187", secretKeys);
services.AddSingleton(encryptService);
services.AddSingleton(secretKeys);
services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
x.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
}).AddCookie(c =>
{
c.Cookie.Name = "CityCyles.Auth";
c.Cookie.HttpOnly = true;
c.Cookie.Expiration = TimeSpan.FromDays(1);
c.Cookie.SecurePolicy = Microsoft.AspNetCore.Http.CookieSecurePolicy.Always;
c.LoginPath = $"/Account/Login";
c.LogoutPath = $"/Account/Logout";
c.AccessDeniedPath = $"/Account/AccessDenied";
});
}
User Login
try
{
result = await _apiProvider.SendPostRequest(_apiProvider.BuildUrl("Auth", "Authenticate"), apiModel);
// Convert the string into a token
var token = new JwtSecurityTokenHandler().ReadJwtToken(result);
// Create cookie options
var authOptions = new AuthenticationProperties()
{
AllowRefresh = model.Remember,
ExpiresUtc = DateTime.UtcNow.AddHours(2),
IssuedUtc = DateTime.UtcNow,
IsPersistent = true
};
// Get the user claims from the user
var claimsIdentity = new ClaimsIdentity(token.Claims, CookieAuthenticationDefaults.AuthenticationScheme, ClaimTypes.Name, ClaimTypes.Role);
// Sign in the user
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(claimsIdentity), authOptions);
// Redirect the user to the requested page or take them home
if (!string.IsNullOrEmpty(returnUrl))
return Redirect(returnUrl);
return RedirectToAction("Index", "Home");
}
catch (WebException ex)
{
_logger.LogWarning(LogEvents.HandlingLogin, ex, "Error authenticating user.");
//Display the error
ModelState.AddModelError("Custom-Error", ex.Message);
return View(model);
}
Controller Action
[HttpGet]
[Authorize(Roles = CityCyclesRoles.CityCyclesDocks)]
public async Task<IActionResult> OnBoard()
{
return View();
}
Token
{
"nameid": "3e637f01-85a9-4437-a113-50d2953d014e",
"given_name": "Stephanie",
"family_name": "Lee",
"http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider": "Bikey Identity",
"Bikeyfy.Identity.SecurityStamp": "1b6228cf-945a-4503-a64e-1dcb7b649c22",
"role": "CityCycles.Staff.Docks",
"nbf": 1552586386,
"exp": 1552593586,
"iat": 1552586386,
"iss": "CityCycles Api",
"aud": "CityCycles.Web"
}
I have a custom Authorize class that I use to invalidate the token when user requests data or anything from the server
but whenever the token expires the principle still returns IsAuthenticated as true and still calls the controllers and get data.
What I want it to do is to invalidate the token and explicitly logout the user out of the system. I couldn't find anything helpful. I can provide code of the JWT attribute/filters if needed
Update 1: Token Generation
public static string GenerateToken(User user)
{
int expireMinutes;
try
{
expireMinutes = string.IsNullOrEmpty(ConfigurationManager.AppSettings["SessionTimeInMinutes"])
? 30
: int.Parse(ConfigurationManager.AppSettings["SessionTimeInMinutes"]);
}
catch (Exception)
{
expireMinutes = 30;
}
var symmetricKey = Convert.FromBase64String(Secret);
var tokenHandler = new JwtSecurityTokenHandler();
var now = DateTime.Now;
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new[]
{
new Claim(ClaimTypes.Email, user.Email)
,new Claim(ClaimTypes.Name, user.FirstName + " " + user.LastName)
,new Claim("uid", user.Id.ToString())
,new Claim("cid", user.ClientId.ToString())
,new Claim("rid", string.Join(",", user.Roles.Select(r => r.RoleId).ToList()))
}),
Expires = now.AddMinutes(Convert.ToInt32(expireMinutes)),
IssuedAt = now,
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(symmetricKey), SecurityAlgorithms.HmacSha256Signature)
};
var stoken = tokenHandler.CreateToken(tokenDescriptor);
var token = tokenHandler.WriteToken(stoken);
return token;
}
Server side Authorization Token
public async Task AuthenticateAsync(
HttpAuthenticationContext context, CancellationToken cancellationToken)
{
var excludedList = new List<string>();
excludedList.Add("/api/Security/IsMustChangePassword");
excludedList.Add("/api/Security/IsTwoFactorEnabled");
if (!excludedList.Contains(context.ActionContext.Request.RequestUri.LocalPath))
{
var request = context.Request;
var authorization = request.Headers.Authorization;
if (authorization == null || authorization.Scheme != "Token")
{
return;
}
if (string.IsNullOrEmpty(authorization.Parameter))
{
context.ErrorResult = new AuthenticationFailureResult("Missing Jwt Token", request);
return;
}
//{
// context.ErrorResult = new AuthenticationFailureResult("Invalid token", request);
// return;
//}
var token = authorization.Parameter;
var principal = await AuthenticateJwtToken(token).ConfigureAwait(true);
var userId = int.Parse(new JwtManager().GetUserIdFromToken(token));
var accountManager = new AccountManager();
var user = accountManager.GetUserDetails(userId);
var newToken = JwtManager.GenerateToken(user);
if (principal == null)
context.ErrorResult = new AuthenticationFailureResult("Invalid token", request);
else
context.Principal = principal;
if (principal.Identity.IsAuthenticated)
{
var expiryDate = JwtManager.GetSecurityToken(token).ValidTo.ToLocalTime();
if ((DateTime.Now - expiryDate).TotalSeconds > 0)
{
context.Request.Headers.Authorization = null;
context.Request.RequestUri = null;
}
else
{
var authorize = new AuthenticationHeaderValue("token", newToken);
context.Request.Headers.Authorization = authorize;
context.ActionContext.Request.Headers.Authorization = authorization;
}
}
}
}
private static bool ValidateToken(string token, out string username, out
string passwordHash)
{
username = null;
passwordHash = null;
try
{
var principle = JwtManager.GetPrincipal(token);
var identity = principle.Identity as ClaimsIdentity;
if (identity == null)
return false;
if (!identity.IsAuthenticated)
return false;
var usernameClaim = identity.FindFirst(ClaimTypes.Name);
var passwordClaim = identity.FindFirst(ClaimTypes.Hash);
username = usernameClaim?.Value;
passwordHash = passwordClaim?.Value;
return !string.IsNullOrEmpty(username);
var user = identity.FindFirst(username);
return (user != null);
//return (user != null && user.PasswordHash == passwordHash);
}
catch (NullReferenceException)
{
return false;
}
}
protected Task<IPrincipal> AuthenticateJwtToken(string token)
{
string username;
string passwordHash;
if (!ValidateToken(token, out username, out passwordHash))
return Task.FromResult<IPrincipal>(null);
// based on username to get more information from database in order to build local identity
var claims = new List<Claim> { new Claim(ClaimTypes.Name, username) };
//claims.Add(new Claim(ClaimTypes.Hash, passwordHash));
// Add more claims if needed: Roles, ...
var identity = new ClaimsIdentity(claims, "Jwt");
IPrincipal user = new ClaimsPrincipal(identity);
return Task.FromResult(user);
}
public Task ChallengeAsync(HttpAuthenticationChallengeContext context,
CancellationToken cancellationToken)
{
var authorization = context.Request.Headers.Authorization;
var excludedList =
new List<string> {
"/api/Security/IsMustChangePassword",
"/api/Security/IsTwoFactorEnabled" };
if (context.Request.Headers.Authorization != null)
{
if (!excludedList.Contains(context.ActionContext.Request.RequestUri.LocalPath))
{
var token = context.Request.Headers.Authorization.Parameter;
var userId = int.Parse(new JwtManager().GetUserIdFromToken(token));
var accountManager = new AccountManager();
var user = accountManager.GetUserDetails(userId);
var newToken = JwtManager.GenerateToken(user);
var expiryDate = JwtManager.GetSecurityToken(token).ValidTo.ToLocalTime();
if ((DateTime.Now - expiryDate).TotalSeconds > 0)
{
context.Request.Headers.Authorization = null;
context.Request.RequestUri = null;
context.Request.RequestUri = new Uri("/Login");
}
else
{
var authorize = new AuthenticationHeaderValue("token", newToken);
context.Request.Headers.Authorization = authorize;
context.ActionContext.Request.Headers.Authorization = authorization;
}
Challenge(context);
}
}
else
{
var req = context.Request.RequestUri;
var url = context.Request.RequestUri = new Uri($"http://{req.Host}:{req.Port}/api/Security/login");
context.Request.RequestUri = url;
context.Request.Headers.Authorization = null;
context.Result= new AuthenticationFailureResult(string.Empty, new HttpRequestMessage());
}
return Task.FromResult(0);
}
First of all: A JWT is just a client-token the client can manipulate. There is no absolute security here. The JWT is secured by the symmetirc key but cannot be invalidated itself. A valid token remains valid until it expires. It's a flaw of JWT (as #TimBiegeleisen pointed out in the comments) that a token itself cannot be easily invalidated.
If a user works too long and gets logged out automatically, your JWT has expired and everthing's good. There's no hassle as it runs out naturally and you have no need to act.
To invalidate a token you need to create a new token with Expires = now.AddMinutes(-1). That way the next time you check the JWT next time you see it has expired.
The case that a user may save a JWT and use it even after being logged out can only be come by with blacklisting JWTs or maintaining some other kind of serverside session aditionally (which doesn't work i.e. with a stateless webservice).
EDIT: Removed wrong information and added a additional way to invalidate a JWT(blacklisting)
You can simply cash a the token you want to revoke
and then make your authentication part compare request that has the revoked
with the one in the cash and refuse the request based on that
until the token expires , the cash should know the remining time for the token and cash it for that long
In our project we have a user management system build on the ASP.NET Identity framework.
When a user registers by providing an email, username and password, everything works fine. We are able to get the users ID in the method in every controller that inherit "ApiController".
However, now we are trying to implement external log in providers, and we are starting off with Facebook. The registration is going smooth, and the user is created in our database, just any other user, but without a PasswordHash of cause, and an access token is retured back to the client for further authorization.
All of that is working as it should, but when it comes to the part, where the programmer should be able to receive the users id with "User.Identity.GetUserId", we are having a little problem. The "User.Identity" is containing the right "userName" but the "GetUserId" is always returning "null".
The following is our registration method, and the generation of the access token
[OverrideAuthentication]
[AllowAnonymous]
[HostAuthentication(DefaultAuthenticationTypes.ExternalBearer)]
[Route("RegisterExternal")]
public async Task<IHttpActionResult> RegisterExternal(RegisterExternalBindingModel model)
{
try
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var verifiedAccessToken = await VerifyExternalAccessToken(model.Provider, model.AccessToken);
if (verifiedAccessToken == null)
{
return BadRequest("Invalid Provider or External Access Token");
}
var user = new ApplicationUser() { UserName = model.Email, Email = model.Email };
IdentityResult result = await UserManager.CreateAsync(user);
if (!result.Succeeded)
{
return GetErrorResult(result);
}
var info = new ExternalLoginInfo()
{
DefaultUserName = model.UserName,
Login = new UserLoginInfo(model.Provider, verifiedAccessToken.user_id)
};
var accessTokenResponse = GenerateLocalAccessTokenResponse(model.UserName, user.Id, model.Provider);
return Ok(accessTokenResponse);
}
catch (Exception e)
{
return null;
}
}
private JObject GenerateLocalAccessTokenResponse(string userName, string userid, string provider)
{
var tokenExpiration = TimeSpan.FromDays(1);
ClaimsIdentity identity = new ClaimsIdentity(OAuthDefaults.AuthenticationType);
identity.AddClaim(new Claim(ClaimTypes.Sid, userid));
identity.AddClaim(new Claim(ClaimTypes.Name, userName));
identity.AddClaim(new Claim("role", "user"));
var props = new AuthenticationProperties()
{
IssuedUtc = DateTime.UtcNow,
ExpiresUtc = DateTime.UtcNow.Add(tokenExpiration),
};
var ticket = new AuthenticationTicket(identity, props);
var accessToken = Startup.OAuthBearerOptions.AccessTokenFormat.Protect(ticket);
JObject tokenResponse = new JObject(
new JProperty("userName", userName),
new JProperty("access_token", accessToken),
new JProperty("token_type", "bearer"),
new JProperty("external_provider", provider),
new JProperty("expires_in", tokenExpiration.TotalSeconds),
new JProperty(".issued", ticket.Properties.IssuedUtc.ToString()),
new JProperty(".expires", ticket.Properties.ExpiresUtc.ToString())
);
return tokenResponse;
}
So all in all, every part of the registration is working as it should, we are just not able to receive the user id of the current user, when it uses an access token for a user created by an external provider.
Well, in case someone in the future needs the answer, it was because the "GetUserId" looked for the claim called "NameIdentifier", so changeing it, made it work.