The login operation is performed correctly. But when another user logs in, the user account of the previous user changes to the last logged in user by refreshing the page. (.net 6)
var calims = new List<Claim>
{
new Claim(ClaimTypes.Name,mobile)
};
var identity = new ClaimsIdentity(calims,
CookieAuthenticationDefaults.AuthenticationScheme);
var pricival = new ClaimsPrincipal(identity);
var authProperties = new AuthenticationProperties
{
IssuedUtc = DateTimeOffset.UtcNow,
ExpiresUtc = DateTimeOffset.UtcNow.AddHours(1),
IsPersistent = false,
};
HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, pricival,
authProperties).Wait();
Related
I have a set of API's that I want to put some authentication on. I have added the authorization and authentication pieces to the project. I have added the context for the database and the application user. I can create users and log them and in and return JWT to the caller and validate the users based on the JWT. However, there are certain users that I want to create that are admins that will have elevated privileges. Here is the code I am user to Create a user:
async Task<Response> ICreateUser.CreateUser(RegisterModel model)
{
var userExists = await userManager.FindByNameAsync(model.UserName);
if (userExists != null)
{
return new Response { Status = "error", Message = "User already exists" };
}
ApplicationUser user = new ApplicationUser()
{
Email = model.Email,
SecurityStamp = Guid.NewGuid().ToString(),
UserName = model.UserName
};
var result = await userManager.CreateAsync(user, model.Password);
if (!result.Succeeded)
{
return new Response { Status = "Error", Message = "Unable to create user" };
}
else
{
var adminRole = await roleManager.FindByNameAsync("admin");
if(!await userManager.IsInRoleAsync(user, adminRole.Name))
{
await userManager.AddToRoleAsync(user, adminRole.Name);
}
}
return new Response { Status = "Success", Message = "User Created" };
}
This will add the user, and even add them to the role. But when I do a list on the claims all I see are the nameidentifier, jti, email, exp, iss, and aud values. Here is the code I am using to return the claims:
public IActionResult Index()
{
var claims = User.Claims.Select(claim => new { claim.Type, claim.Value }).ToArray();
return Json(claims);
}
When I created the Role for the admin I used this code:
public async Task<IActionResult> Create([Required] string name)
{
var adminRole = await roleManager.FindByNameAsync(name);
if (adminRole == null)
{
adminRole = new IdentityRole(name);
await roleManager.CreateAsync(adminRole);
await roleManager.AddClaimAsync(adminRole, new Claim(ClaimTypes.Role, name));
return Ok("Role Created");
}
else
{
return StatusCode(StatusCodes.Status500InternalServerError, new Response { Status = "error", Message = "Role Not created" });
}
}
Like I said, I can create the user, but I don't see the Admin claim added to user so I can't authenticate by the role. What am I missing?
When I created the JWT token I needed to add a call to GetRolesAsync. Once I got this list, I could loop through the array and add the claims.
var claim = new List<Claim>();
claim.Add(new Claim(JwtRegisteredClaimNames.Sub, user.UserName));
claim.Add(new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()));
claim.Add(new Claim(ClaimTypes.Email, model.Email));
var roles = userManager.GetRolesAsync(user).Result.ToArray();
foreach(var role in roles)
{
claim.Add(new Claim(ClaimTypes.Role, role));
}
This bit of code allows me to add the user to the roles they are assigned to when they are created.
This is my initial setting for my mvc connecting with identity server.
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
AuthenticationType = "oidc",
SignInAsAuthenticationType = "Cookies",
Authority = "http://identity.azurewebsites.net",
RedirectUri = "http://localhost:62419/signin-oidc",
PostLogoutRedirectUri = "http://localhost:62419/signout-callback-oidc",
ClientId = "mvc",
ResponseType = "id_token",
Scope = "openid profile",
UseTokenLifetime = false,
RequireHttpsMetadata = false,
Notifications = new OpenIdConnectAuthenticationNotifications
{
SecurityTokenValidated = (context) =>
{
var identity = context.AuthenticationTicket.Identity;
var name = identity.Claims.FirstOrDefault(c => c.Type == identity.NameClaimType)?.Value;
return Task.FromResult(0);
}
}
});
I can get to the identity server. I received a message
Sorry, there was an error : unauthorized_client
Invalid redirect_uri
I have added the redirectUri into the ClientRedirectUris table matched with the code shown above. Is there any other area i forgot to add or set.
Request url: http://identity.azurewebsites.net/home/error?errorId=CfDJ8BPcf2qEDmRMt0TtYfAIujdUrTeIfqktT2TIcVFNomo6u6QFAROi-gEI2wXHP8kbmmiSYIK1aRV1nL-h6tFY_KeZabkMhIzy-V_0vvo2-hUFfj6I66qJWSjPiRhSYmGZa_-kYlULMb8a1Bz6UQ9UV5L6VdLscQRhScCpnOYpM6Ku84KM_S-4eZXrAX13EaVhqjxhpNhD8jIU9kJkjAn1t6sLVGrfZSEM0tAOGkTXFvBzuoucYURIFhZPJPGjVuJuRegrS2vsLPALHJCv3MLrW9ImudDeCkgf9VhAHwrRLfP3TB_7i4OvEffZwhuDuCSoyQ
You have to make sure the redirect url matches a redirect url in your client configuration in IdentityServer. For example
new Client
{
ClientId = "mvc",
ClientName = "MVC Client",
AllowedGrantTypes = GrantTypes.Implicit,
// where to redirect to after login
RedirectUris = { "http://localhost:62419/signin-oidc" },
// where to redirect to after logout
PostLogoutRedirectUris = { "http://localhost:62419/signout-callback-oidc" },
AllowedScopes = new List<string>
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile
}
}
Make sure RedirectUris matches the redirect url set in your client 'http://localhost:62419/signin-oidc'
Also, make sure your scope matches the AlowedScopes in your client configuration. It would help if we could see the request URL. i.e.
https://identity.azurewebsites.net/connect/authorize?
client_id=mvc
&redirect_uri=http://localhost:62419/signin-oidc
&response_type=id_token
&scope=openid profile
&nonce=63653346343504
&state=CfDJAJDR
&response_mode=form_post
I'm totally new to asp.net, and I'm trying to do a simple rest api.
i need to get the username of the current logged in user.
I have User.Identity.IsAuthenticated is true, but User.Identity.Name is null. I'm using jwt tokens for authentication.
here is my login method
[HttpPost]
public async Task<IActionResult> Login([FromBody] LoginData jsonUser) {
IdentityUser claimedUser;
try {
claimedUser = _userManager.Users.First(
user => user.Email == jsonUser.username || user.UserName == jsonUser.username);
} catch (Exception e) {
return NotFound("No user with this username : " + e);
}
bool isPasswordCorrect = await _userManager.CheckPasswordAsync(claimedUser, jsonUser.Password);
return isPasswordCorrect
? Ok(GenerateJwtToken(claimedUser.Email, claimedUser))
: StatusCode(401, "bad login or password");
}
//Génération du token JWT
private string GenerateJwtToken(string email, IdentityUser user) {
var claims = new List<Claim> {
new Claim(JwtRegisteredClaimNames.Sub, email),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim(ClaimTypes.NameIdentifier, user.Id),
new Claim(JwtRegisteredClaimNames.UniqueName, user.UserName),
new Claim(JwtRegisteredClaimNames.GivenName, user.UserName)
};
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["JwtKey"]));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var expires = DateTime.Now.AddDays(Convert.ToDouble(_configuration["JwtExpireDays"]));
var token = new JwtSecurityToken(
_configuration["JwtIssuer"],
_configuration["JwtIssuer"],
claims,
expires: expires,
signingCredentials: creds
);
return new JwtSecurityTokenHandler().WriteToken(token);
}
How can I get the current username ?
Please tell me what is wrong.
public void ConfigureAuth(IAppBuilder app)
{
var mo = new MicrosoftAccountAuthenticationOptions();
mo.ClientId = "xxxxxxxxxxxxxxxxx";
mo.ClientSecret = "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy";
mo.Scope.Add("wl.basic"); // No effect if this commented out
mo.Scope.Add("wl.emails");
// IF I COMMENT NEXT TWO PROPERTIES, USER IS AUTHENTICATED, BUT THE DB IS NOT
// UPDATED. LEAVE THEM AND THE REDIRECT FROM MSLIVE ENDS ON LOGIN PAGE
mo.SignInAsAuthenticationType = "External";
mo.Provider = new MicrosoftAccountAuthenticationProvider()
{
OnAuthenticated = (context) =>
{
// Set breakpoint here to see the context.Identity.Claims HAS CLAIMS DESIRED.
// SO IT APPEARS TO ME Nothing to do here but verify they exist in the debugger.
//(context.Identity.Claims).Items ARE:
//{http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier: xxxxxxxxx}
//{http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name: yyyy yyyyy}
//{urn:microsoftaccount:id: xxxxxxxx}
//{urn:microsoftaccount:name: yyyy yyyyy}
//{http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress: xxxxxxxx#hotmail.com}
return Task.FromResult(0);
}
};
// 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")
});
// Use a cookie to temporarily store information about a user logging in with a third party login provider
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
app.UseMicrosoftAccountAuthentication(mo);
}
A reasonable expectation asserts that the framwework will transparently handle the addition of a Scope to the default *AuthenticationOptions. Subsequently, wrt the MVC5 template, the developer can extract and persist Claims in ExternalLoginConfirmation code. Another reasonable expectation is that the framework will transform incoming standard ClaimTypes into Claims in the ClaimsIdentity exposed by the framework.
I am very glad source code is available MicrosoftAccountAutheticationHandler.cs, and I will check it to solve this; lacking a response. Best wishes to Katana as the documentation and the framework are maturing. Is there a way for the framework to help the developer spot config issues?.
I would have agreed with you if we both had not hit the same logical brick wall of reasonning ....
I think it is something to do with a detached Owin Security Context while the web application operates in a seperate Context and you have to 'seed' the web one. So what I've deduced is this:
in Startup.Auth.cs
var microsoftOptions =
new Microsoft.Owin.Security.MicrosoftAccount.MicrosoftAccountAuthenticationOptions
{
CallbackPath = new Microsoft.Owin.PathString("/Callbacks/External"),//register at oAuth provider
ClientId = "xxxx",
ClientSecret = "yyyyyyyyyyyyyyyyy",
Provider = new Microsoft.Owin.Security.MicrosoftAccount.MicrosoftAccountAuthenticationProvider
{
OnAuthenticated = (context) =>
{
context.Identity.AddClaim(new Claim(providerKey, context.Identity.AuthenticationType));
context.Identity.AddClaim(new Claim(ClaimTypes.Name, context.Identity.FindFirstValue(ClaimTypes.Name)));
return System.Threading.Tasks.Task.FromResult(0);
}
}
};
microsoftOptions.Scope.Add("wl.basic");
microsoftOptions.Scope.Add("wl.emails");
app.UseMicrosoftAccountAuthentication(microsoftOptions);
and in AccountController:
[AllowAnonymous]
public async Task<ActionResult> oAuthCallback(string returnUrl)
{
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
if (loginInfo == null)
{
if (User.Identity.IsAuthenticated)
return RedirectToAction("Index", "Manage");
else
return RedirectToAction("Login");
}
var currentUser = await UserManager.FindAsync(loginInfo.Login);
if (currentUser != null)
{
await StoreExternalTokensOnLocalContext(currentUser);
}
//.... rest as same as per AspNet Sample project.
}
private async Task StoreExternalTokensOnLocalContext(ApplicationUser user)
{
if (user == null)
return;
var externalIdentity = await AuthenticationManager.GetExternalIdentityAsync(DefaultAuthenticationTypes.ExternalCookie);
if (externalIdentity != null)
{
// Retrieve the existing claims for the user and add the FacebookAccessTokenClaim
var currentClaims = await UserManager.GetClaimsAsync(user.Id);
//var providerClaim = externalIdentity.FindFirstValue("provider") ?? string.Empty;
await StoreClaim("provider", user.Id, externalIdentity);
await StoreClaim("FacebookId", user.Id, externalIdentity);
await StoreClaim("image", user.Id, externalIdentity);
await StoreClaim("link", user.Id, externalIdentity);
await StoreClaim(ClaimTypes.Name, user.Id, externalIdentity);
await StoreClaim(ClaimTypes.Email, user.Id, externalIdentity);
var addedClaims = await UserManager.GetClaimsAsync(user.Id);
}
}
private async Task StoreClaim(string typeName, string userId, ClaimsIdentity externalIdentity)
{
var providerClaim = externalIdentity.Claims.FirstOrDefault(c => c.Type.Equals(typeName));
if (providerClaim == null)
return;
var previousClaims = await UserManager.GetClaimsAsync(userId);
if (previousClaims.IndexOf(providerClaim) >= 0)
return;
var idResult = await UserManager.AddClaimAsync(userId, providerClaim);
}
I have a cloud code which creates two account roles when a user signs up. Below is the method
Parse.Cloud.afterSave("account", function(request) {
var accountName = request.object.get("name");
//create admin role
var adminRoleACL = new Parse.ACL();
adminRoleACL.setPublicWriteAccess(true);
var adminRole = new Parse.Role(accountName + "_Administrator", adminRoleACL);
adminRole.save() ;
//create user role
var userRoleACL = new Parse.ACL();
userRoleACL.setPublicWriteAccess(true);
var userRole = new Parse.Role(accountName + "_User", userRoleACL);
userRole.save();
});
Now what i wanted to achieve was to add the user which just signed up to these two roles. But unfortunately i saw that in cloud code i can't get the current user.
So what i did was to add the users in the role after the roles are created from the client side. Below is the code for the same. The code executes fine and i did not see any error, however i did not see the users being added to the roles in the data browser. Any idea why is this happening? Am i missing something. I would be really thankful for all your help.
user.signUp(null, {
success : function(user) {
var currentUser = Parse.User.current();
var accountName = account.get("name");
var query = new Parse.Query(Parse.Role);
query.contains("name", accountName);
query.find({
success : function(roles) {
if (!roles) {
alert("No roles for " + accountName + " were found");
} else {
for (var i = 0; i < roles.length; i++) {
//add the user for admin role
//TODO: this needs to be done only once for the account owner
if (roles[i].get("name").search(USER_TYPE.ADMIN) >= 0) {
roles[i].getUsers().add(currentUser);
}
//add the user for user role
if (roles[i].get("name").search(USER_TYPE.USER) >= 0) {
roles[i].getUsers().add(currentUser);
}
var saved = roles[i].save();
}
alert("User was added into roles");
}
},
error : function(error) {
alert("Could not add users to the account " + accountName + " error: " + error.message);
}
});
alert("User created successfully");
},
error : function(user, error) {
alert("Error: " + error.code + " " + error.message);
}
});
thanks for the help. I go it done by using a cloud code like this
Create the roles while creating the account.
Parse.Cloud.afterSave("account", function(request) {
var accountName = request.object.get("name");
//create admin role
var adminRoleACL = new Parse.ACL();
adminRoleACL.setPublicReadAccess(false);
adminRoleACL.setPublicWriteAccess(false);
var adminRole = new Parse.Role(accountName + "_Administrator", adminRoleACL);
adminRole.save();
//create user role
var userRoleACL = new Parse.ACL();
userRoleACL.setPublicReadAccess(false);
userRoleACL.setPublicWriteAccess(false);
var userRole = new Parse.Role(accountName + "_User", userRoleACL);
userRole.save();
});
Then add the users to the created role
Parse.Cloud.define("addUsersToRole", function(request, response) {
Parse.Cloud.useMasterKey();
var currentUser = request.user;
var accountName = request.params.accountname;
var isAdmin = request.params.admin;
var query = new Parse.Query(Parse.Role);
query.contains("name", accountName);
query.find({
success : function(roles) {
console.log("roles: " + roles.length);
for (var i = 0; i < roles.length; i++) {
if ( isAdmin = false && roles[i].get("name").search("_Administrator") >= 0)
continue;
roles[i].getUsers().add(currentUser);
roles[i].save();
}
response.success();
},
error : function(error) {
response.error("error adding to admin role " + error);
}
});
});
Actually you can access the user that is performing the request, check the Cloud Code Documentation for more details.
In particular, what you want is to look at request.user:
var currentUser = request.user;