Auth 0 configuration audience - jwt

I just found out that I have a problem with auth0 and it relates to the auth0 configuration audience. So when I explicitly write the audience, the JWT verification failed with error The provided Algorithm doesn't match the one defined in the JWT's Header. When I don't write the audience, everything will work fine, except now everytime the token expire and user click on login link it skip the login process and immediately logged in with the previous credential. I don't want this to happen, I want user to still authenticate themselves again after token expire, just like when I write the audience.
So what is audience and why does it affect the behaviour like this?
And How can I fix it to get the behaviour I wanted?
Below is the configuration of the Auth0
auth0 = new auth0.WebAuth({
clientID: environment.auth0ClientId,
domain: environment.auth0Domain,
responseType: 'token id_token',
//Below is the audience I'm talking about
audience: '${constants.MY_APP}/userinfo',
redirectUri: `${constants.ORIGIN_URL}/auth`,
scope: 'openid email'
});
I need to know how I can make the JWT to be verified correctly as well as make the login behaviour correctly when the JWT expire.

Auth0 can issue two types of tokens: opaque and JWT.
When you specify the audience parameter, you will receive a JWT token. JWTs differ from opaque tokens in that they are self-contained and therefore you verify them directly in your application.
In this case, the JWT you have received is signed with an algorithm different to that which you've defined in your verification logic. You can decode the JWT using https://jwt.io and you can see which algorithm it was signed with in the alg attribute of the header.
You can also find out the signing algorithm your API uses in the Auth0 dashboard. Go APIs, click your API, click the Settings tab and then scroll to Token Setting. You will see it listed as the Signing Algorithm.
Judging by the error message, you are using the java-jwt library, in which case you will need change the signing algorithm accordingly per the steps outlined here: https://github.com/auth0/java-jwt#verify-a-token
For HS256:
try {
Algorithm algorithm = Algorithm.HMAC256("secret");
JWTVerifier verifier = JWT.require(algorithm)
.withIssuer("auth0")
.build(); //Reusable verifier instance
DecodedJWT jwt = verifier.verify(token);
} catch (JWTVerificationException exception){
//Invalid signature/claims
}
Where secret is your API's Signing Secret.
For RS256, it's a little more involved. You first need to decode the token to retrieve the kid (key ID) from the header:
String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXUyJ9.eyJpc3MiOiJhdXRoMCJ9.AbIJTDMFc7yUa5MhvcP03nJPyCPzZtQcGEp-zWfOkEE";
try {
DecodedJWT jwt = JWT.decode(token);
} catch (JWTDecodeException exception){
//Invalid token
}
You then need to construct a JwkProvider using the jwks-rsa-java library:
JwkProvider provider = new UrlJwkProvider("https://your-domain.auth0.com/");
Jwk jwk = provider.get(jwt.getKeyId());
Finally, you can use the public key retrieved from the JWKS and use it to verify the token:
RSAPublicKey publicKey = (RSAPublicKey) jwk.getPublicKey();
try {
Algorithm algorithm = Algorithm.RSA256(publicKey, null);
JWTVerifier verifier = JWT.require(algorithm)
.withIssuer("auth0")
.build(); //Reusable verifier instance
DecodedJWT jwt = verifier.verify(token);
} catch (JWTVerificationException exception) {
//Invalid signature/claims
}
Keep in mind that it's preferred to use RS256 over HS256 for the reasons outlined here: https://auth0.com/docs/apis#signing-algorithms
You may also find this article useful for detailed information on verifying tokens: https://auth0.com/docs/api-auth/tutorials/verify-access-token

Related

NestJS & Passport: Change JWT token on user password change?

I'm trying to reset/change the token (JWT) for users who changed their passwords on NestJS API with PassportJS. Here is a clean example of how this authorization works: https://docs.nestjs.com/security/authentication.
I want to generate a new token on password change to make sure, that on every device on which the user was logged in, after password change they will get unauthorized and so logged out.
This is how I handle the password change in users service:
async changePassword(uId: string, password: any) {
return await this.userRepository.createQueryBuilder()
.update(User)
.set({ password: await bcrypt.hash(password.value, 10) })
.where("userId = :userId", { userId: uId })
.execute();
}
There are no prebuild methods to do this I think. JwtService got only 5 methods: decode, sign (this one is used to generate a token), signAsync, verify and verifyAsync.
So how can I do this properly?
You'd need some sort of way to invalidate the JWT that was already given to the user. As you can't just do that to a token, generally (it's stateless, it holds its own validity) you'd need to create a JWT restrictlist in a database that you check the incoming JWT against. If the JWT is in the restrictlist, reject the request. Otherwise, let it through (if it's valid of course)

Signing JWT token with per-user key rather than application-wide

Normally, JWT tokens are signed with an application-wide secret or an asymmetric key pair. However, I am integrating into a system that uses a per-user secret that is in fact the salt of the password in the Users table (called private_key there).
I find this system a bit odd. It was apparently meant to make sure that if a user changed his password, the issued tokens would stop working. But it does kill the main advantage of JWT: for any other system to be able to accept the token without having to call the Auth service to validate it. In this case, decoding the token requires to decode it without validation, fetching the database user/private key and then validating it.
The .net code:
List<Claim> claims = new List<Claim>()
{
new Claim("UserUUID", user.UserUUID.ToString())
};
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(user.PrivateKey));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(
issuer: _configuration["JwtIssuer"],
audience: _configuration["JwtIssuer"],
claims: claims,
expires: expirationDate,
signingCredentials: creds);
return Ok(new
{
access_token = new JwtSecurityTokenHandler().WriteToken(token),
expires_on = expirationDate
});
Is this actually reasonable and if there is an issue other than validation issues, why is it bad ?
I have seen a similar question but it doesn't address whether this scheme is a good idea, bad idea or just useless burden.

Generate access token using JWT

I've been given access to an okta token endpoint. I would like to use this service to request a token. I was given a url, client id, client secret, scope and grant type. I can use postman to make a POST call to the url (/v1/token) and pass the above info (client id, client secret, scope and grant type) and I get an access token back.
I can easily make this call in java with RestTemplate or equivalent, but I would like to use an API that would manage the token for me.
I've found JJWT. All the examples I see out there show me how to create a JWT using JJWT. What I would like to do is to get my access token, but I'm not sure how to do that. I mean i get that JJWT is an API to create JWT, but then how can I use the JWT to get my access token?
Any help/clarification/direction is much appreciated.
We using JWT with the node.js, to create new Token jwt.sign(data, key) takes at least to an argument, the fist must be some credential like userId, email..., the second will be key to verify later. to verify the token is it valid we use jwt.verify(), the first argument is token (where the jwt.sing() give you) and the second is the key (where you provide when creating);
example:
Creating JWT token:
var jwt = require('jsonwebtoken');
cosnt token = jwt.sign({ email: 'test#test.com', userId: '993333' }, 'secretkey');
verifying Token:
try {
const decodedToken = jwt.verify(token, 'secretkey');
}
catch(err) {
throw new Error(err)
}
// once verified
conosole.log(decodedToken)
I found this post how to create and verify token using java, thanks!

Keycloak integration to external identity provider fails when validation tokens with JWKS URL

I'm configuring an external identity provider in my Keycloak instance and trying to get it to validate the tokens using a external JWKS URL. Using the converted PEM from JWKS works fine, the using the URL is not working.
The token validation fails upon login with the following message:
[org.keycloak.broker.oidc.AbstractOAuth2IdentityProvider] (default task-4) Failed to make identity provider oauth callback: org.keycloak.broker.provider.IdentityBrokerException: token signature validation failed
I debugged the Keycloak server get more on the problem and found a "problem" in class JWKSUtils:
/**
* #author Marek Posolda
*/
public class JWKSUtils {
//...
public static Map<String, KeyWrapper> getKeyWrappersForUse(JSONWebKeySet keySet, JWK.Use requestedUse) {
Map<String, KeyWrapper> result = new HashMap<>();
for (JWK jwk : keySet.getKeys()) {
JWKParser parser = JWKParser.create(jwk);
if (jwk.getPublicKeyUse().equals(requestedUse.asString()) && parser.isKeyTypeSupported(jwk.getKeyType())) {
KeyWrapper keyWrapper = new KeyWrapper();
keyWrapper.setKid(jwk.getKeyId());
keyWrapper.setAlgorithm(jwk.getAlgorithm());
keyWrapper.setType(jwk.getKeyType());
keyWrapper.setUse(getKeyUse(jwk.getPublicKeyUse()));
keyWrapper.setVerifyKey(parser.toPublicKey());
result.put(keyWrapper.getKid(), keyWrapper);
}
}
return result;
}
//...
}
The if fails with a NullPointerException because the call jwk.getPublicKeyUse() returns null.
But I found out that it's null because the JWKS URL returns a single key without the attribute use, which is optional according to the specification. [https://www.rfc-editor.org/rfc/rfc7517#section-4.2]
Keycloak only accepts JWKS URLs that return all keys with the attribute use defined. But the IdP I'm trying to connect does not return that attribute in the key.
Given that situation, to who should I file an issue, the IdP or to Keycloak? Or is there something I'm doing wrong in the configuration?
I filed an issue with Keycloak about this exact problem in August 2019.
Their answer:
Consuming keys without validating alg and use is dangerous as such
Keycloak requires these to be present.
In my case, I contacted the IdP and they were able to populate the "use" parameter. If that is not an option, then you're pretty much stuck with your workaround.

Can I get a consistent 'iss' value for a Google OpenIDConnect id_token?

I'm using Google's OpenIDConnect authentication, and I want to validate the JWT id_token returned from Google. However, the documentation seems inconsistent about what value Google returns for the iss (issuer) claim in the ID token.
One page says, "iss: always accounts.google.com", but another page says "The value of iss in the ID token is equal to accounts.google.com or https://accounts.google.com" and a comment in the example code further explains:
// If you retrieved the token on Android using the Play Services 8.3 API or newer, set
// the issuer to "https://accounts.google.com". Otherwise, set the issuer to
// "accounts.google.com". If you need to verify tokens from multiple sources, build
// a GoogleIdTokenVerifier for each issuer and try them both.
I have a server-side application, not an Android app, so I'm not using Play Services.
To further muddy the waters, the OpenIDConnect specification itself contains a note that:
Implementers may want to be aware that, as of the time of this writing, Google's deployed OpenID Connect implementation issues ID Tokens that omit the required https:// scheme prefix from the iss (issuer) Claim Value. Relying Party implementations wishing to work with Google will therefore need to have code to work around this, until such time as their implementation is updated. Any such workaround code should be written in a manner that will not break at such point Google adds the missing prefix to their issuer values.
That document is dated November 8, 2014. In the time since then, has Google standardized on an iss value, or do I really need to check for both of them? The comment above seems to indicate that only Play Services >=8.3 gets iss with https://, and everywhere else the value will be just accounts.google.com. Is that true?
You have to check both possibilities. This is what worked for me...
Decode the token to get the issuer. If the issuer is not equal to either one of https://accounts.google.com or accounts.google.com you can stop there. It's an invalid token.
If the issuer is equal to either of the above Google strings, then pass that same decoded issuer value forward to the verification step.
Following is the an implementation I wrote in JavaScript for some Node.js Express middleware:
function authorize(req, res, next) {
try {
var token = req.headers.authorization;
var decoded = jwt.decode(token, { complete: true });
var keyID = decoded.header.kid;
var algorithm = decoded.header.alg;
var pem = getPem(keyID);
var iss = decoded.payload.iss;
if (iss === 'accounts.google.com' || iss === 'https://accounts.google.com') {
var options = {
audience: CLIENT_ID,
issuer: iss,
algorithms: [algorithm]
}
jwt.verify(token, pem, options, function(err) {
if (err) {
res.writeHead(401);
res.end();
} else {
next();
}
});
} else {
res.writeHead(401);
res.end();
}
} catch (err) {
res.writeHead(401);
res.end();
}
}
Note this function uses jsonwebtoken and jwk-to-pem node modules. I ommitted details of the getPem function which ultimately converts a json web key to pem format.
To start with, I definitely agree that Google's documentation is a murky business.
There are a couple of different ways in which you can validate the integrity of the ID token on the server side (btw this is the page you're looking for):
"Manually" - constantly download Google's public keys, verify signature and then each and every field, including the iss one; the main advantage (albeit a small one in my opinion) I see here is that you can minimize the number of requests sent to Google).
"Automatically" - do a GET on Google's endpoint to verify this token - by far the simplest:
https://www.googleapis.com/oauth2/v3/tokeninfo?id_token={0}
Using a Google API Client Library - the overhead might not be worth it, C# doesn't have an official one etc.
I suggest you go with the 2nd option and let Google worry about the validation algorithm.