I'm using Firebase with a Email & Password authentication. Once the user has been signed in successfully I'll receive an AuthData object which contains a token.
I wanna send this token to my backend, verify it and extract the uid from it - unfortunately I don't know how to do this.
I'm aware of the Firebase secret and if I go to jwt.io, enter the token and the secret the signature is verified and I see the correct payload - so this actually works.
Since I'm running Java on my backend I've been using jjwt for the decryption process unfortunately it always throws a SignatureException:
io.jsonwebtoken.SignatureException: JWT signature does not match locally computed signature. JWT validity cannot be asserted and should not be trusted.
Following the code I've been using:
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
byte[] encodedKey = Base64.decode("my-firebase-secret", Base64.DEFAULT);
Key k = new SecretKeySpec(encodedKey, signatureAlgorithm.getJcaName());
Claims claims = Jwts.parser()
.setSigningKey(k)
.parseClaimsJws("the-token").getBody();
I've also tried it with the following snippet:
Claims claims = Jwts.parser()
.setSigningKey(DatatypeConverter.parseBase64Binary("my-firebase-secret"))
.parseClaimsJws(jwt).getBody();
But got the same exception. So what am I doing wrong? Thanks in advance.
Have you tried to use getBytes instead of parseBase64Binary? Here is the example:
Claims claims = Jwts.parser().setSigningKey("my-firebase-secret".getBytes("UTF-8")).parseClaimsJws(jwt).getBody();
Related
Connecting to Azure AD v2.0 endpoint I cannot validate my token which jwt.io and jwt.ms declare as valid
A public token is no problem:
IDTokenValidator validator = new IDTokenValidator(issuer, clientId, JWSAlgorithm.RS256, jwkSetUrl);
IDTokenClaimsSet validatedClaimsSet;
try {
validatedClaimsSet = validator.validate(jwt, null);
} catch (BadJOSEException | JOSEException e) {
LOGGER.error(e.getMessage());
return;
}
but validating a token coming from a confidential client will throw the exception
Signed JWT rejected: Another algorithm expected, or no matching key(s) found
with the following code
IDTokenValidator validator = new IDTokenValidator(issuer, clientId, JWSAlgorithm.RS256, new Secret(authMethod.getSecret()));
No matching keys are found because (JWKMatcher.java:1258)
keytype does not match: OCT instead of RSA
use is null
ops is null
It seems an inadequate JWKSet is created from the secret in ImmutableSecret.java:47 but I seem to lack understanding as to how the secret can contain the necessary data found in the jwks_uri from Azure
Any advice would be appreciated
Validating an ID token works the same way for both public and confidential clients. So just get rid of the secret parameter and your code will work - using the token's signing public key.
The secret parameter is for JWTs that are signed with a symmetric algorithm such as HMACSHA256 - which is almost never a good idea. More about this topic in JWT Security Best Practices.
I created a jwt token using java keystore public key an and io.jsonwebtoken library.
After generation i copy pasted the generated token in https://jwt.io website. It decoded my token without using private key.How come this is possible?
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
PrivateKey privateKey = pair.getPrivate();
Claims claims = Jwts.claims().setSubject(userName);
claims.put("scopes", scopes);
String token = Jwts.builder()
.setClaims(claims)
.signWith(SignatureAlgorithm.RS256, privateKey)
.compact();
Your JWT is only signed, not encrypted. The main security feature of the incoming JWT is that it has a checksum/signature at the end. Your Java program has the ability to verify that the checksum matches the actual content of the JWT (e.g. headers and claims). If the checksum does not match, then the server will assume the JWT has been tampered with and reject it. The main use of a JWT is not so much protecting critical information as it is about controlling authorization and authentication in your application.
I am using
JwtBuilder builder = Jwts.builder()
.setId(user.getEmail())
.signWith(signatureAlgorithm, signingKey);
to create a token then
Jwts.parser().setSigningKey(secret).parse(token);
to authenticate. When I run this in a JUnit test, it works fine. However, when I authenticate token passed as a header over REST call, authentication fails with SignatureException. I have verified the token on both ends of the HTTP call and the token string is identical. Code to create/authenticate is static, therefore, the secret is same on each side.
static Key secret = MacProvider.generateKey(); will generate a new random key each time your server is reloaded because static variables are initialized when the class is loaded
It means that if you issue a JWT, it is only valid as long as the server does not reboot. The SignatureException you got is because the signing key it is different
You need to store the signing key secret.getEncoded() after first generation and load it when your module starts
I have had the same problem, I noticed that in sources whenever they convert the signing key they explicitly specify UTF-8 encoding. I tried changing the encoding while both decoding the token:
private Jws<Claims> decodeToken(String token) {
return Jwts.parser()
.setSigningKey(securityProperties.getTokenSecret().getBytes(Charset.forName("UTF-8")))
.parseClaimsJws(token);
}
And when signing the token:
private String getSignedToken(UserDetailsAdapter user, List<String> roles, byte[] signingKey) {
return Jwts.builder()
.signWith(Keys.hmacShaKeyFor(signingKey), SignatureAlgorithm.HS512)
.setHeaderParam("typ", securityProperties.getTokenType())
.setIssuer(guiServerSecurityProperties.getTokenIssuer())
.setAudience(guiServerSecurityProperties.getTokenAudience())
.setSubject(user.getUsername())
.setExpiration(new Date(System.currentTimeMillis() + 864000000))
.claim("rol", roles)
.compact();
}
This is the only thing that fixed this for me.
I had a similar problem. In my case it was wrong token validation. I set sign as bytes:
.signWith(SignatureAlgorithm.HS512, jwtConfig.getSecret().getBytes())
But when i was parsing the token and setting signKey i setted it as a String, not as bytes:
Jwts.parser().setSigningKey(signingKey).parseClaimsJws(this.token)
Also always check quotes and spaces when checking token, there often can be excess space/quote in the start/end of the token (use trim() method)
I had a similar problem. In my case both keys were the same, but for some reason I was receiving a token within quotes (e.g "Syasda.da3das.aDjty6" instead of just Syasda.da3das.aDjty6).
It took me quite some time to realize this since most of the time while testing on jwt.io I would just copy the token manually without the brackets to verify it.
token = token.replace("\"","");
Removing those quotes solved the problem for me. Hopefully this will help someone else as well.
I solved the problem modifying the HOST in the URL REST endpoint. It had a wrong host which was returned error HTTP 401 unauthorized.
Two types of problems meight exists:
Key key = Keys.secretKeyFor(SignatureAlgorithm.HS256); generates a different random key each time the function secretKeyFor is run. So if you want to generate a constant key, try this one
public SecretKey generalKey(){
String stringKey = Global.JWT_SECRET;
byte[] encodedKey =Base64.decodeBase64(stringKey);
SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length,
"HmacSHA512");
return key;
}
In my code, I recieve the token transferred by ajax, and JSON.stringify() is used to convert the javasript object to string, which wrapped additional quota on the origin string, try to eliminate the quota as Nuper said.
I am using the jjwt Java library for server side generation of jwt in on servlets, the code snipper below straight from the jjwt GitHub page https://github.com/jwtk/jjwt generates and prints out this token.
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJKb2UifQ.XIKER3owR8BS3Krhsksg9INh9VBSejdn_qN-ONtPans
String compactJws = Jwts.builder()
.setSubject("Joe")
.signWith(SignatureAlgorithm.HS256, "secret")
.compact();
PrintWriter out = response.getWriter();
out.println(compactJws);
However, when I try to verify this token on jwt.io's debugger, it fails the signature check.
Both checking and unchecking secret base64 encoded didn't work
Am I using the library wrongly?
Try with secr and check the base64 option :)
It is due to .signWith(SignatureAlgorithm.HS256, "secret"). It is implemented by DefaultJwtBuilder class
public JwtBuilder signWith(SignatureAlgorithm alg, String base64EncodedSecretKey)
This method assumes that you are providing a key in base64 and secret is not base64. When the method decodes from base64 to byte[] the java converter used by jjwt provides a representation of the string secr which is different to the JavaScript decoder used at jwt.io
You can test yourself with
System.out.println(
javax.xml.bind.DatatypeConverter.printBase64Binary(
javax.xml.bind.DatatypeConverter.parseBase64Binary("secret")));
I recently got to know about Json Web Token (JWT). Since I liked how it works I have started to implement it on my project. My project involves two apps to communicate. One is an android app and the other is Laravel web application.
The mobile app logs in after the user credential is authenticated from the server side.
I have sent the username and password to server from the mobile app and I have got the JWT in string format. But from this point onward I couldn't find a way to collect the JWT content.
I have gone through almost all possible shown (googled results) but I couldn't manage to get the contents, signature and header.
One of the method I have got a little bit further with, was using the following code, notice I have removed the setSigningKey():
try {
Claims claims = Jwts.parser().parseClaimsJwt(jwtHeaderAndClaim).getBody();
System.out.println("ID of the claims: " + claims.getId().toString());
}catch (Exception e){
Log.e("Exception: ", e.toString());
}
The above code generates the following error:
Exception: io.jsonwebtoken.PrematureJwtException: JWT must not be accepted before 2016-06-14T10:20:09+0300. Current time: 2016-06-14T10:19:37+0300´
the jwtHeaderAndClaim is the JWT String after removing the signature part only (i.e: "xxxxxx.yyyyyyyy."). if i put the jwtString (xxxxxxx.yyyyyyyy.ccccccc) instead of jwtHeaderAndClaim the following error will occur:
Exception: io.jsonwebtoken.UnsupportedJwtException: Signed JWSs are not supported
If I put the setSigningKey as shown in stormpath example:
Claims claims = Jwts.parser().setSigningKey(DatatypeConverter.parseBase64Binary(apiKey.getSecret())).parseClaimsJwt(jwtString).getBody();.
The above code will not work for two reasons:
1. I don't have the library
import javax.xml.bind.DatatypeConverter;
2. I don't know how to get the key.
But know that I don't need the key since this time I am trying to login and collect the user information's (like firstname, lastname, phone, etc), and the signature (token) so that the next time I send data to be stored to the server side I have the token to get access to the backend.
Can anyone please help me?
You have many questions. I try to answer some of them
io.jsonwebtoken.PrematureJwtException: JWT must not be accepted before
2016-06-14T10:20:09+0300. Current time: 2016-06-14T10:19:37+0300´
You are using nbf (not before) attribute in JWT. Do not use it (it is optional) or sets a range of validity given that the clocks of the devices will not be synchronized
From RFC 7519
The "nbf" (not before) claim identifies the time before which the JWT
MUST NOT be accepted for processing. The processing of the "nbf" claim requires that the current date/time MUST be after or equal to the not-before date/time listed in the "nbf" claim. Implementers MAY provide for some small leeway, usually no more than a few minutes, to account for clock skew. Its value MUST be a number containing a NumericDate value. Use of this claim is OPTIONAL.
Signed JWS
Exception: io.jsonwebtoken.UnsupportedJwtException: Signed JWSs are
not supported
Do you want to validate the signing key at client side or at server side?
If you use the JWT for authentication replacing user & password, and you are sending token in each request, you can validate the signature at server side.
If you want to validate the key on the app, do not use a symmetric key, because it could be a big vulnerability if it fell into the wrong hands. See. You can use and asymmetric key pair. Sign the JWT in server with the private key and validate on device with public key.
I don't have the library import javax.xml.bind.DatatypeConverter
String base64 = Base64.encodeToString(data, Base64.DEFAULT);
byte[] data = Base64.decode(base64, Base64.DEFAULT);
I don't know how to get the key.
Your key probably was generated on server side in this way
Key key = MacProvider.generateKey(SignatureAlgorithm.HS256);
byte data[] = key.getEncoded();
Make available the key data[] to client in the way you prefer. Using assymetric keys, you only need to make available the public key.
KeyPair keyPair = RsaProvider.generateKeyPair();
byte data[] = keyPair.getPublic().getEncoded();