package org.pac4j.jwt; import com.nimbusds.jose.EncryptionMethod; import org.junit.Test; import org.pac4j.core.exception.CredentialsException; import org.pac4j.core.exception.HttpAction; import org.pac4j.core.exception.TechnicalException; import org.pac4j.core.profile.CommonProfile; import org.pac4j.core.profile.jwt.JwtClaims; import org.pac4j.core.util.TestsConstants; import org.pac4j.core.credentials.TokenCredentials; import org.pac4j.core.util.TestsHelper; import org.pac4j.jwt.config.encryption.SecretEncryptionConfiguration; import org.pac4j.jwt.config.encryption.EncryptionConfiguration; import org.pac4j.jwt.config.signature.ECSignatureConfiguration; import org.pac4j.jwt.config.signature.SecretSignatureConfiguration; import org.pac4j.jwt.config.signature.SignatureConfiguration; import org.pac4j.jwt.credentials.authenticator.JwtAuthenticator; import org.pac4j.jwt.profile.JwtGenerator; import org.pac4j.jwt.profile.JwtProfile; import org.pac4j.oauth.profile.facebook.FacebookProfileDefinition; import org.pac4j.oauth.profile.facebook.FacebookProfile; import com.nimbusds.jose.JWSAlgorithm; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; import java.util.*; import static org.junit.Assert.*; /** * This class tests the {@link JwtGenerator} and {@link org.pac4j.jwt.credentials.authenticator.JwtAuthenticator}. * * @author Jerome Leleu * @since 1.8.0 */ public final class JwtTests implements TestsConstants { private static final String KEY2 = "02ez4f7dsq==drrdz54z---++-6ef78="; private static final Set<String> ROLES = new HashSet<>(Arrays.asList(new String[] { "role1", "role2"})); private static final Set<String> PERMISSIONS = new HashSet<>(Arrays.asList(new String[] { "perm1"})); @Test public void testGenericJwt() throws HttpAction, CredentialsException { final String token = "eyJjdHkiOiJKV1QiLCJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiZGlyIn0..NTvhJXwZ_sN4zYBK.exyLJWkOclCVcffz58CE-3XWWV24aYyGWR5HVrfm4HLQi1xgmwglLlEIiFlOSTOSZ_LeAwl2Z3VFh-5EidocjwGkAPGQA_4_KCLbK8Im7M25ZZvDzCJ1kKN1JrDIIrBWCcuI4Mbw0O_YGb8TfIECPkpeG7wEgBG30sb1kH-F_vg9yjYfB4MiJCSFmY7cRqN9-9O23tz3wYv3b-eJh5ACr2CGSVNj2KcMsOMJ6bbALgz6pzQTIWk_fhcE9QSfaSY7RuZ8cRTV-UTjYgZk1gbd1LskgchS.ijMQmfPlObJv7oaPG8LCEg"; final TokenCredentials credentials = new TokenCredentials(token, JwtAuthenticator.class.getName()); final JwtAuthenticator authenticator = new JwtAuthenticator(new SecretSignatureConfiguration(MAC_SECRET), new SecretEncryptionConfiguration(MAC_SECRET)); authenticator.validate(credentials, null); assertNotNull(credentials.getUserProfile()); } @Test(expected = TechnicalException.class) public void testGenerateAuthenticateSub() throws HttpAction, CredentialsException { final JwtGenerator<FacebookProfile> generator = new JwtGenerator<>(new SecretSignatureConfiguration(MAC_SECRET)); final FacebookProfile profile = createProfile(); profile.addAttribute(JwtClaims.SUBJECT, VALUE); final String token = generator.generate(profile); assertToken(profile, token); } @Test(expected = CredentialsException.class) public void testPlainJwtWithSignatureConfigurations() throws HttpAction, CredentialsException { final JwtGenerator<FacebookProfile> generator = new JwtGenerator<>(); final FacebookProfile profile = createProfile(); final String token = generator.generate(profile); assertToken(profile, token); } @Test public void testPlainJwtWithoutSignatureConfigurations() throws HttpAction, CredentialsException { final JwtGenerator<FacebookProfile> generator = new JwtGenerator<>(); final FacebookProfile profile = createProfile(); final String token = generator.generate(profile); assertToken(profile, token, new JwtAuthenticator()); } @Test public void testPlainJwtNotExpired() { final JwtGenerator<FacebookProfile> generator = new JwtGenerator<>(); Map<String, Object> claims = new HashMap<>(); claims.put(JwtClaims.SUBJECT, ID); claims.put(JwtClaims.EXPIRATION_TIME, tomorrow()); final String token = generator.generate(claims); JwtAuthenticator authenticator = new JwtAuthenticator(); assertNotNull(authenticator.validateToken(token)); } @Test public void testPlainJwtExpired() { final JwtGenerator<FacebookProfile> generator = new JwtGenerator<>(); Map<String, Object> claims = new HashMap<>(); claims.put(JwtClaims.SUBJECT, ID); claims.put(JwtClaims.EXPIRATION_TIME, yesterday()); final String token = generator.generate(claims); JwtAuthenticator authenticator = new JwtAuthenticator(); assertNull(authenticator.validateToken(token)); } @Test public void testPlainJwtNoSubject() { final JwtGenerator<FacebookProfile> generator = new JwtGenerator<>(); final String token = generator.generate(new HashMap<>()); JwtAuthenticator authenticator = new JwtAuthenticator(); TestsHelper.expectException(() -> authenticator.validateToken(token), TechnicalException.class, "JWT must contain a subject ('sub' claim)"); } @Test public void testPemJwt() throws Exception { final FacebookProfile profile = createProfile(); final ECSignatureConfiguration signatureConfiguration = buildECSignatureConfiguration(); final JwtGenerator<FacebookProfile> generator = new JwtGenerator<>(signatureConfiguration); final String token = generator.generate(profile); final JwtAuthenticator authenticator = new JwtAuthenticator(); authenticator.addSignatureConfiguration(signatureConfiguration); assertToken(profile, token, authenticator); } @Test public void testGenerateAuthenticate() throws HttpAction, CredentialsException { final JwtGenerator<FacebookProfile> generator = new JwtGenerator<>(new SecretSignatureConfiguration(MAC_SECRET), new SecretEncryptionConfiguration(MAC_SECRET)); final FacebookProfile profile = createProfile(); final String token = generator.generate(profile); assertToken(profile, token); } @Test public void testDoubleGenerateAuthenticate() throws HttpAction, CredentialsException { final JwtGenerator<FacebookProfile> generator = new JwtGenerator<>(new SecretSignatureConfiguration(MAC_SECRET), new SecretEncryptionConfiguration(MAC_SECRET)); final FacebookProfile profile = createProfile(); final String token = generator.generate(profile); final JwtAuthenticator authenticator = new JwtAuthenticator(new SecretSignatureConfiguration(MAC_SECRET), new SecretEncryptionConfiguration(MAC_SECRET)); final TokenCredentials credentials = new TokenCredentials(token, CLIENT_NAME); authenticator.validate(credentials, null); final FacebookProfile profile2 = (FacebookProfile) credentials.getUserProfile(); generator.generate(profile2); } @Test public void testGenerateAuthenticateClaims() { final JwtGenerator<JwtProfile> generator = new JwtGenerator<>(new SecretSignatureConfiguration(MAC_SECRET), new SecretEncryptionConfiguration(MAC_SECRET)); final Map<String, Object> claims = new HashMap<>(); claims.put(JwtClaims.SUBJECT, VALUE); final Date tomorrow = tomorrow(); claims.put(JwtClaims.EXPIRATION_TIME, tomorrow); final String token = generator.generate(claims); final JwtAuthenticator jwtAuthenticator = new JwtAuthenticator(new SecretSignatureConfiguration(MAC_SECRET), new SecretEncryptionConfiguration(MAC_SECRET)); final JwtProfile profile = (JwtProfile) jwtAuthenticator.validateToken(token); assertEquals(VALUE, profile.getSubject()); assertEquals(tomorrow.getTime() / 1000, profile.getExpirationDate().getTime() / 1000); final Map<String, Object> claims2 = jwtAuthenticator.validateTokenAndGetClaims(token); assertEquals(VALUE, claims2.get(JwtClaims.SUBJECT)); assertEquals(tomorrow.getTime() / 1000, ((Date) claims2.get(JwtClaims.EXPIRATION_TIME)).getTime() / 1000); } private Date tomorrow() { final Date now = new Date(); long tomorrow = now.getTime() + 24 * 3600 * 1000; return new Date(tomorrow); } private Date yesterday() { final Date now = new Date(); long tomorrow = now.getTime() - 24 * 3600 * 1000; return new Date(tomorrow); } @Test public void testGenerateAuthenticateDifferentSecrets() throws HttpAction, CredentialsException { final SignatureConfiguration signatureConfiguration = new SecretSignatureConfiguration(MAC_SECRET); final EncryptionConfiguration encryptionConfiguration = new SecretEncryptionConfiguration(KEY2); final JwtGenerator<FacebookProfile> generator = new JwtGenerator<>(signatureConfiguration, encryptionConfiguration); final FacebookProfile profile = createProfile(); final String token = generator.generate(profile); assertToken(profile, token, new JwtAuthenticator(signatureConfiguration, encryptionConfiguration)); } @Test public void testGenerateAuthenticateUselessSignatureConfiguration() throws HttpAction, CredentialsException { final SignatureConfiguration signatureConfiguration = new SecretSignatureConfiguration(KEY2); final SignatureConfiguration signatureConfiguration2 = new SecretSignatureConfiguration(MAC_SECRET); final EncryptionConfiguration encryptionConfiguration = new SecretEncryptionConfiguration(MAC_SECRET); final JwtGenerator<FacebookProfile> generator = new JwtGenerator<>(signatureConfiguration, encryptionConfiguration); final FacebookProfile profile = createProfile(); final String token = generator.generate(profile); final JwtAuthenticator jwtAuthenticator = new JwtAuthenticator(); jwtAuthenticator.addSignatureConfiguration(signatureConfiguration); jwtAuthenticator.addSignatureConfiguration(signatureConfiguration2); jwtAuthenticator.setEncryptionConfiguration(encryptionConfiguration); assertToken(profile, token, jwtAuthenticator); } @Test public void testGenerateAuthenticateSlightlyDifferentSignatureConfiguration() { final JwtGenerator<FacebookProfile> generator = new JwtGenerator<>(new SecretSignatureConfiguration(KEY2)); final FacebookProfile profile = createProfile(); final String token = generator.generate(profile); final JwtAuthenticator jwtAuthenticator = new JwtAuthenticator(); jwtAuthenticator.addSignatureConfiguration(new SecretSignatureConfiguration(MAC_SECRET)); final Exception e = TestsHelper.expectException(() -> assertToken(profile, token, jwtAuthenticator)); assertTrue(e.getMessage().startsWith("JWT verification failed")); } @Test public void testGenerateAuthenticateDifferentSignatureConfiguration() throws Exception { final JwtGenerator<FacebookProfile> generator = new JwtGenerator<>(new SecretSignatureConfiguration(KEY2)); final FacebookProfile profile = createProfile(); final String token = generator.generate(profile); final JwtAuthenticator jwtAuthenticator = new JwtAuthenticator(); jwtAuthenticator.addSignatureConfiguration(buildECSignatureConfiguration()); final Exception e = TestsHelper.expectException(() -> assertToken(profile, token, jwtAuthenticator)); assertTrue(e.getMessage().startsWith("No signature algorithm found for JWT:")); } @Test public void testGenerateAuthenticateDifferentEncryptionConfiguration() { final JwtGenerator<FacebookProfile> generator = new JwtGenerator<>(); generator.setEncryptionConfiguration(new SecretEncryptionConfiguration(KEY2)); final FacebookProfile profile = createProfile(); final String token = generator.generate(profile); final JwtAuthenticator jwtAuthenticator = new JwtAuthenticator(); jwtAuthenticator.addEncryptionConfiguration(new SecretEncryptionConfiguration(MAC_SECRET)); final Exception e = TestsHelper.expectException(() -> assertToken(profile, token, jwtAuthenticator)); assertTrue(e.getMessage().startsWith("No encryption algorithm found for JWT:")); } @Test public void testGenerateAuthenticateNotEncrypted() throws HttpAction, CredentialsException { final JwtGenerator<FacebookProfile> generator = new JwtGenerator<>(new SecretSignatureConfiguration(MAC_SECRET)); final FacebookProfile profile = createProfile(); final String token = generator.generate(profile); assertToken(profile, token); } @Test public void testGenerateAuthenticateNotSigned() throws HttpAction, CredentialsException { final JwtGenerator<FacebookProfile> generator = new JwtGenerator<>(); generator.setEncryptionConfiguration(new SecretEncryptionConfiguration(MAC_SECRET)); final FacebookProfile profile = createProfile(); final String token = generator.generate(profile); assertToken(profile, token); } @Test public void testGenerateAuthenticateAndEncryptedWithRolesPermissions() throws HttpAction, CredentialsException { final JwtGenerator<FacebookProfile> generator = new JwtGenerator<>(new SecretSignatureConfiguration(MAC_SECRET)); final FacebookProfile profile = createProfile(); profile.addRoles(ROLES); profile.addPermissions(PERMISSIONS); final String token = generator.generate(profile); final CommonProfile profile2 = assertToken(profile, token); assertEquals(ROLES, profile2.getRoles()); assertEquals(PERMISSIONS, profile2.getPermissions()); } private CommonProfile assertToken(FacebookProfile profile, String token) throws HttpAction, CredentialsException { return assertToken(profile, token, new JwtAuthenticator(new SecretSignatureConfiguration(MAC_SECRET), new SecretEncryptionConfiguration(MAC_SECRET))); } private CommonProfile assertToken(FacebookProfile profile, String token, JwtAuthenticator authenticator) throws HttpAction, CredentialsException { final TokenCredentials credentials = new TokenCredentials(token, CLIENT_NAME); authenticator.validate(credentials, null); final CommonProfile profile2 = credentials.getUserProfile(); assertTrue(profile2 instanceof FacebookProfile); final FacebookProfile fbProfile = (FacebookProfile) profile2; assertEquals(profile.getTypedId(), fbProfile.getTypedId()); assertEquals(profile.getFirstName(), fbProfile.getFirstName()); assertEquals(profile.getDisplayName(), fbProfile.getDisplayName()); assertEquals(profile.getFamilyName(), fbProfile.getFamilyName()); assertEquals(profile.getVerified(), fbProfile.getVerified()); return profile2; } private FacebookProfile createProfile() { final FacebookProfile profile = new FacebookProfile(); profile.setId(ID); profile.addAttribute(FacebookProfileDefinition.NAME, NAME); profile.addAttribute(FacebookProfileDefinition.VERIFIED, true); return profile; } @Test(expected = CredentialsException.class) public void testAuthenticateFailed() throws HttpAction, CredentialsException { final JwtAuthenticator authenticator = new JwtAuthenticator(new SecretSignatureConfiguration(MAC_SECRET), new SecretEncryptionConfiguration(MAC_SECRET)); final TokenCredentials credentials = new TokenCredentials("fakeToken", CLIENT_NAME); authenticator.validate(credentials, null); } @Test public void testJwtGenerationA256CBC() { final JwtGenerator<CommonProfile> g = new JwtGenerator<>(new SecretSignatureConfiguration(MAC_SECRET + MAC_SECRET + MAC_SECRET + MAC_SECRET + MAC_SECRET + MAC_SECRET + MAC_SECRET + MAC_SECRET), new SecretEncryptionConfiguration(KEY2 + KEY2) ); ((SecretEncryptionConfiguration) g.getEncryptionConfiguration()).setMethod(EncryptionMethod.A256CBC_HS512); final String g1 = g.generate(new CommonProfile()); assertNotNull(g1); } @Test public void testJwtGenerationA256GCM() { final JwtGenerator<CommonProfile> g = new JwtGenerator<>( new SecretSignatureConfiguration(MAC_SECRET + MAC_SECRET + MAC_SECRET + MAC_SECRET + MAC_SECRET + MAC_SECRET + MAC_SECRET + MAC_SECRET), new SecretEncryptionConfiguration(MAC_SECRET) ); ((SecretEncryptionConfiguration) g.getEncryptionConfiguration()).setMethod(EncryptionMethod.A256GCM); final String g1 = g.generate(new CommonProfile()); assertNotNull(g1); } private ECSignatureConfiguration buildECSignatureConfiguration() throws NoSuchAlgorithmException { final KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC"); final KeyPair keyPair = keyGen.generateKeyPair(); return new ECSignatureConfiguration(keyPair, JWSAlgorithm.ES256); } }