Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Boot OAuth2 with encrypted JWT access token

In my Spring Bott application I have configured own OAuth2 with Authorization/Resource servers.

I have implemented following JwtAccessTokenConverter:

@Bean
public JwtAccessTokenConverter accessTokenConverter() {
    JwtAccessTokenConverter converter = new JwtAccessTokenConverter() {

        @Override
        public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
            DBUserDetails user = (DBUserDetails) authentication.getUserAuthentication().getPrincipal();
            final Map<String, Object> additionalInfo = new HashMap<>();
            additionalInfo.put("user_id", user.getUser().getId());
            ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
            OAuth2AccessToken enhancedToken = super.enhance(accessToken, authentication);
            return enhancedToken;
        }

    };

    converter.setSigningKey("123");

    DefaultAccessTokenConverter accessTokenConverter = new DefaultAccessTokenConverter();
    DefaultUserAuthenticationConverter userTokenConverter = new DefaultUserAuthenticationConverter();
    userTokenConverter.setUserDetailsService(userDetailsService);
    accessTokenConverter.setUserTokenConverter(userTokenConverter);

    converter.setAccessTokenConverter(accessTokenConverter);

    return converter;
}

Right now my application produces following tokens, for example:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxNzgzNTEsInVzZXJfbmFtZSI6ImFkbWluIiwic2NvcGUiOlsicmVhZCIsIndyaXRlIl0sImV4cCI6MTQ3NTA5NDA4NSwiYXV0aG9yaXRpZXMiOlsiUEVSTUlTU0lPTl9ERUxFVEVfT1dOX0NSSVRFUklPTiIsIlBFUk1JU1NJT05fREVMRVRFX09XTl9DT01NRU5UIiwiUEVSTUlTU0lPTl9VUERBVEVfT1dOX0NSSVRFUklPTiIsIlBFUk1JU1NJT05fVVBEQVRFX0FOWV9DUklURVJJT04iLCJQRVJNSVNTSU9OX0RFTEVURV9PV05fQ1JJVEVSSU9OX0dST1VQIiwiUEVSTUlTU0lPTl9ERUxFVEVfQU5ZX0RFQ0lTSU9OIiwiUEVSTUlTU0lPTl9DUkVBVEVfVk9URSIsIlBFUk1JU1NJT05fREVMRVRFX0FOWV9DUklURVJJT04iLCJQRVJNSVNTSU9OX0NSRUFURV9DUklURVJJT05fR1JPVVAiLCJQRVJNSVNTSU9OX0RFTEVURV9PV05fREVDSVNJT04iLCJQRVJNSVNTSU9OX0NSRUFURV9ERUNJU0lPTiIsIlBFUk1JU1NJT05fREVMRVRFX0FOWV9DUklURVJJT05fR1JPVVAiLCJQRVJNSVNTSU9OX0RFTEVURV9BTllfVk9URSIsIlBFUk1JU1NJT05fREVMRVRFX09XTl9WT1RFIiwiUEVSTUlTU0lPTl9VUERBVEVfT1dOX0NSSVRFUklPTl9HUk9VUCIsIlBFUk1JU1NJT05fVVBEQVRFX0FOWV9DUklURVJJT05fR1JPVVAiLCJQRVJNSVNTSU9OX1VQREFURV9BTllfQ09NTUVOVCIsIlBFUk1JU1NJT05fVVBEQVRFX09XTl9DT01NRU5UIiwiUEVSTUlTU0lPTl9VUERBVEVfQU5ZX0RFQ0lTSU9OIiwiUEVSTUlTU0lPTl9BUFBFTkRfREVDSVNJT04iLCJQRVJNSVNTSU9OX1VQREFURV9PV05fVk9URSIsIlBFUk1JU1NJT05fVVBEQVRFX0FOWV9WT1RFIiwiUEVSTUlTU0lPTl9ERUxFVEVfQU5ZX0NPTU1FTlQiLCJQRVJNSVNTSU9OX1VQREFURV9PV05fREVDSVNJT04iLCJQRVJNSVNTSU9OX0NSRUFURV9DT01NRU5UIiwiUEVSTUlTU0lPTl9DUkVBVEVfQ1JJVEVSSU9OIiwiUEVSTUlTU0lPTl9SRUFEX0FDVFVBVE9SX0RBVEEiXSwianRpIjoiMWU3OGMzMGYtNTY0ZS00NjliLWE1MmMtODlhOGM4YzFiZmY2IiwiY2xpZW50X2lkIjoiZGVjaXNpb253YW50ZWRfY2xpZW50X2lkIn0.Cnj_7b3FAanmL0Y-_kxcH2f4yjLFHOw-4NOVr67WZ88

This token can be decoded with JWT debugger here https://jwt.io/

I don't want to expose the internals of this token to external world and would like to encode this token in a some way.

How it can be implemented with Spring Boot, OAuth2, JWT ?

like image 629
alexanoid Avatar asked Sep 28 '16 08:09

alexanoid


People also ask

Can we use JWT and OAuth together?

JWT and OAuth2 are entirely different and serve different purposes, but they are compatible and can be used together. The OAuth2 protocol does not specify the format of the tokens, therefore JWTs can be incorporated into the usage of OAuth2.

Does OAuth2 require JWT?

You need to guide OAuth2 to issue two tokens. The first token should be access_token and the second token should be a JWT token featuring additional identity details.


1 Answers

i try this and it is working for me:https://gist.github.com/salgmachine/352799a6052b02901982dcbf85d30346

Create Custom JwtAccessTokenConverter

public class JwtJweAccessTokenConverter extends JwtAccessTokenConverter {

    RSAKey recipientJWK, recipientPublicJWK;

    public JwtJweAccessTokenConverter() {
        try {
            recipientJWK = new RSAKeyGenerator(2048).keyID("456").keyUse(KeyUse.ENCRYPTION).generate();
            recipientPublicJWK = recipientJWK.toPublicJWK();
        } catch (JOSEException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    @Override
    protected String encode(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
        String jwt = super.encode(accessToken, authentication);

        try {
            // jwt is already signed at this point (by JwtAccessTokenConverter)
            SignedJWT parsed = SignedJWT.parse(jwt);

            // Create JWE object with signed JWT as payload
            JWEObject jweObject = new JWEObject(
                    new JWEHeader.Builder(JWEAlgorithm.RSA_OAEP_256, EncryptionMethod.A256GCM).contentType("JWT") // required
                                                                                                                    // to
                                                                                                                    // indicate
                                                                                                                    // nested
                                                                                                                    // JWT
                            .build(),
                    new Payload(parsed));

            // Encrypt with the recipient's public key
            jweObject.encrypt(new RSAEncrypter(recipientPublicJWK));

            // Serialise to JWE compact form
            String jweString = jweObject.serialize();

            return jweString;
        } catch (Exception e) {
            e.printStackTrace();
        }

        return jwt;
    }

    @Override
    protected Map<String, Object> decode(String token) {
        try {
            // basically treat the incoming token as an encrypted JWT
            EncryptedJWT parse = EncryptedJWT.parse(token);
            // decrypt it
            RSADecrypter dec = new RSADecrypter(recipientJWK);
            parse.decrypt(dec);
            // content of the encrypted token is a signed JWT (signed by
            // JwtAccessTokenConverter)
            SignedJWT signedJWT = parse.getPayload().toSignedJWT();
            // pass on the serialized, signed JWT to JwtAccessTokenConverter
            return super.decode(signedJWT.serialize());

        } catch (ParseException e) {
            e.printStackTrace();
        } catch (JOSEException e) {
            e.printStackTrace();
        }

        return super.decode(token);
    }
}

And Config your Oauth2 Auth server and resource to use your Custom JwtAccessTokenConverter

@Bean
public TokenStore tokenStore() {

    return new JwtTokenStore(accessTokenConverter());
}

@Bean
public JwtAccessTokenConverter accessTokenConverter() {
    final JwtAccessTokenConverter converter = new JwtJweAccessTokenConverter();
    final KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(new ClassPathResource("mytest.jks"),
            "mypass".toCharArray());
    converter.setKeyPair(keyStoreKeyFactory.getKeyPair("mytest"));
    return converter;
}

Check github link for completed code example

like image 74
Mukesh M Avatar answered Nov 03 '22 00:11

Mukesh M