Invalid JWT bearer token on .net 6.0 web api - jwt

I am trying to implement security to my app and used Identity and JWT bearer token to authenticate but I always get the invalid token response in my swagger. Tried many solutions but I still get the same response.
[HttpPost("[action]")]
public async Task<IActionResult> Login(LoginBindingModel login)
{
IActionResult actionResult;
var user = await userManager.FindByEmailAsync(login.Email);
if (user == null)
{
actionResult = NotFound(new {errors = new[] {$"User with email {login.Email} is not found."}});
}
else if (await userManager.CheckPasswordAsync(user, login.Password))
{
if(!user.EmailConfirmed)
{
actionResult = BadRequest(new { errors = new[] { $"Email not confirmed." } });
}
else
{
var token = GenerateTokenAsync(user);
actionResult = Ok(token);
}
}
else
{
actionResult = BadRequest(new { errors = new[] { $"Password is not valid." } });
}
return actionResult;
}
private string GenerateTokenAsync(IdentityUser user)
{
IList<Claim> userClaims = new List<Claim>
{
new Claim("UserName", user.UserName),
new Claim("Email", user.Email)
};
var x = jwtOptions.SecurityKey;
return new JwtSecurityTokenHandler().WriteToken(new JwtSecurityToken(
claims: userClaims,
expires: DateTime.UtcNow.AddMonths(1),
signingCredentials: new SigningCredentials(jwtOptions.SecurityKey, SecurityAlgorithms.HmacSha256)));
}
Program.cs
using ASPNetCoreMasterAPIAssignment2.Filters;
using DomainModels;
using Infrastructure.Data.Models;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using Repositories;
using Services;
using System.Text;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
builder.Services.AddSwaggerGen(x =>
{
x.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
In = ParameterLocation.Header,
Description = "Please insert JWT token with bearer into field",
Name = "Authorization",
Type = SecuritySchemeType.ApiKey
});
x.AddSecurityRequirement(new OpenApiSecurityRequirement {
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
}
},
new string[] { }
}
});
});
builder.Services.AddScoped<IItemService, ItemService>();
builder.Services.AddScoped<IItemRepository, ItemRepository>();
builder.Services.AddDbContext<ItemDbContext>(opt =>
{
opt.UseSqlServer(builder.Configuration.GetConnectionString("default"));
});
builder.Services.AddIdentity<IdentityUser, IdentityRole>()
.AddEntityFrameworkStores<ItemDbContext>()
.AddDefaultTokenProviders();
SecurityKey key = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(builder.Configuration["jwt:secret"]));
builder.Services.Configure<JWTOptions>(_ => _.SecurityKey = key);
builder.Services.AddAuthentication(opt =>
{
opt.DefaultAuthenticateScheme = "Bearer";
opt.DefaultChallengeScheme = "Bearer";
opt.DefaultScheme = "Bearer";
})
.AddJwtBearer(opt =>
{
opt.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false,
ValidateIssuer = false,
IssuerSigningKey = key
};
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI();
}
else
{
app.UseExceptionHandler("/error");
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"default": "Server=localhost;Database=EfCoreDb;Trusted_Connection=True;"
},
"jwt": {
"secret": "5a927360-790b-4ba5-bae1-09aa98364090"
}
}
when i add the [Authorized] attribute to a controller, i get the following error

invalid_token can be caused by a lot of cases.
Try to view the root cause by handling the OnChallenge method (in particular the content of context.AuthenticateFailure):
return builder.AddJwtBearer(options =>
{
options.Authority = issuer;
options.Audience = audience;
options.TokenValidationParameters = new TokenValidationParameters()
{
ClockSkew = new System.TimeSpan(0, 0, 30)
};
options.Events = new JwtBearerEvents()
{
OnChallenge = context =>
{
context.HandleResponse();
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
context.Response.ContentType = "application/json";
// Ensure we always have an error and error description.
if (string.IsNullOrEmpty(context.Error))
context.Error = "invalid_token";
if (string.IsNullOrEmpty(context.ErrorDescription))
context.ErrorDescription = "This request requires a valid JWT access token to be provided";
// Add some extra context for expired tokens.
if (context.AuthenticateFailure != null && context.AuthenticateFailure.GetType() == typeof(SecurityTokenExpiredException))
{
var authenticationException = context.AuthenticateFailure as SecurityTokenExpiredException;
context.Response.Headers.Add("x-token-expired", authenticationException.Expires.ToString("o"));
context.ErrorDescription = $"The token expired on {authenticationException.Expires.ToString("o")}";
}
return context.Response.WriteAsync(JsonSerializer.Serialize(new
{
error = context.Error,
error_description = context.ErrorDescription
}));
}
};
});
source: https://sandrino.dev/blog/aspnet-core-5-jwt-authorization#configuring-jwt-bearer-authentication
In my case, it was resolved by updating some nuget, specially Microsoft.IdentityModel.Tokens : https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/issues/1792

Related

asp.net core webapi JWT validation using authorization filter and JWKS

I am writing an webapi using .net core 6. It need to validate user Authorization JWT token using Authorisation filter and JWKS (keys). May I have any example. I have search google and lots of example are using middleware.
I would like to use Authorisation filter + JWKS
Token header contain below
{
"alg": "RS256",
"typ": "JWT",
**"kid": "asdfb6T82zfdfdfJwc2eruee"**
}
Please any sample code, example would be excellent.
I'm not familiar with jwks and I tried with jwt token and validate the token in filter as below:
public class JwTFilter : IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext context)
{
var jwt = context.HttpContext.Request.Headers["Authorization"].ToString();
var validateParameters = new TokenValidationParameters()
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("1234567890123456")),
ValidateIssuer = true,
ValidIssuer = "http://localhost:5000",
ValidateAudience = true,
ValidAudience = "http://localhost:5001",
ValidateLifetime = true,
ClockSkew = TimeSpan.FromMinutes(5)
};
var valiresult = ValidateToken(jwt, validateParameters);
if (valiresult == false)
{
context.HttpContext.Response.StatusCode = 401;
context.Result = new JsonResult(new errmessage() { statuecode = "401", err = "Auth Failed" });
}
}
private static bool ValidateToken(string token, TokenValidationParameters validationParameters)
{
var tokenHandler = new JwtSecurityTokenHandler();
try
{
tokenHandler.ValidateToken(token, validationParameters, out var validatedToken);
return true;
}
catch (Exception)
{
return false;
}
}
}
public class errmessage
{
public string statuecode { get; set; }
public string err { get; set; }
}
In startup class:
services.AddControllers(options =>
{
options.Filters.Add<JwTFilter>();
});
The ResulT:
Update:
Is it what you want?
public override void OnAuthorization(HttpActionContext context)
{
try
{
var access_token = context.Request.Headers.Authorization?.Parameter;
if (String.IsNullOrEmpty(access_token))
{
//401
context.HttpContext.Response.StatusCode = 401;
context.Result = new JsonResult(new errmessage() { statuecode = "401", err = "Auth Failed" });
}
else
{
//access the public key
var httpclient = new HttpClient();
var jwtKey = httpclient.GetStringAsync(somejwksendpoint).Result;
//chache jwks
var Ids4keys = JsonConvert.DeserializeObject<Ids4Keys>(jwtKey);
var jwk = Ids4keys.keys;
var parameters = new TokenValidationParameters
{
ValidIssuer = issUrl,
IssuerSigningKeys = jwk,
ValidateLifetime = true,
ValidAudience = apiName
};
var handler = new JwtSecurityTokenHandler();
//validate
var vali = handler.ValidateToken(access_token, parameters, out var _);
}
}
catch (Exception ex)
{
context.HttpContext.Response.StatusCode = 401;
context.Result = new JsonResult(new errmessage() { statuecode = "401", err = "Auth Failed" });
}
}
.....
public class Ids4Keys
{
public JsonWebKey[] keys { get; set; }
}

In AspBoilerPlate - Unauthorized error when calling from Angular when Windows Authentication is On

Have already raised this before and thought I have addressed it as per what suggested on THIS and THIS but seems not!
I am using ABP template (Angular and ASP .NET CORE Application) on Full .Net Framework. I simply want to use Windows Authentication to Authenticate user.
I added [Authorize] to the Authenticate in the TokenAuthController and have finally got the HttpContext.User.Identity.Name populated but only when I call the Authenticate from the Swagger (http://localhost:21021/swagger). But I am getting Unauthorized error when calling the method from Angular (login.service.ts):
POST http://localhost:21021/api/TokenAuth/Authenticate 401 (Unauthorized)
Here is the steps I have taken so far:
Changed launchSetting.json:
"iisSettings": {
"windowsAuthentication": true,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:21021/",
"sslPort": 0
}
},
Added ExternalAuthenticationSource:
public class WindowsAuthSource : DefaultExternalAuthenticationSource<Tenant, User>, ITransientDependency
{
public override string Name
{
get { return "Windows Authentication"; }
}
public override Task<bool> TryAuthenticateAsync(string userNameOrEmailAddress, string plainPassword, Tenant tenant)
{
return Task.FromResult(true);
}
}
Added it to CoreModule:
Configuration.Modules.Zero().UserManagement.ExternalAuthenticationSources.Add<WindowsAuthSource>();
4.Adjust AuthConfigurer:
services.AddAuthentication(opt => {
opt.DefaultScheme = IISDefaults.AuthenticationScheme;
opt.DefaultAuthenticateScheme = IISDefaults.AuthenticationScheme;
opt.DefaultChallengeScheme = IISDefaults.AuthenticationScheme;
});
Adjust StartUp.cs:
services.Configure<IISOptions>(iis =>
{
iis.AuthenticationDisplayName = "WINDOWS";
iis.AutomaticAuthentication = true;
});
Changed Authenticate method in the TokenAuthController:
public async Task<AuthenticateResultModel> Authenticate([FromBody]
AuthenticateModel model)
{
//var username = WindowsIdentity.GetCurrent().Name.Split('\\').Last();
var username = HttpContext.User.Identity.Name;
model.UserNameOrEmailAddress = username;
var loginResult = await GetLoginResultAsync(
model.UserNameOrEmailAddress,
model.Password,
null
);
var accessToken = CreateAccessToken(CreateJwtClaims(loginResult.Identity));
return new AuthenticateResultModel
{
AccessToken = accessToken,
EncryptedAccessToken = GetEncrpyedAccessToken(accessToken),
ExpireInSeconds = (int)_configuration.Expiration.TotalSeconds,
UserId = loginResult.User.Id
};
}
Sending dummy username and password from login.service.ts:
authenticate(finallyCallback?: () => void): void {
finallyCallback = finallyCallback || (() => { });
//Dummy data
this.authenticateModel.userNameOrEmailAddress = "DummyUsername";
this.authenticateModel.password = "DummyPassword";
this._tokenAuthService
.authenticate(this.authenticateModel)
.finally(finallyCallback)
.subscribe((result: AuthenticateResultModel) => {
this.processAuthenticateResult(result);
});
}

the tokenGetter method does not wait for the promise to be completed before attempting to process the token

I am using Jwt tokens for authentication and using a interceptor for adding access token to the requests.I have a getToken() method which is checking for token's validity and calling the service for getting new set of tokens. The method is returning promise but the requests are taking the promise before it gets completed and failing to get the updated token.
Below is my code:
export class TokenService {
refresh = false;
constructor(public injector: Injector) {
}
public getToken(): string | Promise<string> {
const jwtHelper = new JwtHelperService();
let token = localStorage.getItem('token');
let refreshToken = localStorage.getItem('refreshToken');
if (!token || !refreshToken) {
return null;
}
if (jwtHelper.isTokenExpired(token)) {
if (jwtHelper.isTokenExpired(refreshToken)) {
return null;
} else {
let tokenPromise;
if (!this.refresh) {
this.refresh = true;
tokenPromise = this.promiseFromObservable(this.getTokenService(localStorage.getItem('refreshToken')));
}
return tokenPromise;
}
} else {
return token;
}
}
getTokenService(refreshToken: string) {
let http = this.injector.get(HttpClient);
const httpOptions = {
headers: new HttpHeaders({
'Authorization': 'Bearer ' + refreshToken
})
};
return http.post<Tokens>(location.origin + '/LiveTime/services/v1/auth/tokens?locale=en', null, httpOptions);
}
promiseFromObservable(o): Promise<string> {
return new Promise((resolve, reject) => o.subscribe((token: Tokens) => resolve(token.token),reject(), err => { console.log(err); return null; }))
.then((token: Tokens) => {
localStorage.setItem('token', token.token);
localStorage.setItem('refreshToken', token.refreshToken);
this.refresh = false;
return token.token;
},
err => { console.log(err); return null; }
)
.catch((error) => { console.log(error);reject();
});
}
}
Can someone tell me what is wrong in this code?

Append firebase JWT in Ember-simple-auth

I am trying to do authorization in my Ember App(2.10). My workflow is
user hit the button of Facebook login then
i'm using torii to get the access token /my user database is on firebase/
Then i send token to firebase.auth with facebook provider. It returns JWT token.
Problem is i got the JWT token and now i have to login to my emberapp. I am trying to customize torii authenticator here. How can i implement this in ember app. Below is my authenticator:
authenticate() {
return this._super(...arguments).then((torii) => {
const serverTokenEndpoint = this.get('serverTokenEndpoint');
return this.get('ajax').request(serverTokenEndpoint, {
type: 'POST',
data: {
'type': torii.provider,
'client_id': this.client,
'token': torii.authorizationCode
}
}).then((token) => {
var provider = new firebase.auth.FacebookAuthProvider();
firebase.auth().signInWithPopup(provider).then(function(result) {
// This gives Facebook Access Token.
// JWT-token=result.user.Cd
// JWT-token.iat at=result.user.ea.Sa
// JWT-token-refresh = result.user.refreshToken
console.log(result)
// token = result.user.Cd;
// const expiresAt = this._absolutizeExpirationTime(result.user.ea.Sa);
token = Ember.assign(token, { 'expires_at': result.user.ea.Sa });
// this._scheduleAccessTokenRefresh(result.user.ea.Sa, expiresAt, result.user.refreshToken, torii);
return Ember.assign(token, {'torii': torii});
});
});
});
}
Check out this guide in the ESA repo. It covers torii and Github auth but the general concepts are the same for your use case.
#marcoow I did try this and it authenticate but when token is expired i can not refresh token.Seems it is not the right approach, How can i refresh token using firebase
export default ToriiAuthenticator.extend({
torii: Ember.inject.service(),
ajax: Ember.inject.service(),
refreshAccessTokens: true,
rejectWithResponse: false,
restore(data) {
return new RSVP.Promise((resolve, reject) => {
const now = (new Date()).getTime();
const refreshAccessTokens = this.get('refreshAccessTokens');
if (!isEmpty(data['expires_at']) && data['expires_at'] < now) {
// if (refreshAccessTokens) {
this._refreshAccessToken(data['expires_in'], data['refresh_token']).then(() => {
resolve();
}).catch(function(error) {
reject();
});
// } else {
// reject();
// }
} else {
if (!this._validate(data)) {
reject();
} else {
this._scheduleAccessTokenRefresh(data['expires_in'], data['expires_at'], data['refresh_token']);
resolve(data);
}
}
});
},
authenticate() {
return new Ember.RSVP.Promise((resolve, reject) => {
var provider = new firebase.auth.FacebookAuthProvider();
firebase.auth().signInWithPopup(provider).then((result) => {
var expires_in = this._absolutizeExpirationTime(result.user.ea.Sa);
var expiresAt = result.user.ea.Sa;
result = Ember.assign(result, { 'expires_at': expiresAt, 'expires_in': expires_in, 'access_token': result.user.Cd, 'refresh_token': result.refresh_token });
resolve(result)
});
// const useResponse = this.get('rejectWithResponse');
// const provider = new firebase.auth.FacebookAuthProvider();
// firebase.auth().signInWithPopup(provider).then((result) => {
// let expires_in = result.user.ea.Sa;
// const expiresAt = this._absolutizeExpirationTime(expires_in);
// this._scheduleAccessTokenRefresh(expires_in, expiresAt, result.refresh_token);
// if (!isEmpty(expiresAt)) {
// result = Ember.assign(result, { 'expires_at': expiresAt, 'expires_in': expires_in, 'access_token': result.user.Cd, 'refresh_token': result.refresh_token });
// }
// // resolve(result);
// }, (response) => {
// Ember.run(null, reject, useResponse ? response : response.responseJSON);
// }).catch(function(error) {
// console.log(error);
// });
});
},
invalidate(data) {
const serverTokenRevocationEndpoint = this.get('serverTokenRevocationEndpoint');
return new RSVP.Promise((resolve) => {
if (isEmpty(serverTokenRevocationEndpoint)) {
resolve();
} else {
if (!Ember.isEmpty(data.access_token)) {
delete data.access_token;
firebase.auth().signOut();
resolve();
}
}
});
},
_scheduleAccessTokenRefresh(expiresIn, expiresAt, refreshToken) {
console.log('sched')
const refreshAccessTokens = this.get('_refreshAccessTokens');
if (refreshAccessTokens) {
const now = (new Date()).getTime();
if (isEmpty(expiresAt) && !isEmpty(expiresIn)) {
expiresAt = new Date(now + expiresIn * 1000).getTime();
}
const offset = this.get('tokenRefreshOffset');
if (!isEmpty(refreshToken) && !isEmpty(expiresAt) && expiresAt > now - offset) {
run.cancel(this._refreshTokenTimeout);
delete this._refreshTokenTimeout;
if (!testing) {
this._refreshTokenTimeout = run.later(this, this._refreshAccessToken, expiresIn, refreshToken, expiresAt - now - offset);
}
}
}
},
_refreshAccessToken(expiresIn, refreshToken) {
console.log('refresh');
const data = { 'grant_type': 'refresh_token', 'refresh_token': refreshToken };
firebase.auth().currentUser.getToken(/ forceRefresh / true).then((response) => {
return new RSVP.Promise((resolve, reject) => {
// firebase.auth().currentUser.getToken(true).then((response) => {
expiresIn = response.user.ea.Sa || expiresIn;
refreshToken = response.refresh_token || refreshToken;
const expiresAt = this._absolutizeExpirationTime(expiresIn);
const data = assign(response, { 'expires_in': expiresIn, 'expires_at': expiresAt, 'refresh_token': refreshToken });
this._scheduleAccessTokenRefresh(expiresIn, null, refreshToken);
this.trigger('sessionDataUpdated', data);
resolve(data);
}, (response) => {
warn(`Access token could not be refreshed - server responded with ${response.responseJSON}.`);
reject();
});
});
},
_absolutizeExpirationTime(expiresIn) {
if (!isEmpty(expiresIn)) {
return new Date((new Date().getTime()) + expiresIn * 1000).getTime();
}
},
_validate(data) {
return !isEmpty(data['access_token']);
}
});

Can't get email or public profile facebook by ASP MVC5

I use MVC5 to get public profile user, but I just get ID and DisplayName of user. I have researched multi ways but have no result. This my setup snippet:
var options = new FacebookAuthenticationOptions
{
AppId = "xxx",
AppSecret = "xxx",
SignInAsAuthenticationType = "ExternalCookie",
Provider = new FacebookAuthenticationProvider
{
OnAuthenticated = async context =>
{
context.Identity.AddClaim(new System.Security.Claims.Claim("FacebookAccessToken", context.AccessToken));
}
}
};
options.Scope.Add("email");
options.Scope.Add("public_profile");
app.UseFacebookAuthentication(options);
And I call request result in action
public async Task<ActionResult> Index()
{
var userDisplayModel = new UserDisplayModel();
var authenticateResult = await AuthenticationManager.AuthenticateAsync("ExternalCookie");
if (authenticateResult != null)
{
userDisplayModel = new UserDisplayModel()
{
DisplayName = authenticateResult.Identity.Claims.FirstOrDefault(t => t.Type == ClaimTypes.Name).Value
};
}
return View(userDisplayModel);
}