I have a JWT with the secret key :
private final static Algorithm ALGORITHM = Algorithm.HMAC256("secret");
public String createToken(#NonNull String username) {
Timestamp timestamp = Timestamp.from(Instant.from(ZonedDateTime.now(ZoneId.of("Z"))));
Timestamp expTime = Timestamp.from(Instant.from(ZonedDateTime.now(ZoneId.of("Z")).plusMinutes(10)));
try {
String token = JWT.create()
.withIssuer("auth0")
.withClaim("username", username)
.withClaim("time", timestamp).withExpiresAt(expTime)
.sign(ALGORITHM);
return token;
and the verify method:
public DecodedJWT verifyToken(String token) {
DecodedJWT decodedJWT = JWT.decode(token);
try {
JWTVerifier verifier = JWT
.require(ALGORITHM)
.withIssuer("auth0")
.build();
DecodedJWT jwt = verifier.verify(token);
return jwt;
} catch (JWTVerificationException exception) {
System.out.println("token not verified");
}
My problem is that the token returned from the method has an invaild signature as by https://jwt.io/ . Also the verify method is not working because of that.
I read some blogs in which they said that you have to encode your secret, so I tried it like this:
private final static Algorithm ALGORITHM = Algorithm.HMAC256(Base64.getEncoder().encodeToString("secret".getBytes()));
but it didnt work the signature was also invalid. Has anyone an idea on how can I fix that?
Thanks in advance
Related
I am trying to generate token using MSAL4j-1.8 jar in my Java application.
Below is the code I am using :
private static IAuthenticationResult getAccessTokenByClientCredentialGrant() throws Exception {
ConfidentialClientApplication app = ConfidentialClientApplication.builder(
clientId,
ClientCredentialFactory.createFromSecret(secret))
.authority(authority)
.build();
// With client credentials flows the scope is ALWAYS of the shape "resource/.default", as the
// application permissions need to be set statically (in the portal), and then granted by a tenant administrator
ClientCredentialParameters clientCredentialParam = ClientCredentialParameters.builder(
Collections.singleton(scope))
.build();
CompletableFuture<IAuthenticationResult> future = app.acquireToken(clientCredentialParam);
return future.get();
}
I get an error :
Caused by: com.microsoft.aad.msal4j.MsalServiceException: AADSTS50049: Unknown or invalid instance.
Trace ID: c6a936bf-2b0f-489e-ada3-d2311e708500
Correlation ID: f515dd78-7915-43d7-9020-62631f27c955
Timestamp: 2020-12-10 16:14:09Z
at com.microsoft.aad.msal4j.AadInstanceDiscoveryProvider.validate(AadInstanceDiscoveryProvider.java:147)
at com.microsoft.aad.msal4j.AadInstanceDiscoveryProvider.doInstanceDiscoveryAndCache(AadInstanceDiscoveryProvider.java:138)
at com.microsoft.aad.msal4j.AadInstanceDiscoveryProvider.getMetadataEntry(AadInstanceDiscoveryProvider.java:42)
at com.microsoft.aad.msal4j.AuthenticationResultSupplier.getAuthorityWithPrefNetworkHost(AuthenticationResultSupplier.java:32)
at com.microsoft.aad.msal4j.AcquireTokenByAuthorizationGrantSupplier.execute(AcquireTokenByAuthorizationGrantSupplier.java:59)
at com.microsoft.aad.msal4j.AuthenticationResultSupplier.get(AuthenticationResultSupplier.java:59)
at com.microsoft.aad.msal4j.AuthenticationResultSupplier.get(AuthenticationResultSupplier.java:17)
at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1601)
at java.lang.Thread.run(Thread.java:818)
Any idea on what can be the problem?
As #juunas asked, seems there is something wrong with your authority setting.
Try the code below:
import java.util.Collections;
import java.util.concurrent.CompletableFuture;
import com.microsoft.aad.msal4j.ClientCredentialFactory;
import com.microsoft.aad.msal4j.ClientCredentialParameters;
import com.microsoft.aad.msal4j.ConfidentialClientApplication;
import com.microsoft.aad.msal4j.IAuthenticationResult;
public class AADClientCred {
public static void main(String[] args) {
String tenantID = "<your tenant ID or name>";
String clientID = "<your Azure AD app id>";
String Secret = "<your azure ad app secret>";
String authority = "https://login.microsoftonline.com/" + tenantID;
//I use microsoft graph as resource for demo
String scope = "https://graph.microsoft.com/.default";
try {
String access_token = getAccessTokenByClientCredentialGrant(clientID, Secret, authority, scope)
.accessToken();
System.out.println("access token : " + access_token);
} catch (Exception e) {
e.printStackTrace();
}
}
private static IAuthenticationResult getAccessTokenByClientCredentialGrant(String clientID, String Secret,
String authority, String scope) throws Exception {
ConfidentialClientApplication app = ConfidentialClientApplication
.builder(clientID, ClientCredentialFactory.createFromSecret(Secret)).authority(authority).build();
ClientCredentialParameters clientCredentialParam = ClientCredentialParameters
.builder(Collections.singleton(scope)).build();
CompletableFuture<IAuthenticationResult> future = app.acquireToken(clientCredentialParam);
return future.get();
}
}
Result:
Let me know if you have any further questions.
Here's my Java code, which is generating tokens:
private static final GzipCompressionCodec COMPRESSION_CODEC = new GzipCompressionCodec();
private String issuer;
private int expirationSec;
private int clockSkewSec;
private String secretKey;
//constructor, some other methods
public String createToken(final Map<String, String> attributes) {
Date now = new Date();
Date exp = new Date(System.currentTimeMillis() + (1000 * expirationSec));
Claims claims = Jwts.claims();
claims.putAll(attributes);
return Jwts
.builder()
.setClaims(claims)
.setIssuer(issuer)
.signWith(HS256, secretKey)
.compressWith(COMPRESSION_CODEC)
.setIssuedAt(now)
.setExpiration(exp)
.compact();
}
I can decode this token in Java without any problems, but when I try to decode it with https://jwt.io I get "Invalid Signature" error. I need to extract token expiration date. I've also tried to generate token without compression, but also fails.
#Test
public void getUsers_Success() throws Exception {
when(userRepository.findAll()).thenReturn(userList.getUsers());
String token = TokenAuthenticationService.createToken("me#me.com");
assertNotNull(token);
RequestBuilder request = MockMvcRequestBuilders
.get("/api/v1/users")
.header("Authorization", "Bearer "+ token);
MvcResult result = mockMvc.perform(request)
.andExpect(status().isOk())
.andExpect(content().json(usersJson))
.andReturn();
result.getResponse().getContentAsString();
}
Here is the token generation
public static String createToken(String username) {
String jwt = Jwts.builder()
.setSubject(username)
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATIONTIME))
.signWith(SignatureAlgorithm.HS512, SECRET)
.compact();
return jwt;
}
When I run this test, the response is always empty. And then the json parsing fails because the response is empty.
The method signWith(SignatureAlgorithm.HS512, SECRET) is deprecated and will be removed in future releases. You should use :
signWith(SECRET, SignatureAlgorithm.HS512)
I suppose SECRET is a String object => you need to generate secret key based on your secret:
signWith(Keys.hmacShaKeyFor(SECRET.getBytes()), SignatureAlgorithm.HS512)
I'm using spring boot to build a simple auth process for my app.
I have AuthorizationServerConfig & ResourceServerConfig setup, my frontend is a SPA. When I hit /oauth/token route, I got a JWT back which I previously stored in localStorage, and when I try to hit the resource server route, I have authorization header setup with this JWT, everything works.
But now I want to do authorization with JWT stored in the cookie, how I can config it so that it works with my current authorization/resource server config? I googled for a while and the best I can find is to set up a customize token extractor, but I'm not sure how to get it right, thank you in advance.
-------------- update --------------
I have #EnableAuthorizationServer and #EnableResourceServer on, and the EnableResourceServer setup an OAuthAuthenticationProcessingFilter automatically, this filter user bearer header authentication which uses a bearer token extractor to extract from the request header, I looked at the source code, it's hardcoded into the library, how I can customize this filter to extract JWT from the cookie?
Read cookie value from the request object and parse jwt manually.
Here is sample code
public Jws<Claims> parseJWT(HttpServletRequest request) {
Cookie cookie = WebUtils.getCookie(request, "Token cookie name");
if(cookie == null) {
throw new SecurityException("Token not found from cookies");
}
String token = cookie.getValue();
return Jwts.parser().setSigningKey("your signing Key").parseClaimsJws(token);
}
you can create request filter and check jwt.
There are many implementation for JWT. Am using this. io.jsonwebtoken
I am adding a Token Helper Class which has methods to validate, generate, refresh token. You can focus on the JWT extraction part.
Jar Dependency
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>
JWT Helper Class. It contains methods to validate, refresh, generate token.
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import com.test.dfx.common.TimeProvider;
import com.test.dfx.model.LicenseDetail;
import com.test.dfx.model.User;
#Component
public class TokenHelper {
protected final Log LOGGER = LogFactory.getLog(getClass());
#Value("${app.name}")
private String APP_NAME;
#Value("${jwt.secret}")
public String SECRET; // Secret key used to generate Key. Am getting it from propertyfile
#Value("${jwt.expires_in}")
private int EXPIRES_IN; // can specify time for token to expire.
#Value("${jwt.header}")
private String AUTH_HEADER;
#Autowired
TimeProvider timeProvider;
private SignatureAlgorithm SIGNATURE_ALGORITHM = SignatureAlgorithm.HS512; // JWT Algorithm for encryption
public Date getIssuedAtDateFromToken(String token) {
Date issueAt;
try {
final Claims claims = this.getAllClaimsFromToken(token);
issueAt = claims.getIssuedAt();
} catch (Exception e) {
LOGGER.error("Could not get IssuedDate from passed token");
issueAt = null;
}
return issueAt;
}
public String getAudienceFromToken(String token) {
String audience;
try {
final Claims claims = this.getAllClaimsFromToken(token);
audience = claims.getAudience();
} catch (Exception e) {
LOGGER.error("Could not get Audience from passed token");
audience = null;
}
return audience;
}
public String refreshToken(String token) {
String refreshedToken;
Date a = timeProvider.now();
try {
final Claims claims = this.getAllClaimsFromToken(token);
claims.setIssuedAt(a);
refreshedToken = Jwts.builder()
.setClaims(claims)
.setExpiration(generateExpirationDate())
.signWith( SIGNATURE_ALGORITHM, SECRET )
.compact();
} catch (Exception e) {
LOGGER.error("Could not generate Refresh Token from passed token");
refreshedToken = null;
}
return refreshedToken;
}
public String generateToken(String username) {
String audience = generateAudience();
return Jwts.builder()
.setIssuer( APP_NAME )
.setSubject(username)
.setAudience(audience)
.setIssuedAt(timeProvider.now())
.setExpiration(generateExpirationDate())
.signWith( SIGNATURE_ALGORITHM, SECRET )
.compact();
}
private Claims getAllClaimsFromToken(String token) {
Claims claims;
try {
claims = Jwts.parser()
.setSigningKey(SECRET)
.parseClaimsJws(token)
.getBody();
} catch (Exception e) {
LOGGER.error("Could not get all claims Token from passed token");
claims = null;
}
return claims;
}
private Date generateExpirationDate() {
long expiresIn = EXPIRES_IN;
return new Date(timeProvider.now().getTime() + expiresIn * 1000);
}
public int getExpiredIn() {
return EXPIRES_IN;
}
public Boolean validateToken(String token, UserDetails userDetails) {
User user = (User) userDetails;
final String username = getUsernameFromToken(token);
final Date created = getIssuedAtDateFromToken(token);
return (
username != null &&
username.equals(userDetails.getUsername()) &&
!isCreatedBeforeLastPasswordReset(created, user.getLastPasswordResetDate())
);
}
private Boolean isCreatedBeforeLastPasswordReset(Date created, Date lastPasswordReset) {
return (lastPasswordReset != null && created.before(lastPasswordReset));
}
public String getToken( HttpServletRequest request ) {
/**
* Getting the token from Authentication header
* e.g Bearer your_token
*/
String authHeader = getAuthHeaderFromHeader( request );
if ( authHeader != null && authHeader.startsWith("Bearer ")) {
return authHeader.substring(7);
}
return null;
}
public String getAuthHeaderFromHeader( HttpServletRequest request ) {
return request.getHeader(AUTH_HEADER);
}
}
Finally your controller class
public void validateToken(HttpServletRequest request) {
Cookie cookie = WebUtils.getCookie(request, "TOKEN_NAME");
if(cookie == null) {
throw new SecurityException("JWT token missing");
}
String token = cookie.getValue(); // JWT Token
Claims claims = TokenHelper.getAllClaimsFromToken(token); // claims will be null if Token is invalid
}
I am facing a issue that whenever I am signing a token also I parse it and it is not throwing any signature exception.
You can see the key are different still it giving me the proper response.
public class JwtUtil {
public String parseToken(String token) {
try {
Jws<Claims> jwt = Jwts.parser()
.setSigningKey("Test#12")
.parseClaimsJws(token);
System.out.println(jwt.getBody().getSubject());
return "Valid";
} catch (SignatureException jwtException) {
jwtException.printStackTrace();
return null;
}
}
public String generateToken() {
Claims claim = Jwts.claims();
claim.put("GivenName", "Johnny");
claim.put("Surname", "Rocket");
claim.put("Email", "jrocket#example.com");
return Jwts.builder().setHeaderParam("typ", "JWT").setClaims(claim)
.setIssuer("Online JWT Builder")
.setAudience("www.example.com").setSubject("jrocket#example.com")
.signWith(SignatureAlgorithm.HS256, "Test#123").compact();
}
public static void main(String[] args) {
JwtUtil jwtUtil = new JwtUtil();
String token = jwtUtil.generateToken();
System.out.println(token);
JwtUtil jwtUtil1 = new JwtUtil();
jwtUtil1.parseToken(token);
}
}
Really Test#12 and Test#123 are the same key
It is due to JwtBuilder.signWith(SignatureAlgorithm alg, String base64EncodedSecretKey). assumes that you are providing a key in base64 and your keys are not base64. When the method decodes from base64 to byte[] the java converter used by jjwt provides a representation of the string. Test#12 and Test#123 are encoded with the byte array
See https://stackoverflow.com/a/38269014/6371459
You can test yourself with
System.out.println(
javax.xml.bind.DatatypeConverter.printBase64Binary(
javax.xml.bind.DatatypeConverter.parseBase64Binary("Test#12")));
System.out.println(
javax.xml.bind.DatatypeConverter.printBase64Binary(
javax.xml.bind.DatatypeConverter.parseBase64Binary("Test#123")));
Try a (more) different key and the SignatureException will be thrown