#Test
public void testJwtBuilder() {
JwtBuilder jwtBuilder = Jwts.builder()
.setId("123456")
.setSubject("Snake")
.setIssuedAt(new Date())
.signWith(SignatureAlgorithm.HS256, "123456789");
String token = jwtBuilder.compact();
System.out.println(token);
for (String s : token.split("\\.")) {
System.out.println(Base64Codec.BASE64.decodeToString(s));
}
}
This is the token I generated: eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIxMjM0NTYiLCJzdWIiOiJTbmFrZSIsImlhdCI6MTYyNjg4NTMwMH0.R0WmOmXaH93DiY_On98p7wSmKMsYpQN4a0T8-b82-bA
I set secret to "123456789",but I can parse it with "123456789x" or "12345678".
Here is my parsing code:
#Test
public void parseToken() {
String token = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIxMjM0NTYiLCJzdWIiOiJTbmFrZSIsImlhdCI6MTYyNjg4NTMwMH0.R0WmOmXaH93DiY_On98p7wSmKMsYpQN4a0T8-b82-bA";
Claims claims = Jwts.parser()
.setSigningKey("123456789x")
.parseClaimsJws(token)
.getBody();
System.out.println(claims);
}
Why does this happen?
Related
How can i test an endpoint with authorization header with a user context?
mockMvc.perform(post("/endpoint")
.content(objectMapper.writeValueAsString(request))
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.header("Authorization", authorizationToken(APP_AUTHORIZATION)))
.andDo(print()).andExpect(status().is2xxSuccessful());
I tried to do this, but it didn't work.
private String buildToken(#Nonnull ApiClient.JwtData data) throws JsonProcessingException {
final var token = JWT.create()
.withSubject(data.getUserId())
.withIssuedAt(new Date())
.withClaim("name", data.getUsername())
.withClaim("fullname", data.getUserFullName())
.withClaim("userid", data.getUserId())
.withClaim("locale", Objects.requireNonNullElse(data.getLocale(), DEFAULT_LOCALE));
final var authsMap = new HashMap<String, String[]>();
token.withClaim("authorities", objectMapper.writeValueAsString(authsMap));
return token.sign(Algorithm.HMAC256(SECRET));
}
protected String authorizationToken(String... authorities) throws JsonProcessingException {
final var jwtData = new ApiClient.JwtData("USER_ID", "USER_NAME", "USER_FULLNAME"
, "locale", "USER_AUTHORITIES", "US-EN", authorities);
return buildToken(jwtData);
}
I'm using org.springframework.security.oauth2.jwt library to write a JWT utility class which will be in charge of creating tokens and parsing claims. Now my question is how to I parse claims of my JWT correctly? There seems to far and few examples on the web on how to extract a claim using this library. Any help would be appreciate it
#Service
public class JwtUtil {
public static final String JWT_ISSUER = "test.com";
public static final long JWT_ACCESS_TOKEN_EXPIRY = 3600L;
public static final long JWT_REFRESH_TOKEN_EXPIRY = 7200L;
private JwtEncoder jwtEncoder;
private String extractExpiresAt(String token) {
// return expires at claim
}
private String createToken(String subject, Consumer<Map<String, Object>> claims) {
Instant now = Instant.now();
JwtClaimsSet claimsSet = JwtClaimsSet.builder()
.issuer(JWT_ISSUER)
.issuedAt(now)
.expiresAt(now.plusSeconds(JWT_ACCESS_TOKEN_EXPIRY))
.subject(subject)
.claims(claims)
.build();
return jwtEncoder.encode(JwtEncoderParameters.from(claimsSet)).getTokenValue();
}
private String createRefreshToken(String subject) {
Instant now = Instant.now();
JwtClaimsSet claimsSet = JwtClaimsSet.builder()
.issuer(JWT_ISSUER)
.issuedAt(now)
.expiresAt(now.plusSeconds(JWT_REFRESH_TOKEN_EXPIRY))
.subject(subject)
.build();
return jwtEncoder.encode(JwtEncoderParameters.from(claimsSet)).getTokenValue();
}
}
I'm using the Spring boot resource server. The authentication server issues a JWT. This JWT is re-encoded(with AES) with a key and in the Resource server, I should decode the JWT (from AES) before sending it to the JwtAuthenticator.
Now, I have a security configuration.
#Override
protected void configure(HttpSecurity http) throws Exception {
JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(new KeycloakRoleConverter());
http
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/users/status/check")
.hasRole("developer")
.anyRequest().authenticated()
.and()
.oauth2ResourceServer()
.jwt()
.decoder(new JWTDecoder())
.jwtAuthenticationConverter(jwtAuthenticationConverter);
}
and a JWT Decoder
import com.nimbusds.jwt.JWT;
import com.nimbusds.jwt.JWTParser;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.JwtException;
import java.text.ParseException;
public class JWTDecoder implements JwtDecoder {
#Override
public Jwt decode(String token) throws JwtException {
//decrypt from AES here
JWT jwt = null;
try {
jwt = JWTParser.parse(token);
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
}
What should I do then? The function should return org.springframework.security.oauth2.jwt.Jwt. How can I convert String token to Jwt?
I tried the following, but a problem occurred.
private Jwt createJwt(String token, JWT parsedJwt) {
try {
Map<String, Object> headers = new LinkedHashMap<>(parsedJwt.getHeader().toJSONObject());
Map<String, Object> claims = parsedJwt.getJWTClaimsSet().getClaims();
return Jwt.withTokenValue(token)
.headers(h -> h.putAll(headers))
.claims(c -> c.putAll(claims))
.build();
} catch (Exception ex) {
if (ex.getCause() instanceof ParseException) {
throw new JwtException("There is a problem parsing the JWT.");
} else {
throw new JwtException("There is a problem decoding the JWT.");
}
}
}
The error I received:
java.lang.IllegalArgumentException: timestamps must be of type Instant: java.lang.Long
I'm using Keycloak to generate the JWT. So, the exp field of the token in the jwt.io is "exp": 1657363340,. But after parsing the JWT in my code, It changes to the Date format. So, I changed the exp to Instant and my final method is like the following:
private Jwt createJwt(String token, JWT parsedJwt) {
try {
Map<String, Object> headers = new LinkedHashMap<>(parsedJwt.getHeader().toJSONObject());
Map<String, Object> claims = parsedJwt.getJWTClaimsSet().getClaims();
Jwt.Builder finalJwt = Jwt.withTokenValue(token)
.headers(h -> h.putAll(headers))
.claims(c -> c.putAll(claims));
finalJwt.expiresAt(((Date) claims.get("exp")).toInstant());
return finalJwt.build();
} catch (Exception ex) {
if (ex.getCause() instanceof ParseException) {
throw new JwtException("There is a problem parsing the JWT: " + ex.getMessage());
} else {
throw new JwtException("There is a problem decoding the JWT: " + ex.getMessage());
}
}
}
But the problem exists yet.
It could be due to expiration date of your token is a timestamp, and it should be a number (Long). Or you are trying to parse a timestamp to number Long.
As #Jose told me, I set the value of expiration time with an Instant type of the timestamp. Then, I set it to both the exp and iat fields of the JWT. My final function is like the following:
Map<String, Object> headers = new LinkedHashMap<>(parsedJwt.getHeader().toJSONObject());
Map<String, Object> claims = new HashMap<>();
for (String key : parsedJwt.getJWTClaimsSet().getClaims().keySet()) {
Object value = parsedJwt.getJWTClaimsSet().getClaims().get(key);
if (key.equals("exp") || key.equals("iat")) {
value = ((Date) value).toInstant();
}
claims.put(key, value);
}
return Jwt.withTokenValue(token)
.headers(h -> h.putAll(headers))
.claims(c -> c.putAll(claims))
.build();
I'm trying to get an Access Token from Keycloak over SpringBoot and did try the following example. But the KeycloakAuthenticationToken token is null.
Does someone know another approach to get an Access Token?
#GetMapping("/token")
public String getToken(HttpServletRequest request) throws IOException {
KeycloakAuthenticationToken token = (KeycloakAuthenticationToken) request.getUserPrincipal();
RefreshableKeycloakSecurityContext session = (RefreshableKeycloakSecurityContext) token.getAccount().getKeycloakSecurityContext();
KeycloakSecurityContext context = token.getAccount().getKeycloakSecurityContext();
String accessTokenPretty = JsonSerialization.writeValueAsPrettyString(session.getToken());
String idTokenPretty = JsonSerialization.writeValueAsPrettyString(session.getIdToken());
RefreshToken refreshToken;
try {
refreshToken = new JWSInput(session.getRefreshToken()).readJsonContent(RefreshToken.class);
} catch (JWSInputException e) {
throw new IOException(e);
}
String refreshTokenPretty = JsonSerialization.writeValueAsPrettyString(refreshToken);
return refreshTokenPretty;
}
Seems like I can get a token like this with ('org.keycloak:keycloak-admin-client'):
Keycloak keycloak = KeycloakBuilder.builder() //
.serverUrl(serverUrl) //
.realm(realm) //
.grantType(OAuth2Constants.PASSWORD) //
.clientId(clientId) //
.clientSecret(clientSecret) //
.username(userName) //
.password(password) //
.build();
AccessTokenResponse tok = keycloak.tokenManager().getAccessToken();
If someone knows a more elegant way, I would appreciate if you let me know :)
Thanks in advance!
Try the following:
HttpEntity<MultiValueMap<String, String>> request =
new TokenRequest.Builder(clientID, OAuth2Constants.PASSWORD)
.add("username", userName)
.add("password", password)
.build();
ResponseEntity<String> response = restTemplate.postForEntity( postUrl, request , String.class );
return response.getBody();
and the helper class:
public class TokenRequest {
public static class Builder{
MultiValueMap<String, String> data;
public Builder(String clientID, String grant_type){
data = new LinkedMultiValueMap<>();
data.put("client_id", Collections.singletonList(clientID));
data.put("grant_type", Collections.singletonList(grant_type));
}
public Builder add(String key, String value){
data.put(key, Collections.singletonList(value));
return this;
}
public HttpEntity<MultiValueMap<String, String>> build(){
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
return new HttpEntity<>(data, headers);
}
}
private TokenRequest(){
}
}
Try this:
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
KeycloakAuthenticationToken keycloakAuthenticationToken = (KeycloakAuthenticationToken) request.getUserPrincipal();
KeycloakPrincipal<KeycloakSecurityContext> principal = (KeycloakPrincipal) keycloakAuthenticationToken.getPrincipal();
String token = principal.getKeycloakSecurityContext().getIdTokenString();
We are using spring boot as backend to for only auth and generating jwt token rest is handled in hasura.
I am facing problem in generating JWT properly.
public String generateToken(String email,String role,Long id) {
Map<String, Object> claims = new HashMap<>();
Map<String,Object> claim =new HashMap<>();
claims.put("x-hasura-user-id",id);
claims.put("x-hasura-default-role",role);
claims.put("x-hasura-allowed-roles", new String[]{"job_seeker", "employer", "admin"});
claim.put("https://hasura.io/jwt/claims",claims);
System.out.println(claim);
return doGenerateToken(claim, email);
}
private String doGenerateToken(Map<String, Object> claim, String subject) {
return Jwts.builder().setClaims(claim).setSubject(subject).setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + Long.parseLong(tokenValidity))).signWith(SignatureAlgorithm.HS256, secret).compact();
}
This is generating jwt token as
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJyb3NhbjEyM0BnbWFpbC5jb20iLCJodHRwczovL2hhc3VyYS5pby9qd3QvY2xhaW1zIjp7IngtaGFzdXJhLWRlZmF1bHQtcm9sZSI6ImFkbWluIiwieC1oYXN1cmEtdXNlci1pZCI6NCwieC1oYXN1cmEtYWxsb3dlZC1yb2xlcyI6WyJqb2Jfc2Vla2VyIiwiZW1wbG95ZXIiLCJhZG1pbiJdfSwiZXhwIjoxNjA5ODU1OTA2LCJpYXQiOjE2MDk4NTExMDZ9.WqJE1xLIsycW92tzFXdq0UHub3qUfQbUvUax9rvks4Q
but it hasura is returning Invalid signature. Where as in node
generateToken: (user: any) => {
const payload = {
sub: user.email,
"https://hasura.io/jwt/claims": {
"x-hasura-default-role": `${user.role.name}`,
"x-hasura-user-id": `${user.id}`,
"x-hasura-allowed-roles": ["job_seeker", "employer", "admin"],
},
};
return jwt.sign(payload, secretkey);
},
jwt from node
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJib2hhcmFuaXNjaGFsQGdtYWlsLmNvbSIsImh0dHBzOi8vaGFzdXJhLmlvL2p3dC9jbGFpbXMiOnsieC1oYXN1cmEtZGVmYXVsdC1yb2xlIjoiam9iX3NlZWtlciIsIngtaGFzdXJhLXVzZXItaWQiOiIyNCIsIngtaGFzdXJhLWFsbG93ZWQtcm9sZXMiOlsiam9iX3NlZWtlciIsImVtcGxveWVyIiwiYWRtaW4iXX0sImlhdCI6MTYwOTg1NDEzMX0.8UDrqvRujakGsEtGEAu1XWl5RsFda8HaA_-97vwY62I
using same secret key and algorithm is working perfectly fine. For node i have used jsonwebtoken library.
I found the solution. String should be converted to byte[]:
private String doGenerateToken(Map<String,Object> header,Map<String, Object> claim, String subject) {
return Jwts.builder().setHeader(header).setClaims(claim).setSubject(subject).setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + Long.parseLong(tokenValidity))).signWith(SignatureAlgorithm.HS256, secret.getBytes(StandardCharsets.UTF_8)).compact();
}