I am getting the access_token from an external api (other app) but i couldn't authenticate/authorize my api using this token, here i am getting the token :
[HttpPost("/authorize/user")]
public async Task<IActionResult> Post([FromBody] LoginDTO login)
{
LoginDTO data = new LoginDTO
{
Email = login.Email,
Password = login.Password
};
var stringPayload = await Task.Run(() => JsonConvert.SerializeObject(data));
var httpContent = new StringContent(stringPayload, Encoding.UTF8, "application/json");
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("myrul");
var response = client.PostAsync("auth/login", httpContent).Result;
var payload = JObject.Parse(await response.Content.ReadAsStringAsync())["token"];
var access_token = payload.Value<string>("access_token");
var refresh_token = payload.Value<string>("refresh_token");
var expires_in = payload.Value<string>("expires_in");
if (response.IsSuccessStatusCode)
return Ok(access_token);
else
return BadRequest();
}
}
and the startup.cs :
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(x =>
{
x.SaveToken = true;
x.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes("012345678901234567890123456789ab")),
ValidateIssuer = false,
ValidateAudience = false,
RequireExpirationTime = false,
ValidateLifetime = true
};
});
the API i need to authorize :
[HttpGet]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
but the problem its always 401 unauthorized :
postman pic
Related
I have a minimal api here. It creates me a JWT for authorization, and then it tests it. using the [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] attribute.
app.MapPost("/login", [AllowAnonymous]
async (HttpContext http, ITokenService tokenService, IUserRepositoryService userRepositoryService) =>
{
var userLogin = await http.Request.ReadFromJsonAsync<UserModel>();
var userDto = userRepositoryService.GetUser(userLogin);
if (userDto == null)
{
http.Response.StatusCode = 401;
return;
}
var token = tokenService.BuildToken(builder.Configuration["Jwt:Key"], builder.Configuration["Jwt:Issuer"],
builder.Configuration["Jwt:Audience"], userDto);
await http.Response.WriteAsJsonAsync(new { Token = token });
});
app.MapGet("/secretAction",
(Func<string>)([Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]() => "Action Succeeded")
);
The first method works just fine. A post to the login endpoint with a json string containing the login and password returns to me a jwt.
The second endpoint is always returning unauthorized.
I have been back and forth with this trying to understand why its not able to parse the token that it creates.
Any help would be greatly appreciated.
appsettings.json
"Jwt": {
"Key": "this-is-the-secret",
"Issuer": "https://jwtauth.example.com",
"Audience": "api1"
}
program.cs
using System.ComponentModel.DataAnnotations;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.IdentityModel.Tokens;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton<ITokenService>(new TokenService());
builder.Services.AddSingleton<IUserRepositoryService>(new UserRepositoryService());
builder.Services.AddAuthorization();
var key = Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"]);
builder.Services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(opt =>
{
opt.RequireHttpsMetadata = false;
opt.SaveToken = true;
opt.TokenValidationParameters = new TokenValidationParameters
{
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuerSigningKey = true,
ValidIssuer = builder.Configuration["Jwt:Issuer"],
ValidateIssuer = false,
ValidAudience = builder.Configuration["Jwt:Audience"],
ValidateAudience = true,
ValidateLifetime = true,
};
});
await using var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.MapGet("/",
(Func<string>)(() =>
"Test JWT Authentication using Minimalist Web API .net 6. <br> /login UserName: user1, Password: test <br> /secretAction "));
app.MapPost("/login", [AllowAnonymous]
async (HttpContext http, ITokenService tokenService, IUserRepositoryService userRepositoryService) =>
{
var userLogin = await http.Request.ReadFromJsonAsync<UserModel>();
var userDto = userRepositoryService.GetUser(userLogin);
if (userDto == null)
{
http.Response.StatusCode = 401;
return;
}
var token = tokenService.BuildToken(builder.Configuration["Jwt:Key"], builder.Configuration["Jwt:Issuer"],
builder.Configuration["Jwt:Audience"], userDto);
await http.Response.WriteAsJsonAsync(new { Token = token });
});
app.MapGet("/secretAction",
(Func<string>)([Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]() => "Action Succeeded")
);
await app.RunAsync();
public record UserDto(string UserName, string Password);
public record UserModel
{
[Required] public string UserName { get; set; }
[Required] public string Password { get; set; }
}
public interface IUserRepositoryService
{
UserDto GetUser(UserModel userModel);
}
public class UserRepositoryService : IUserRepositoryService
{
private List<UserDto> _users => new()
{
new("User1", "test"),
};
public UserDto GetUser(UserModel userModel)
{
return _users.FirstOrDefault(x =>
string.Equals(x.UserName, userModel.UserName) && string.Equals(x.Password, userModel.Password));
}
}
public interface ITokenService
{
string BuildToken(string key, string issuer, string audience, UserDto user);
}
public class TokenService : ITokenService
{
private TimeSpan ExpiryDuration = new TimeSpan(0, 30, 0);
public string BuildToken(string key, string issuer, string audience, UserDto user)
{
var keyBytes = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key));
var claims = new[]
{
new Claim(ClaimTypes.Name, user.UserName),
new Claim(ClaimTypes.NameIdentifier, Guid.NewGuid().ToString())
};
var descriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(claims),
Issuer = issuer,
Audience = audience,
SigningCredentials = new SigningCredentials(keyBytes, SecurityAlgorithms.HmacSha256Signature),
IssuedAt = DateTime.Now,
NotBefore = DateTime.Now,
Expires = DateTime.Now.AddDays(1)
};
var jwtHandler = new JwtSecurityTokenHandler();
var token = jwtHandler.CreateToken(descriptor);
return jwtHandler.WriteToken(token);
// alternatively
// return jwtHandler.CreateEncodedJwt(descriptor);
}
To test it.
const string jwtUrl = "https://localhost:7080/login";
var content = new StringContent("{\"UserName\" : \"User1\",\"Password\" : \"test\"}", Encoding.UTF8, "application/json");
var httpResponseMessage = await client.PostAsync(jwtUrl,content);
var jwt = await httpResponseMessage.Content.ReadAsStringAsync();
var queueMessage = JsonSerializer.Deserialize<TokenResponse>(jwt);
Console.WriteLine(queueMessage.token);
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", queueMessage.token);
const string protectedUrl = "https://localhost:7080/secretAction";
var result = await client.GetStringAsync(protectedUrl);
Console.WriteLine(result);
Logs
Here is a JWT just created.
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiVXNlcjEiLCJuYW1laWQiOiI2OWJmOTg5Ni0wYTIyLTQ1N2UtODkyMy00ZTM4MGQzMTEyNTkiLCJuYmYiOjE2NjcyOTA5NTgsImV4cCI6MTY2NzM3NzM1OCwiaWF0IjoxNjY3MjkwOTU4LCJpc3MiOiJodHRwczovL2p3dGF1dGguZXhhbXBsZS5jb20i
LCJhdWQiOiJhcGkxIn0.aUxhJuOrNOHeId6vHpHe1ZqnuC2MJ4TaYi577Cc37oU
The logs from trying to access the secretAction sending the jwt as a bearer token.
System.Net.Http.HttpRequestException: Response status code does not indicate success: 401 (Unauthorized).
at System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode()
at System.Net.Http.HttpClient.GetStringAsyncCore(HttpRequestMessage request, CancellationToken cancellationToken)
at Program.$(String[] args) in C:\Development\FreeLance\Glassix\asp-net-core-auth-with-self-generated-jwt\ConsoleApp1\Program.cs:line 27
So with some hints from friends on twitter. and Khellang I added event logging to the error
opt.Events = new JwtBearerEvents()
{
OnAuthenticationFailed = context =>
{
var err = context.Exception.ToString();
return context.Response.WriteAsync(err);
}
};
This lead to the error message
Method not found: 'Void Microsoft.IdentityModel.Tokens.InternalValidators.ValidateLifetimeAndIssuerAfterSignatureNotValidatedJwt
Followed by this question on Stack Unauthorized (Invalid Token) when authenticating with JWT Bearer Token after update to .NET 6
After installing the recommended package System.IdentityModel.Tokens.Jwt everything magically works.
I have an API secured by Bearer token to be consumed in a mvc project. I'm using ClientFactory to call the API in mvc. I get the generated token from WeatherForecast [HttpGet] method and use it to to access the Authorized PlayerController methods. It works fine in Postman, But when I try to access PlayerController in mvc, On running a debugger It shows Unauthorized response!
Here is the action to get and use the generated token in mvc project
private async Task<JWTToken> CreateToken()
{
var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost:42045/weatherforecast");
var client = _clientFactory.CreateClient();
HttpResponseMessage response = await client.SendAsync(request);
var token = await response.Content.ReadAsStringAsync();
HttpContext.Session.SetString("JwToken", token);
return JsonConvert.DeserializeObject<JWTToken>(token);
}
public async Task<IActionResult> GetAllPlayers()
{
JWTToken token = null;
var strToken = HttpContext.Session.GetString("JwToken");
if (string.IsNullOrWhiteSpace(strToken))
{
token = await CreateToken();
}
else
{
token = JsonConvert.DeserializeObject<JWTToken>(strToken);
}
if (token == null || string.IsNullOrWhiteSpace(token.token) || token.expireAt <= DateTime.UtcNow)
{
token = await CreateToken();
}
List<Player> players = new List<Player>();
var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost:42045/api/player");
var client = _clientFactory.CreateClient();
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token.token);
HttpResponseMessage response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
var apiString = await response.Content.ReadAsStringAsync();
players = JsonConvert.DeserializeObject<List<Player>>(apiString);
}
return View(players);
}
Here is CreateToken method in WeatherForecast controller
[HttpGet]
public ActionResult<string> Get()
{
var rng = new Random();
var weathers = Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = Summaries[rng.Next(Summaries.Length)]
});
return Ok(new {
expireAt = DateTime.UtcNow.AddMinutes(15), token = CreateToken()
});
}
public string CreateToken()
{
var key = new SymmetricSecurityKey(System.Text.Encoding.ASCII.GetBytes(_configuration
.GetSection("AppSettings:Token").Value));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256Signature);
var token = new JwtSecurityToken(
claims: new List<Claim>
{
new Claim("firstName", "Ahmad"),
new Claim("lastName", "Zooghii")
},
signingCredentials: creds,
expires: DateTime.UtcNow.AddMinutes(15)
);
return new JwtSecurityTokenHandler().WriteToken(token);
}
add services.AddHttpClient(); in startup.cs
And this code in controller:
private readonly IHttpClientFactory _httpClientFactory;
public HomeController(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
}
public async Task<IActionResult> IndexAsync()
{
var httpClient = _httpClientFactory.CreateClient();
var token = "your_token";
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
HttpResponseMessage response = await httpClient.GetAsync("your_url");
return View();
}
======================Cors policy========================
add below code in startup.cs -> ConfigureServices method
services.AddCors(options =>
{
options.AddPolicy(name: "mypolicy",
builder =>
{
builder.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin();
});
});
add app.UseCors("mypolicy"); in startup.cs -> Configure method behind app.UseRouting(); line.
I've posted this question before but being new I wanted to learn how to write a "proper" question (in case you've tried to help me and I didn't get back immediately, wanted to get it mostly right if not all hopefully I've got the hang of it)
This is just registration/login code whose purpose is self explanatory, the error am getting (could not load file or assembly 'microsoft aspnetcore razor runtime 3.1 1) happens in a service registration class in the AddControllers() method, I've tried adding the package specified in the error but it didn't work, I tried a few other similar packages but the result has been the same, when I build I get no errors or warnings but when the app runs I get the error, hope this is enough data to work with.
//controller class Login and registration
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public class IdentifyMe : Controller
{
private readonly IIdentify _identify;
public IdentifyMe(IIdentify identifying)
{
_identify = identifying;
}
[HttpGet(Api_Routes.Identity.Register)]
public async Task<IActionResult> Register(UserRegistration register)
{
if (!ModelState.IsValid)
{
return BadRequest(new Unauthenticated
{
Errors=ModelState.Values.SelectMany(x=>x.Errors.Select(xx=>xx.ErrorMessage))
});
}
var authresponce = await _identify.RegisterAsync(register.Email, register.Password, register.User_Name);
if (!authresponce.Success)
{
return BadRequest(new Unauthenticated
{
Errors = authresponce.Errors
});
}
return Ok(new Authenticated
{
Token = authresponce.Token
});
}
[HttpGet(Api_Routes.Identity.Login)]
public async Task<IActionResult> LoginAsync(User_login login)
{
var authresponce = await _identify.LoginAsync(login.Password, login.email);
if (!authresponce.Success)
{
return BadRequest(new Unauthenticated
{
Errors = authresponce.Errors
});
}
return Ok(new Authenticated
{
Token = authresponce.Token
});
}
}
// service registration
public class Dbinstaller : IInstaller
{
public void Cleanner(IConfiguration configuration, IServiceCollection services)
{
var jwt = new JWTsettings();
configuration.Bind(nameof(jwt), jwt);
services.AddSingleton(jwt);
services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(x =>
{
x.SaveToken = true;
var TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(jwt.Secret)),
ValidateIssuer = false,
ValidateAudience = false,
RequireExpirationTime = false,
ValidateLifetime = true
};
});
services.AddSwaggerGen(x =>
{
x.SwaggerDoc("v1", new OpenApiInfo { Title = "TXC API", Version = "v1" });
var security = new Dictionary<string, IEnumerable<string>>
{
{"Bearer", new string[0]}
};
x.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Description="JWT Authorization Header using the bearer scheme",
Name="Authorization",
In=ParameterLocation.Header,
Type=SecuritySchemeType.ApiKey
});
x.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{new OpenApiSecurityScheme{Reference=new OpenApiReference
{
Id="Bearer",
Type=ReferenceType.SecurityScheme
}
},new List<string>() }
});
});
services.AddDbContext<DataContext>(opt =>
opt.UseSqlServer(configuration.GetConnectionString("TXC Connection")));
services.AddIdentityCore<IdentityUser>();
}
}
//service registration, specified error occurs in Add controllers()
public class In_App_Componentes : IInstaller
{
public void Cleanner(IConfiguration configuration, IServiceCollection services)
{
services.AddControllers().AddNewtonsoftJson(p => p.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver());
services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
services.AddScoped<IX_Change, The_center>();
services.AddScoped<IIdentify, Identify>();
}
}
//service implementation class
public class Identify : IIdentify
{
private readonly UserManager<IdentityUser> _manager;
private readonly JWTsettings jwtset;
public Identify(UserManager<IdentityUser> userManager, JWTsettings jW)
{
_manager = userManager;
jwtset = jW;
}
public async Task<Authentication_result> RegisterAsync(string email, string password, string Username)
{
var exists = _manager.FindByEmailAsync(email);
if (exists != null)
{
return new Authentication_result
{
Errors = new[] { "User with this email already exists" }
};
}
var newPerson = new IdentityUser
{
Email = email,
UserName = Username
};
var Creation = await _manager.CreateAsync(newPerson, password);
if (!Creation.Succeeded)
{
return new Authentication_result
{
Errors = new[] { "Invalid user!" }
};
}
return Generate_Authentication_Result(newPerson);
}
public async Task<Authentication_result> LoginAsync(string email, string Password)
{
var user = await _manager.FindByEmailAsync(email);
if (user == null)
{
return new Authentication_result
{
Errors = new[] { "User does not exists" }
};
}
var validate_password = await _manager.CheckPasswordAsync(user, Password);
if (!validate_password)
{
return new Authentication_result
{
Errors = new[] { "" }
};
}
return Generate_Authentication_Result(user);
}
private Authentication_result Generate_Authentication_Result(IdentityUser newPerson)
{
var Tokenhandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(jwtset.Secret);
var TokenDescripter = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new[]
{
new Claim(JwtRegisteredClaimNames.Sub, newPerson.Email),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim(JwtRegisteredClaimNames.Email, newPerson.Email),
new Claim("id",newPerson.Id)
}),
Expires = DateTime.UtcNow.AddHours(2),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
var token = Tokenhandler.CreateToken(TokenDescripter);
return new Authentication_result
{
Success = true,
Token = Tokenhandler.WriteToken(token)
};
}
}
Ciao, I tried to search Microsoft.AspNetCore.Razor.Runtime v. 3.1.1 on NuGet but I found only 2.2.0 . I didn't found any reference to 3.1.1 . Anyway, downgrading Microsoft.AspNetCore.Identity.UI to 3.1.0 should solve your problem as mentioned here
I'm currently converting my Web API 2.0 to .NET Core Web API but there is one section I'm struggling with.
In my existing API, I have an attribute with the following code:
public class JwtAuthentication : Attribute, IAuthenticationFilter
{
public string Realm { get; set; }
public bool AllowMultiple => false;
public async Task AuthenticateAsync(
HttpAuthenticationContext context,
CancellationToken cancellationToken)
{
var request = context.Request;
var authorization = request.Headers.Authorization;
// checking request header value having required scheme "Bearer" or not.
if (authorization == null ||
authorization.Scheme.ToLowerInvariant() != "bearer" ||
string.IsNullOrEmpty(authorization.Parameter))
{
context.ErrorResult = new AuthenticationFailureResult("JWT Token is Missing", request);
return;
}
// Getting Token value from header values.
var token = authorization.Parameter;
var principal = await AuthJwtToken(token);
if (principal == null)
{
context.ErrorResult = new AuthenticationFailureResult("Invalid JWT Token", request);
}
else
{
context.Principal = principal;
}
}
private static bool ValidateToken(string token, out ICollection<Claim> claims)
{
claims = null;
var simplePrinciple = JwtAuthManager.GetPrincipal(token);
if (simplePrinciple == null)
{
return false;
}
var identity = simplePrinciple.Identity as ClaimsIdentity;
if (identity == null)
{
return false;
}
if (!identity.IsAuthenticated)
{
return false;
}
var usernameClaim = identity.FindFirst(ClaimTypes.Name);
var emailClaim = identity.FindFirst(ClaimTypes.Email);
var username = usernameClaim?.Value;
var email = emailClaim?.Value;
if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(email))
{
return false;
}
claims = identity.Claims.ToList();
return true;
}
protected Task<IPrincipal> AuthJwtToken(string token)
{
if (ValidateToken(token, out var claims))
{
var identity = new ClaimsIdentity(claims, "Jwt");
IPrincipal user = new ClaimsPrincipal(identity);
return Task.FromResult(user);
}
return Task.FromResult<IPrincipal>(null);
}
public Task ChallengeAsync(
HttpAuthenticationChallengeContext context,
CancellationToken cancellationToken)
{
Challenge(context);
return Task.FromResult(0);
}
private void Challenge(HttpAuthenticationChallengeContext context)
{
string parameter = null;
if (!string.IsNullOrEmpty(Realm))
{
parameter = "realm=\"" + Realm + "\"";
}
context.ChallengeWith("Bearer", parameter);
}
}
If I understand correctly, in ASP.NET Core, all I have to do is define the following in my startup:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = Configuration["Jwt:Issuer"],
ValidAudience = Configuration["Jwt:Issuer"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"]))
};
});
and I'm not sure whether or not I'll need the below but it looks like it:
services.AddMvc();
and all I could do is use the [Authorize] attribute but what if I want to replicate the Attribute I used in my ASP.NET MVC Web API 2.0?
Should I? I like the fact that I can see where things have gone wrong with the token. If it can be used the same way and assuming it is OK to do so, how do I do this? I haven't found anything that would help when googling for a solution?
Thanks.
Based on #dropoutcoder answer,
As Events in options.Events is null, I was getting an error object reference not set... and to get around this problem I used the following instead:
options.Events = new JwtBearerEvents()
{
OnMessageReceived = context =>
{
return Task.CompletedTask;
},
OnAuthenticationFailed = context =>
{
return Task.CompletedTask;
},
OnTokenValidated = context =>
{
return Task.CompletedTask;
},
OnChallenge = context =>
{
return Task.CompletedTask;
},
OnForbidden = context =>
{
return Task.CompletedTask;
}
};
I guess you don't want to reinvent the whole bearer token authentication wheel.
In case you'd like to customize how events are handled you can use JwtBearerOptions.Events Property to hook your own delegates to one or more of them. (OnAuthenticationFailed Property, OnChallenge Property, OnMessageReceived Property, OnTokenValidated Property).
Example failed authentication logging.
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = Configuration["Jwt:Issuer"],
ValidAudience = Configuration["Jwt:Issuer"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"]))
};
options.Events.OnAuthenticationFailed = (context) =>
{
// Log failed authentication here
// Return control back to JWT Bearer middleware
return Task.CompletedTask;
}
});
Hope it helps
I'm using IdentityServer4 in ASP.NET Core on Framework 4.6.2 with EntityFramework 6 and Asp.Net Identity 2 (not Core).
For this I implemented the IProfileService.
And I am using in my client with Angular 4.x the oidc-client.js library.
I write the access_token in the localStorage to get it and mount the header with the Authentication Bearer.
The problem is that in IPrincipal the Name and Claim are empty. The IsAuthenticated property is false.
My Client configuration:
new Client
{
ClientName = "API Client",
ClientId = IdentityContants.Clients.ImplicitClient.ClientId,
RequireClientSecret = false,
AllowedGrantTypes = GrantTypes.Implicit,
AccessTokenType = AccessTokenType.Jwt,
AllowAccessTokensViaBrowser = true,
AlwaysSendClientClaims = true,
RequireConsent = false,
RedirectUris = { ... },
PostLogoutRedirectUris = { ... },
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityContants.Scopes.ApiScope.Name,
}
}
The configuration in API:
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
{
Authority = IdentityContants.AuthServer,
ApiName = IdentityContants.Clients.ImplicitClient.ClientId,
LegacyAudienceValidation = true,
AllowedScopes = new[]
{
"openid",
"profile",
IdentityContants.Scopes.ApiScope.Name
},
RequireHttpsMetadata = false
});
And I have these settings in Angular 4.x applications:
const settings: any = {
authority: environment.urls.api.account,
client_id: environment.clients.api.id,
redirect_uri: environment.urls.front.apps + '/auth',
post_logout_redirect_uri: environment.urls.front.apps,
response_type: 'id_token token',
scope: 'api.scope openid profile',
loadUserInfo: true
};
I'm using JwtClaimTypes.Name and ClaimTypes.Name.
I tried with and without the JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear().
The ProfileService
public class ProfileService : IProfileService
{
private readonly AppUserManager _userManager;
public ProfileService(AppUserManager userManager)
{
_userManager = userManager;
}
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
{
var subject = context.Subject;
if (subject == null) throw new ArgumentNullException(nameof(context.Subject));
var subjectId = subject.GetSubjectId();
var user = await _userManager.Users
.Include( ... the includes )
.SingleOrDefaultAsync(x => x.Id == subjectId);
if (user == null)
throw new ArgumentException("Invalid subject identifier");
context.IssuedClaims = await GetClaimsFromUserAsync(user);
}
public async Task IsActiveAsync(IsActiveContext context)
{
var subject = context.Subject;
if (subject == null) throw new ArgumentNullException(nameof(context.Subject));
var subjectId = subject.GetSubjectId();
var user = await _userManager.Context.Users
.Include( ... the includes )
.SingleOrDefaultAsync(x => x.Id == subjectId);
context.IsActive = await ValidateSecurityStamp(user, subject);
}
private async Task<bool> ValidateSecurityStamp(Usuario user, ClaimsPrincipal subject)
{
if (user == null)
return false;
if (!_userManager.SupportsUserSecurityStamp)
return true;
var securityStamp = subject.Claims.Where(c => c.Type == "security_stamp").Select(c => c.Value).SingleOrDefault();
if (securityStamp == null)
return true;
var dbSecurityStamp = await _userManager.GetSecurityStampAsync(user.Id);
return dbSecurityStamp == securityStamp;
}
public async Task<List<Claim>> GetClaimsFromUserAsync(Usuario user)
{
var claims = new List<Claim>
{
new Claim(JwtClaimTypes.Subject, user.Id),
new Claim(JwtClaimTypes.Name, user.UserName),
new Claim(JwtClaimTypes.PreferredUserName, user.UserName),
new Claim(ClaimTypes.Name, user.UserName) // to test
};
if (_userManager.SupportsUserEmail)
{
claims.AddRange(new[]
{
new Claim(JwtClaimTypes.Email, user.Email),
new Claim(JwtClaimTypes.EmailVerified, user.EmailConfirmed ? "true" : "false", ClaimValueTypes.Boolean)
});
}
if (_userManager.SupportsUserClaim)
{
claims.Add(new Claim(CustomClaimTypes.User.Name, user.Name));
... others claims
claims.AddRange(await _userManager.GetClaimsAsync(user.Id));
}
if (_userManager.SupportsUserRole)
{
var roles = await _userManager.GetRolesAsync(user.Id);
claims.AddRange(roles.Select(role => new Claim(JwtClaimTypes.Role, role)));
claims.AddRange(roles.Select(role => new Claim(ClaimTypes.Role, role))); // to test
}
return claims;
}
}
And, then Action Login Post:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginInputModel model)
{
if (ModelState.IsValid)
{
var user = await _userManager.Users
.Include( ... the includes )
.SingleOrDefaultAsync(x => x.UserName
.Equals(model.Username, StringComparison.CurrentCultureIgnoreCase));
if (await _userManager.CheckPasswordAsync(user, model.Password))
{
AuthenticationProperties props = null;
if (AccountOptions.AllowRememberLogin)
{
props = new AuthenticationProperties
{
IsPersistent = true,
ExpiresUtc = DateTimeOffset.UtcNow.Add(AccountOptions.RememberMeLoginDuration)
};
}
await _events.RaiseAsync(new UserLoginSuccessEvent(user.UserName, user.Id, user.UserName));
await HttpContext.Authentication.SignInAsync(user.Id, user.UserName, props);
if (!_interaction.IsValidReturnUrl(model.ReturnUrl) || !Url.IsLocalUrl(model.ReturnUrl))
return Redirect(model.ReturnUrl);
return Redirect(Urls.Apps);
}
await _events.RaiseAsync(new UserLoginFailureEvent(model.Username, "invalid credentials"));
ModelState.AddModelError("", AccountOptions.InvalidCredentialsErrorMessage);
}
return View(new LoginInputModel(model.Username, model.ReturnUrl));
}
Nothing work!
What could be wrong?
You need to add the following to your authentication middleware.
TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = JwtClaimTypes.Name;
}