5
SOLVED - Authorize with roles
With the help of this link: https://stackoverflow.com/a/47025009/10647645
And a reply in the post with the final files with the resolution
UNSOLVED - Identityserver, how to do and use?
I have 3 initial problems here:
Token decoding: when receiving the request, I need to decode the token to validate and allow access to the roles with Authorize.
Authorize with roles in my system: putting a policy I managed to do, but with the roles I could not.
Separate solutions: I have the solution of Identity and the system itself, therefore, I need to know how to make the "system itself visualize" the Identity to validate the token and so, making the authorization of the token.
ARCHIVES
appsettings.json
"JwtKey": "MYSECRET",
"JwtIssuer": "http://localhost:8040/",
"JwtExpireDays": 30,
Startup.Cs
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
var key = Encoding.ASCII.GetBytes(Configuration["JwtKey"]);
services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(x =>
{
x.RequireHttpsMetadata = false;
x.SaveToken = true;
x.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false
};
});
// Usando a politica
services.AddAuthorization(auth =>
{
auth.AddPolicy("BearerPolicy", new AuthorizationPolicyBuilder()
.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
.RequireAuthenticatedUser().Build());
});
Accountcontroller.Cs
[HttpPost]
[AllowAnonymous]
public async Task<AccountModel> Login([FromBody] AccountModel model)
{
var usuario = new ApplicationUser();
...
var appUser = _userManager.Users.Include(...);
var claims = await GetValidClaims(appUser);
var accountModel = new AccountModel(usuario, _roleManager);
accountModel.Token = GenerateJwtToken(appUser, claims);
return accountModel;
}
private string GenerateJwtToken(ApplicationUser user, List<Claim> claims)
{
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(_configuration["JwtKey"]);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(claims),
Expires = DateTime.UtcNow.AddDays(1),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
var token = tokenHandler.CreateJwtSecurityToken(tokenDescriptor);
return tokenHandler.WriteToken(token);
}
private async Task<List<Claim>> GetValidClaims(ApplicationUser user)
{
IdentityOptions _options = new IdentityOptions();
var claims = new List<Claim>
{
new Claim(JwtRegisteredClaimNames.Sub, user.UserName),
new Claim(_options.ClaimsIdentity.UserIdClaimType, user.Id.ToString()),
new Claim(_options.ClaimsIdentity.UserNameClaimType, user.UserName)
};
var userClaims = await _userManager.GetClaimsAsync(user);
var userRoles = await _userManager.GetRolesAsync(user);
claims.AddRange(userClaims);
foreach (var userRole in userRoles)
{
claims.Add(new Claim(ClaimTypes.Role, userRole));
var role = await _roleManager.FindByNameAsync(userRole);
if (role != null)
{
var roleClaims = await _roleManager.GetClaimsAsync(role);
foreach (Claim roleClaim in roleClaims)
{
claims.Add(roleClaim);
}
}
}
return claims;
}
Rolemodel
public class RoleModel
{
public string Id{ get; set; }
public string Nome { get; set; }
public string NomeNormalizado { get; set; }
public string SeloConcorrencia { get; set; }
public RoleModel() { }
public RoleModel(ApplicationRole applicationRole)
{
Id = applicationRole.Id;
Nome = applicationRole.Name;
NomeNormalizado = applicationRole.NormalizedName;
SeloConcorrencia = applicationRole.ConcurrencyStamp;
}
}
Applicationuser
public class ApplicationUser : IdentityUser
{
public string PrimeiroNome { get; set; }
public string UltimoNome { get; set; }
public virtual ICollection<ApplicationUserRole> UserRoles { get; } = new List<ApplicationUserRole>();
}
Applicationrole
public class ApplicationRole: IdentityRole
{
public virtual ICollection<ApplicationUserRole> UserRoles { get; } = new List<ApplicationUserRole>();
}
Applicationuserrole
public class ApplicationUserRole : IdentityUserRole<string>
{
public virtual ApplicationUser User { get; set; }
public virtual ApplicationRole Role { get; set; }
}
Request POST Angular
AdicionarPessoa(pessoa: Pessoa): Observable<Pessoa> {
return this.httpClient.post<Pessoa>(environment.url + "/Pessoa/AdicionarPessoa", pessoa, environment.token);
};
Environment
token: { headers: {'Authorization':'Bearer ' + localStorage.getItem("token")}}
Personal controller
[HttpPost]
[Route("api/Pessoa/AdicionarPessoa")]
// [Authorize("BearerPolicy")] <-- Usando a politica
[Authorize(Roles = "Administrador")]
public Pessoa AdicionarPessoa([FromBody]Pessoa model)
ERROR: http://localhost/api/Person/Add 401 (Unauthorized)
EDITIONS
I did the example in another method, so I don’t need to pass a json in the post:
My Token
FIRST ATTEMPT - FAILURE
Get all users with Role Administrator
[HttpGet]
[Route("api/Pessoa/ObterTodosPessoas")]
[Authorize(Roles = "Administrador")]
public IEnumerable<Pessoa> ObterTodosPessoas()
{
var PessoaRepositoryVar = new PessoaRepository();
var Pessoas = PessoaRepositoryVar.ObterTodos();
return Pessoas;
}
Console POSTMAN
I made some modifications since the day I posted here, so I’m editing the post to stay as equal as possible.
During some time of research and study, I came across some doubts:
At the beginning of the post, I said that I had 3 initial problems, I will answer them and I would like you to say "that’s it, this right so..." or "no, this wrong, think this way:...." I thank you from the beginning!
Decode the token
There is no reason why I decode the token, since Authorize and Authentication is done in startup.Cs
Authorize
I managed to do, in the reply of the post has as I did
Separate solutions
I don’t know if it’s clear to everyone, but roughly speaking, I need to open 2 visual studio, 1 to load Identity and another to load the other solution. And about Identityserver, I searched little about, I want to be able to make work the roles in Identity and then try to make Identityserver and put in the rest of the system... If anyone has any material that explains this, I’d appreciate it
ARCHIVES 07/08/2019
Startup.Cs
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddDbContext<ApplicationDbContext>(options =>
options.UseOracle(
Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity<ApplicationUser, ApplicationRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultUI()
.AddDefaultTokenProviders();
services.AddCors();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
var key = Encoding.ASCII.GetBytes(Configuration["JwtKey"]);
services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(x =>
{
x.RequireHttpsMetadata = false;
x.SaveToken = true;
x.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false
};
});
}
Accountcontroller.Cs
[HttpPost]
[Route("Login")]
public async Task<AccountModel> Login([FromBody] AccountModel model)
{
if (ModelState.IsValid)
{
var usuario = new ApplicationUser();
var signInResultado = new Microsoft.AspNetCore.Identity.SignInResult();
Task.Run(async () =>
{
usuario = await _userManager.FindByEmailAsync(model.Email);
}).Wait();
...
if (signInResultado.Succeeded)
{
var appUser = _userManager.Users.FirstOrDefault(u => u.Id == usuario.Id);
var claims = await GetValidClaims(appUser);
var accountModel = new AccountModel(usuario, _roleManager);
accountModel.Token = GenerateJwtToken(appUser, claims);
return accountModel;
}
}
return model;
}
private string GenerateJwtToken(ApplicationUser user, List<Claim> claims)
{
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(_configuration["JwtKey"]);
// AQUI TENHO 2 FORMAS DE FAZER, NÃO SEI QUAL FUNCIONA OU QUAL A MAIS CORRETA
// FORMA 1
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(claims),
Expires = DateTime.UtcNow.AddDays(1),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
var token = tokenHandler.CreateJwtSecurityToken(tokenDescriptor);
return tokenHandler.WriteToken(token);
// FORMA 2
var tokens = new JwtSecurityToken(
claims: claims,
expires: DateTime.UtcNow.AddDays(1),
signingCredentials: new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
);
return new JwtSecurityTokenHandler().WriteToken(tokens);
}
private async Task<List<Claim>> GetValidClaims(ApplicationUser user)
{
IdentityOptions _options = new IdentityOptions();
var claims = new List<Claim>
{
new Claim(JwtRegisteredClaimNames.Sub, user.UserName),
new Claim(_options.ClaimsIdentity.UserIdClaimType, user.Id.ToString()),
new Claim(_options.ClaimsIdentity.UserNameClaimType, user.UserName)
};
var userClaims = await _userManager.GetClaimsAsync(user);
var userRoles = await _userManager.GetRolesAsync(user);
claims.AddRange(userClaims);
foreach (var userRole in userRoles)
{
claims.Add(new Claim(ClaimTypes.Role, userRole));
var role = await _roleManager.FindByNameAsync(userRole);
if (role != null)
{
var roleClaims = await _roleManager.GetClaimsAsync(role);
foreach (Claim roleClaim in roleClaims)
{
claims.Add(roleClaim);
}
}
}
return claims;
}
The other files remain the same...
My biggest difficulty at the moment is getting C# to identify/validate/authorize the roles you have in the token passed by the header!
Friend, also put your request in the POST so we can see how you are doing it, it can be something in it.
– Maycon F. Castro
@Mayconf.Castro, I don’t know if I put what I said
– LeoHenrique
Friend you have to pass your token in the header of your request, from what I see, you are just firing the url so it is returning 401 - unauthorized. Try to add your token to the header of your request and tell me if it worked.
– Maycon F. Castro
Can you help me with that? I don’t know how to put information in the request header
– LeoHenrique
Amigo check out this link: https://stackoverflow.com/questions/45286764/angular-httpclient-doesnt-send-header
– Maycon F. Castro
In your case the header should be, Authorization and your access token
– Maycon F. Castro
I re-wanted by Postman, edited the post, gave 401 Unauthorized
– LeoHenrique
I added a
policy
and I did with theAuthorize("Policy")
, worked within Identity, but the other solutions that are separate, do not work. But not only that, I would like to do by the roles too: example:Authorize(Roles = "Adm")
– LeoHenrique