ASP.Net Core - JWT Authentication with WebAPI and MVC Frontend not working - asp.net-core

The Project consists of two Parts:
ASP.Net Core API
ASP.Net Core MVC Frontend
Basically, what I want to do is authentication via JWT. So the API issues JWT and the MVC Frontend uses Identity with the claims and roles declared in the JWT.
Startup.cs in the API:
private const string SecretKey = "my_Secret_Key";
private readonly SymmetricSecurityKey _signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(SecretKey));
#region JWT Auth
// jwt wire up
// Get options from app settings
var jwtAppSettingOptions = Configuration.GetSection(nameof(JwtIssuerOptions));
// Configure JwtIssuerOptions
services.Configure<JwtIssuerOptions>(options =>
{
options.Issuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)];
options.Audience = jwtAppSettingOptions[nameof(JwtIssuerOptions.Audience)];
options.SigningCredentials = new SigningCredentials(_signingKey, SecurityAlgorithms.HmacSha256);
});
var tokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)],
ValidateAudience = true,
ValidAudience = jwtAppSettingOptions[nameof(JwtIssuerOptions.Audience)],
ValidateIssuerSigningKey = true,
IssuerSigningKey = _signingKey,
RequireExpirationTime = false,
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero
};
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(configureOptions =>
{
configureOptions.ClaimsIssuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)];
configureOptions.TokenValidationParameters = tokenValidationParameters;
configureOptions.SaveToken = true;
});
// api user claim policy
services.AddAuthorization(options =>
{
options.AddPolicy(Constants.Policies.ApiAccess, policy => policy.RequireClaim(Constants.JwtClaimIdentifiers.Rol, Constants.JwtClaims.ApiAccess));
});
#endregion
JWT Generation:
public async Task<string> GenerateEncodedToken(string userName)
{
User user = _userManager.GetUserByUserName(userName);
List<string> userRoles = _userManager.GetRoles(user.Guid);
var claimsToEncode = new[]
{
new Claim(JwtRegisteredClaimNames.Sub, userName),
new Claim("web", user.WebId),
new Claim(JwtRegisteredClaimNames.Jti, await _jwtOptions.JtiGenerator()),
new Claim(JwtRegisteredClaimNames.Iat, ToUnixEpochDate(_jwtOptions.IssuedAt).ToString(), ClaimValueTypes.Integer64),
new Claim(Constants.JwtClaimIdentifiers.Rol,Constants.JwtClaims.ApiAccess),
};
// Create the JWT security token and encode it.
var jwt = new JwtSecurityToken(
issuer: _jwtOptions.Issuer,
audience: _jwtOptions.Audience,
claims: claimsToEncode,
notBefore: _jwtOptions.NotBefore,
expires: _jwtOptions.Expiration,
signingCredentials: _jwtOptions.SigningCredentials);
jwt.Payload.Add("roles", userRoles.ToArray());
var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);
return encodedJwt;
}
Authorization works like a charm with this in the API.
Now I want to do the following:
Implement the same in the Frontend, so that:
MVC Frontend receives Credentials, send them to the API, get Token, and Authorize with the Claims and Roles in the Token.
I tried several things, but none of them worked so far.
What do I have to insert in the Startup.cs in the Frontend so that Identity checks not against the secret key (which the Frontend is not allowed to have) but against a public key? Or do I have to implement a Endpoint in the API which validates the JWT remotely?

When you get the token in web client, you can store it in a session object and send that whenever you are requesting something from the webapi

Related

ASP.NET Core : how to use jwt token to authorize in client side?

I have 2 completely separate projects. The first project is an ASP.NET Core Web API (backend) project and the second project is an ASP.NET Core 6.0 MVC (frontend) project.
I want JWT token authentication to be performed after the user enters the information on the login page in frontend.
I can send the username and password from the frontend to the backend and the token is also generated. But I don't know how to authenticate through this token on the client side.
I use the following function to create a token in the Web API project and the token is generated successfully.
public string BuildToken(string key,string issuer, User user)
{
var claims = new[] {
new Claim(ClaimTypes.Name, user.UserName),
new Claim(ClaimTypes.Role, user.Role),
new Claim(ClaimTypes.NameIdentifier, Guid.NewGuid().ToString())
};
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key));
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256Signature);
var tokenDescriptor = new JwtSecurityToken(issuer, issuer, claims,
expires: DateTime.Now.AddMinutes(30), signingCredentials: credentials);
return new JwtSecurityTokenHandler().WriteToken(tokenDescriptor);
}
and in program.cs
builder.Services.AddAuthentication(auth =>
{
auth.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = builder.Configuration["Jwt:Issuer"],
ValidAudience = builder.Configuration["Jwt:Issuer"],
IssuerSigningKey = new
SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"]))
};
});

Check if user exists in ASP.NET Core WebAPI JWT Authentication

I have succesfully setup JWT authentication/authorization in my WebAPI, but there's one problem: I can create a new user account, generate it's JWT token, then delete the account while the token is still valid.
How and where should I check if the user associated with the token actually exists before authorizing?
Here's my code to setup JWT (Startup.cs):
var secretKey = Configuration.GetValue<string>("SecretKey");
var symmetricKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretKey));
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateIssuerSigningKey = true,
ValidIssuer = "localhost",
ValidAudience = "localhost",
IssuerSigningKey = symmetricKey
};
});
I'm using the [Authorize] attribute on my controllers and the user ID is in the JWT token.
Thanks in advance!
You can also validate the user in AddJwtBearer events :
options.Events = new JwtBearerEvents()
{
OnTokenValidated = context =>
{
//get userid if type is "userid"
var userid = context.Principal.Claims.Where(x => x.Type == "userid").FirstOrDefault().Value;
if (true )
{
context.Fail("invaild token");
}
return Task.CompletedTask;
},
};
If you want to check database in that event , you can use dependency inject to get db context like :
var dbcontext = context.HttpContext.RequestServices.GetRequiredService<ApplicationDbContext>();

Automatic login using jwt in Cookie in ASP.net MVC Core

My process flow is :
User logs into a Issuer Application (Username/Password)
Clicks a link of the Client Application that they want to goto
Issuer Application creates a jwt and stores it in a Cookie
Issuer Application does a Response.Redirect to Client Application
Client Application authenticates user using the jwt in the Cookie and creates the Principal and automatically logs in user.
Below is my Client Application setting from the Startup ConfigureServices method:
var key = new SymmetricSecurityKey(Encoding.ASCII.GetBytes("password"));
SigningCredentials SigningCredentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var tokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = false,
ValidIssuer = "issuerapp",
ValidateAudience = false,
ValidAudience = "clientapp",
ValidateIssuerSigningKey = true,
IssuerSigningKey = SigningCredentials.Key,
RequireExpirationTime = false,
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero
};
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.ClaimsIssuer = "issuerapp";
options.TokenValidationParameters = tokenValidationParameters;
options.SaveToken = true;
})
.AddCookie(JwtBearerDefaults.AuthenticationScheme,
options =>
{
options.Cookie.HttpOnly = true;
options.Cookie.Name = Configuration.GetValue<string>("AppSettings:CookieName");
options.AccessDeniedPath = authenticationSettings.AccessDeniedPath;
options.Events = new CookieAuthenticationEvents
{
// Check if JWT needs refreshed
OnValidatePrincipal = RefreshTokenMonitor.ValidateAsync,
OnSigningOut = (context) =>
{
context.HttpContext.Response.Redirect(Configuration.GetValue<string>("AppSettings:LogoutPath"));
return Task.CompletedTask;
},
};
});
In my Client Application I have all controllers decorated with [Authorize] attribute.
I need the Client Application automatically authenticating the user using the jwt. Which is not happening using the above mentioned configurations.
My AccessDeniedPath (Action Method) is not getting hit either.
The workaround that I have been using is to redirect user from the Issuer Applicaiton to a Login action in the Client Application where :
I read the jwt from the Cookie
Validate the jwt to get the Principal
Call httpContext.SignInAsync
How can I get the user logged in automatically using the jwt.
Any help / pointer are appreciated.
Thanks.
By default , the AddJwtBearer extension will get the token from request's Authorization header :
Authorization: Bearer <token>
But you are pass the token in cookie , so you can find the token in cookie and set token in OnMessageReceived event in AddJwtBearer :
options.Events = new JwtBearerEvents {
OnMessageReceived = ctx =>
{
ctx.Token = ctx.HttpContext.Request.Cookies["jwt"];
return Task.CompletedTask;
}
};

Multiple JWT bearer authentication in .net core 2.1 - Claims issue

Project: .net core 2.1 APIs
In my project I have a requirement to include 2 JWT bearer authentication.
a) We create token JWT internally and use it for authentication
b) We get JWT token from external third party and need to get this authenticated as well.
I tried following code in start up:
services.AddAuthentication( )
.AddJwtBearer("InteralBearer", options =>
{
SymmetricSecurityKey key = TokenGenerator.GenerateKey();
options.Audience = "***************";
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = "***************",
ValidateAudience = true,
ValidAudience = "***************",
ValidateIssuerSigningKey = true,
IssuerSigningKey = key,
ValidateLifetime = true
};
})
.AddJwtBearer("ExternalBearer", options =>
{
options.Audience = "***************";
options.Authority = "***************";
});
services.AddAuthorization(options =>
{
options.DefaultPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.AddAuthenticationSchemes("ExternalBearer", "InteralBearer")
.Build();
options.AddPolicy("Applicant", new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.AddAuthenticationSchemes("ExternalBearer", "InteralBearer")
.RequireClaim("role", "Applicant")
.Build());
});
In my controller I have:
[ApiController]
[Authorize(Policy = "Applicant")]
public class ApplicantController : ApplicantAbstract
{
}
I also have custom autorization filter:
public class SelfAuthorizationFilter : IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext context)
{
ClaimsPrincipal principal = context.HttpContext.User;
........
}
}
When I above set up, issue is, context.HttpContext.User does not return any claims as part of "Identity" object in the request. I am expecting "Claims" object to have different claims which is already configured.
Every thing works fine if I have either "InternalBearer" or "ExternalBearer", but not both.
What am I doing wrong here?

JwtBearer asp net core get signing key from api

Is it possible to configure JwtBearer from asp.net-core that it can take signing key (in my case public key) required to verify is user is authorized?
I have somehing like this:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(o =>
{
o.BackchannelHttpHandler = new HttpClientHandler();
o.MetadataAddress = "http://auth-server.local.com/api";
o.Authority = "http://localhost:5000";
o.Audience = "http://localhost:5001";
o.RequireHttpsMetadata = false;
o.TokenValidationParameters = new TokenValidationParameters
{
IssuerSigningKey = "here should be public key exposed by my auth api server"
ValidIssuer = "http://localhost:5000",
ValidAudience = "http://loclhost:5001"
};
})
;
but my client does not call my auth api in order to obtain public key.
The code you have there just tells your applications that you want to USE JWT Tokens for authentication and what parameters to validate incoming requests (with tokens) with.
You need to setup and endpoint now to issue those tokens.. or "public key" as you put it.
Your code (notice the "mysecret" in the issuer)
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(o =>
{
o.BackchannelHttpHandler = new HttpClientHandler();
o.MetadataAddress = "http://auth-server.local.com/api";
o.Authority = "http://localhost:5000";
o.Audience = "http://localhost:5001";
o.RequireHttpsMetadata = false;
o.TokenValidationParameters = new TokenValidationParameters
{
IssuerSigningKey = "MySecret"
ValidIssuer = "http://localhost:5000",
ValidAudience = "http://loclhost:5001"
};
})
;
Now in a controller:
public class AccountController : Controller
{
[HttpGet]
public IActionResult getKey()
{
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("MySecret"));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(
issuer: "http://localhost:5000",
audience: "http://localhost:5001",
expires: DateTime.Now.AddYears(10),
signingCredentials: creds);
return Json(token);
}
}
Notice how in the services.. You set the private key to "MySecret" - this tells the application that any token used with a request.. must be signed with this value. Otherwise it rejects it.
In the controller.. We create a key with the same "MySecret" and issue it at host/account/getkey
Now - just add the [Authorize] tag to any function or controller you want to protect.
[Authorize]
[HttpPost]
public IActionResult Test()
{
}
EDIT: It appears you want some kind of permanent token. Just set the expires field in the new JWTtoken() line to expire in 1000 years or whatever and publicly broadcast that to whomever you want. Albiet - this is an insecure authorization model.