Skip to content

Commit

Permalink
migrate from nimbusds:oauth2 to com.auth0:java-jwt #176
Browse files Browse the repository at this point in the history
  • Loading branch information
anatol-sialitski committed Mar 5, 2024
1 parent 1f5476e commit 1566739
Show file tree
Hide file tree
Showing 6 changed files with 57 additions and 45 deletions.
3 changes: 0 additions & 3 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,13 @@ dependencies {
include "com.enonic.xp:lib-portal:${xpVersion}"
include "com.enonic.xp:lib-cluster:${xpVersion}"
include "com.enonic.lib:lib-http-client:3.2.2"
include "com.nimbusds:oauth2-oidc-sdk:11.10.1"


include 'com.auth0:java-jwt:4.4.0'
include 'com.auth0:jwks-rsa:0.22.1'

testImplementation "com.enonic.xp:testing:${xpVersion}"
}


repositories {
mavenCentral()
xp.enonicRepo()
Expand Down
70 changes: 41 additions & 29 deletions src/main/java/com/enonic/app/oidcidprovider/OIDCUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,52 +2,64 @@

import java.math.BigInteger;
import java.security.SecureRandom;
import java.text.ParseException;
import java.util.Base64;
import java.util.Map;
import java.util.function.Supplier;

import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.JWSHeader;
import com.nimbusds.jose.JWSObject;
import com.nimbusds.jose.JWSSigner;
import com.nimbusds.jose.Payload;
import com.nimbusds.jose.crypto.MACSigner;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.JWTParser;
import com.nimbusds.jwt.proc.BadJWTException;
import com.nimbusds.oauth2.sdk.id.ClientID;
import com.nimbusds.oauth2.sdk.id.Issuer;
import com.nimbusds.openid.connect.sdk.Nonce;
import com.nimbusds.openid.connect.sdk.validators.IDTokenClaimsVerifier;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.fasterxml.jackson.databind.ObjectMapper;

import com.enonic.app.oidcidprovider.handler.IdProviderConfigService;
import com.enonic.app.oidcidprovider.jwt.RSAAlgorithmProvider;
import com.enonic.app.oidcidprovider.mapper.ClaimSetMapper;
import com.enonic.xp.script.bean.BeanContext;
import com.enonic.xp.script.bean.ScriptBean;

public class OIDCUtils
implements ScriptBean
{
public static String generateToken()
private static final ObjectMapper MAPPER = new ObjectMapper();

private Supplier<IdProviderConfigService> idProviderConfigServiceSupplier;

public String generateToken()
{
return new BigInteger( 130, new SecureRandom() ).toString( 32 );
}

public static ClaimSetMapper parseClaims( final String s, final String issuer, final String clientID, final String nonce )
throws ParseException, BadJWTException
public ClaimSetMapper parseClaims( final String jwtToken, final String issuer, final String clientID, final String nonce,
final String idProviderName )
throws Exception
{
final JWTClaimsSet jwtClaimsSet = JWTParser.parse( s ).getJWTClaimsSet();
DecodedJWT decodedJWT = JWT.decode( jwtToken );

RSAAlgorithmProvider rsaAlgorithmProvider = idProviderConfigServiceSupplier.get().getAlgorithmProvider( idProviderName );

Algorithm algorithm = rsaAlgorithmProvider.getAlgorithm( decodedJWT.getAlgorithm() );

final IDTokenClaimsVerifier verifier =
new IDTokenClaimsVerifier( new Issuer( issuer ), new ClientID( clientID ), new Nonce( nonce ), 1 );
verifier.verify( jwtClaimsSet, null );
DecodedJWT verifiedJWT = JWT.require( algorithm ).
withIssuer( issuer ).
withAudience( clientID ).
withClaim( "nonce", nonce ).
acceptLeeway( 1 ). // 1 sec for nbf and iat
build().
verify( decodedJWT );

return ClaimSetMapper.create().claimSet( jwtClaimsSet ).build();
Map<String, Object> claims = MAPPER.readValue( Base64.getDecoder().decode( verifiedJWT.getPayload() ), Map.class );

return ClaimSetMapper.create().claimMap( claims ).build();
}

public static String generateJwt( final Map message, final String clientSecret )
throws Exception
public String generateJwt( final Map<String, Object> message, final String clientSecret )
{
final JWSSigner signer = new MACSigner( clientSecret );
final JWSObject jwsObject = new JWSObject( new JWSHeader( JWSAlgorithm.HS256 ), new Payload( message ) );

jwsObject.sign( signer );
return JWT.create().withPayload( message ).sign( Algorithm.HMAC256( clientSecret ) );
}

return jwsObject.serialize();
@Override
public void initialize( final BeanContext beanContext )
{
this.idProviderConfigServiceSupplier = beanContext.getService( IdProviderConfigService.class );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,17 @@

import java.util.Map;

import com.nimbusds.jwt.JWTClaimsSet;

import com.enonic.xp.script.serializer.MapGenerator;
import com.enonic.xp.script.serializer.MapSerializable;

public class ClaimSetMapper
implements MapSerializable
{
private final JWTClaimsSet claimSet;
private final Map<String, Object> claimMap;

private ClaimSetMapper( final Builder builder )
{
claimSet = builder.claimSet;
claimMap = builder.claimMap;
}

public static Builder create()
Expand All @@ -25,7 +23,6 @@ public static Builder create()
@Override
public void serialize( final MapGenerator gen )
{
final Map<String, Object> claimMap = claimSet.getClaims();
for ( Map.Entry<String, Object> claimEntry : claimMap.entrySet() )
{
gen.value( claimEntry.getKey(), claimEntry.getValue() );
Expand All @@ -34,15 +31,15 @@ public void serialize( final MapGenerator gen )

public static final class Builder
{
private JWTClaimsSet claimSet;
private Map<String, Object> claimMap;

private Builder()
{
}

public Builder claimSet( final JWTClaimsSet claimSet )
public Builder claimMap( final Map<String, Object> claimMap )
{
this.claimSet = claimSet;
this.claimMap = claimMap;
return this;
}

Expand Down
1 change: 1 addition & 0 deletions src/main/resources/idprovider/idprovider.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ function handleAuthenticationResponse(req) {

//https://tools.ietf.org/html/rfc6749#section-2.3.1
const idToken = oidcLib.requestIDToken({
idProviderName: idProviderConfig._idProviderName,
issuer: idProviderConfig.issuer,
tokenUrl: idProviderConfig.tokenUrl,
method: idProviderConfig.method,
Expand Down
1 change: 1 addition & 0 deletions src/main/resources/lib/configFile/configProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ function validate(config, idProviderName) {
checkConfig(config, 'issuer', idProviderName);
checkConfig(config, 'authorizationUrl', idProviderName);
checkConfig(config, 'tokenUrl', idProviderName);
checkConfig(config, 'jwksUri', idProviderName);

if (config.clientId != null) {
checkConfig(config, 'clientSecret', idProviderName);
Expand Down
14 changes: 9 additions & 5 deletions src/main/resources/lib/oidc.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,19 @@ const preconditions = require('/lib/preconditions');
const httpClient = require('/lib/http-client');

function generateToken() {
return Java.type('com.enonic.app.oidcidprovider.OIDCUtils').generateToken();
const bean = __.newBean('com.enonic.app.oidcidprovider.OIDCUtils');
return bean.generateToken();
}

function parseClaims(jwt, issuer, clientId, nonce) {
const parsedJwt = Java.type('com.enonic.app.oidcidprovider.OIDCUtils').parseClaims(jwt, issuer, clientId, nonce);
function parseClaims(jwt, issuer, clientId, nonce, idProviderName) {
const bean = __.newBean('com.enonic.app.oidcidprovider.OIDCUtils');
const parsedJwt = bean.parseClaims(jwt, issuer, clientId, nonce, idProviderName);
return __.toNativeObject(parsedJwt);
}

function generateJwt(jwtData, clientSecret) {
return Java.type('com.enonic.app.oidcidprovider.OIDCUtils').generateJwt(jwtData, clientSecret);
const bean = __.newBean('com.enonic.app.oidcidprovider.OIDCUtils');
return bean.generateJwt(jwtData, clientSecret);
}

function generateAuthorizationUrl(params) {
Expand Down Expand Up @@ -40,6 +43,7 @@ function requestIDToken(params) {
const redirectUri = preconditions.checkParameter(params, 'redirectUri');
const nonce = preconditions.checkParameter(params, 'nonce');
const code = preconditions.checkParameter(params, 'code');
const idProviderName = preconditions.checkParameter(params, 'idProviderName');
const method = params.method;

//https://openid.net/specs/openid-connect-core-1_0.html#TokenRequest
Expand Down Expand Up @@ -92,7 +96,7 @@ function requestIDToken(params) {
throw 'Token error [' + params.error + ']' + (params.error_description ? ': ' + params.error_description : '');
}

const claims = parseClaims(responseBody.id_token, issuer, clientId, nonce);
const claims = parseClaims(responseBody.id_token, issuer, clientId, nonce, idProviderName);
log.debug('Parsed claims: ' + JSON.stringify(claims));

return {
Expand Down

0 comments on commit 1566739

Please sign in to comment.