diff --git a/src/test/java/org/seedstack/oauth/fixtures/TokenErrorCode.java b/src/test/java/org/seedstack/oauth/fixtures/TokenErrorCode.java new file mode 100644 index 0000000..51ec9b6 --- /dev/null +++ b/src/test/java/org/seedstack/oauth/fixtures/TokenErrorCode.java @@ -0,0 +1,16 @@ +/* + * Copyright © 2013-2017, The SeedStack authors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package org.seedstack.oauth.fixtures; + +import org.seedstack.shed.exception.ErrorCode; + +public enum TokenErrorCode implements ErrorCode { + + UNABLE_TO_FETCH_PRIVATE_KEY, FAILED_TO_LOAD_JWKS +} diff --git a/src/test/java/org/seedstack/oauth/provider/resources/TokenBuilder.java b/src/test/java/org/seedstack/oauth/provider/resources/TokenBuilder.java index 17621aa..e89834e 100644 --- a/src/test/java/org/seedstack/oauth/provider/resources/TokenBuilder.java +++ b/src/test/java/org/seedstack/oauth/provider/resources/TokenBuilder.java @@ -7,28 +7,112 @@ */ package org.seedstack.oauth.provider.resources; +import java.io.IOException; +import java.text.ParseException; import java.util.Date; import java.util.List; +import org.seedstack.oauth.fixtures.TokenErrorCode; +import org.seedstack.oauth.OAuthConfig; +import org.seedstack.seed.SeedException; + +import com.nimbusds.jose.JOSEException; +import com.nimbusds.jose.JWSAlgorithm; +import com.nimbusds.jose.JWSHeader; +import com.nimbusds.jose.JWSSigner; import com.nimbusds.jose.PlainHeader; +import com.nimbusds.jose.crypto.RSASSASigner; +import com.nimbusds.jose.jwk.JWK; +import com.nimbusds.jose.jwk.JWKSet; +import com.nimbusds.jose.jwk.RSAKey; import com.nimbusds.jwt.JWTClaimsSet; import com.nimbusds.jwt.PlainJWT; +import com.nimbusds.jwt.SignedJWT; +import com.nimbusds.oauth2.sdk.token.AccessToken; +import com.nimbusds.oauth2.sdk.token.BearerAccessToken; +import static com.google.common.base.Preconditions.checkNotNull; public class TokenBuilder { + private String ACCESS_TOKEN_VALUE = "ya29.Gl0OBRawZls_r7atLBziIl051NW1xWZTp96JbPyuz8g09Ty0QvavJaQzBMtpclRxDxgq2b3pdQbUFCDaRq-qIJ7Qsw_KQmYMhxxczJsXP7DqMkiQf7CvOsZhwQkqpfE"; + private String TOKEN_TYPE = "Bearer"; + private int TOKEN_EXPIRES_IN = 3563; + private String RSA_KEY_ID = "5ef69cb85daeef24c4791e20553af176fd216e68"; + private String clientID = ""; private boolean testInvalidNonce; private boolean testTokenExpiry; private boolean testInvalidAudience; private boolean buildOnlyAccessToken; - private static String accessTokenValue = "ya29.Gl0OBRawZls_r7atLBziIl051NW1xWZTp96JbPyuz8g09Ty0QvavJaQzBMtpclRxDxgq2b3pdQbUFCDaRq-qIJ7Qsw_KQmYMhxxczJsXP7DqMkiQf7CvOsZhwQkqpfE"; - private static String tokenType = "Bearer"; - private static int tokenExpiresIn = 3563; - private String clientID = ""; + OAuthConfig oauthConfig; + public TokenBuilder(OAuthConfig oauthConfig){ + this.oauthConfig = oauthConfig; + } - public String buildPlainJWT(String nonce){ + public TokenData buildToken(String nonce,List scopes){ + TokenData td = new TokenData(); + td.setAccess_token(buildAccessToken()); + td.setExpires_in(TOKEN_EXPIRES_IN); + td.setToken_type(TOKEN_TYPE); + td.setScope(scopes.toString()); + if(!buildOnlyAccessToken){ + if(oauthConfig.openIdConnect().isUnsecuredTokenAllowed()){ + td.setId_token(buildPlainJWT(nonce)); + }else{ + td.setId_token(buildSignedJWT(nonce)); + } + } + + return td; + } + + private String buildAccessToken(){ + + AccessToken accessToken = new BearerAccessToken(ACCESS_TOKEN_VALUE); + return accessToken.getValue(); + + } + + private String buildPlainJWT(String nonce){ PlainHeader plainHeader = new PlainHeader(null,null,null,null,null); + JWTClaimsSet jWTClaimsSet = buildJWTClaimSet(nonce); + + return new PlainJWT(plainHeader, jWTClaimsSet).serialize(); + + } + + private String buildSignedJWT(String nonce){ + + SignedJWT signedJWT = null ; + try { + + JWKSet jwkSet = JWKSet.load(checkNotNull(oauthConfig.openIdConnect() + .getJwks().toURL(), "Unable to load JWK set")); + JWK key = jwkSet.getKeyByKeyId(RSA_KEY_ID); + + JWTClaimsSet jWTClaimsSet = buildJWTClaimSet(nonce); + JWSHeader jswHeader = new JWSHeader.Builder(JWSAlgorithm.RS256).keyID(RSA_KEY_ID).build(); + signedJWT = new SignedJWT(jswHeader, jWTClaimsSet); + + JWSSigner signer = new RSASSASigner((RSAKey)key); + signedJWT.sign(signer); + + return signedJWT.serialize(); + + }catch (JOSEException e) { + SeedException.wrap(e, TokenErrorCode.UNABLE_TO_FETCH_PRIVATE_KEY); + }catch (IOException | ParseException e1) { + SeedException.wrap(e1, TokenErrorCode.FAILED_TO_LOAD_JWKS); + } + + return ""; + + } + + + private JWTClaimsSet buildJWTClaimSet(String nonce){ + Long iat = System.currentTimeMillis(); Long exp = (iat) + (3600*60); @@ -38,38 +122,26 @@ public String buildPlainJWT(String nonce){ clientID = "2344574985.incorrect.client"; }else if(testTokenExpiry){ exp = iat; + }else{ + clientID = oauthConfig.getClientId(); } + JWTClaimsSet jWTClaimsSet = new JWTClaimsSet.Builder() - .claim("at_hash", "GlCoaDfQuUvpilxrKRBBdQ") - .audience(clientID) - .subject("118090614001964330293") - .claim("email_verified", "true") - .claim("azp", clientID) - .issuer("https://mockedserver.com") - .expirationTime(new Date(exp)) - .claim("nonce", nonce) - .issueTime(new Date(iat)) - .claim("email", "jyotirathalye@gmail.com").build(); + .claim("at_hash", "GlCoaDfQuUvpilxrKRBBdQ") + .audience(clientID) + .subject("118090614001964330293") + .claim("email_verified", "true") + .claim("azp", clientID) + .issuer("https://mockedserver.com") + .expirationTime(new Date(exp)) + .claim("nonce", nonce) + .issueTime(new Date(iat)) + .claim("email", "jr@gmail.com").build(); - return new PlainJWT(plainHeader, jWTClaimsSet).serialize(); + return jWTClaimsSet; } - - public TokenData buildToken(String nonce,List scopes){ - TokenData td = new TokenData(); - td.setAccess_token(accessTokenValue); - td.setExpires_in(tokenExpiresIn); - td.setToken_type(tokenType); - td.setScope(scopes.toString()); - if(!buildOnlyAccessToken){ - - td.setId_token(buildPlainJWT(nonce)); - } - - return td; - } - public void setTestInvalidNonce(boolean testInvalidNonce) { this.testInvalidNonce = testInvalidNonce; } @@ -82,12 +154,8 @@ public void setTestInvalidAudience(boolean testInvalidAudience) { this.testInvalidAudience = testInvalidAudience; } - public void setTestClientId(String clientID){ - this.clientID=clientID; - } - public void setFlagForAccessToken(boolean buildOnlyAccessToken){ this.buildOnlyAccessToken = buildOnlyAccessToken; } - + } diff --git a/src/test/java/org/seedstack/oauth/provider/resources/TokenResource.java b/src/test/java/org/seedstack/oauth/provider/resources/TokenResource.java index 8d1611d..9f5df52 100644 --- a/src/test/java/org/seedstack/oauth/provider/resources/TokenResource.java +++ b/src/test/java/org/seedstack/oauth/provider/resources/TokenResource.java @@ -37,7 +37,7 @@ public class TokenResource { @POST @Produces(MediaType.APPLICATION_JSON) - public Response createttoken(){ + public Response createToken(){ NonceHandler n = new NonceHandler(); String nonce = n.getNonce(); @@ -47,11 +47,10 @@ public Response createttoken(){ } private TokenData tokenData(String nonce){ - TokenBuilder tb = new TokenBuilder(); + TokenBuilder tb = new TokenBuilder(oauthConfig); tb.setTestInvalidNonce(testInvalidNonce); tb.setTestInvalidAudience(testInvalidAudience); tb.setTestTokenExpiry(testTokenExpiry); - tb.setTestClientId(oauthConfig.getClientId()); tb.setFlagForAccessToken(buildOnlyAccessToken); return tb.buildToken(nonce,oauthConfig.getScopes()); } diff --git a/src/test/resources/META-INF/resources/JWKset.json b/src/test/resources/META-INF/resources/JWKset.json new file mode 100644 index 0000000..6ec7752 --- /dev/null +++ b/src/test/resources/META-INF/resources/JWKset.json @@ -0,0 +1,45 @@ +{ + "keys": [ + { + "kty": "RSA", + "d": "PCP_SFBlbV-gCnshIOaSbEx3DsgRYP5-UJ7Er8XQKAIbpnBFNeNQxXEh7-zBzey1rkGODkEPWCtCliDbJWtW1l0WxuFh6eNIyY25ZNbX6A6HFkQiVgMy88_fDPycJeUK5d9sGj3flOf-NhPaY9dmZpI-pG2suu9jgD4qJNIc6RJqAviYzlMJbOHPMCD581Fbp9N-FUN_skDyO674sGLbN7XiHGVUnyUvMTvH2_UnopxbYx2zrbTe4vemJJYhVJyYIXJKsMZ7_6w-4wsrOlec0q-pqFiabMpT08o4XKVut-UVoatrp2rDROjjgrnVkEaJS1iWUqU38lrsMQnv-qyR0Q", + "e": "AQAB", + "use": "sig", + "kid": "5ef69cb85daeef24c4791e20553af176fd216e68", + "alg": "RS256", + "n": "godE6U9vpw7u5HTj_Td5U6ajE0nTyb11kIqKal5Xiq84IO17L5Uqoi6oR3mWn_uwmpA6u8vBaMaK-3N6-7-JMAS5MSO3QMgaJzB8I4qjPfGL2-CaURiYHq4gmLOsmSfaYfT2JVHYNg1AhIty_gOudXnSRpK2UJv7HROWWzDhrQ05IBn_wILU7IgNM5D_GRkRopTI6WdTlTp0iGfPNMueSi8_WcIAXy9yYVThgrXPQjFwb9R73DMi78Rc-e5wtHIs0Sn-ifpTn5MPPiWD3AWNYRWlGlRjomgp2La78CTdBBheOH_U-pYQHHzZIx5_i0TgxqQN2GbjSGV8_jNtLs0jSQ" +}, + { + "kty": "RSA", + "alg": "RS256", + "use": "sig", + "kid": "5ef96cb85daeef24c4791e20553af176fd216e68", + "n": "wU2vnSZM9sOmYrmVTejHveM0HZmsz2lqs7pScxm45NRF3owocm0pfKb_haGULJeiQpO_vtZU6u79Zz-IZFXvLNdgohdruGRcnZhGAD7AHdFNznEAe1xHdwLYWi4oWFaqdr2QF127-Bkeowquf1Opnlt866D0gHmg13LPF96p1-gN2736KLleJ7lSu8L6-7gC6V5WdOP_fVFZ8B7Bztvk_Q6oajVDQPvgu5CROGhkzfMrS8oUlHkJdtAKOxUby0AzyAQLTvFLCywhZdFvkAyZK9V71aeO_flaGwkMex54RFDWSU9vsC1V-b0Z4ZnV54xfAmkioIQ23xo4lXVPbcz08Q", + "e": "AQAB" + }, + { + "kty": "RSA", + "alg": "RS256", + "use": "sig", + "kid": "b233daad97e70fb9c12fd76791009f2c0dcc1869", + "n": "3WB0fmvWWHjFfLiNlKtVXv90zF2RbVblolxZjt9_xWCaDLeUVB7NIzsYvaenraa4K88NzP8m2B7yC2v0h6qLl_z52aR4_i-21_TBuVZdzAEn4KzUTbPFg_VGs2x2-ogQdHGbbXp_xaLMWVpqYo0gdrxU3K2whuqHhnDzNJNkKUHbx6dYFEElxh8cQ9GbILFbzC1mwvumgZOUFwJCDgIZz4EIjSsTh8DLU_eNGusvkpazSu8uub1y25f1AMYC_C_8b5KDQ1M-qMZndQBjo_ULv3CUs4r2GJBzJavZ3-dTwL0MG_h12CMIxhcYGJNauxva74rR9sN7qQcqbEirM6zADw", + "e": "AQAB" + }, + { + "kty": "RSA", + "alg": "RS256", + "use": "sig", + "kid": "0aa4541e4c8eea84468fe61a93fb1c0632db5a0f", + "n": "sXwnk7pRx02LXWgVL6VutrlJd4HrQLIykCo6ngBGdDh9tXWRsUvcC6Pt8v9iJARaGZUUL0dyAMqHhfQyyZ_1sSD0kVFPXTFdvsbESBn5aeLCkF-j54hQfkqwjWEULfZA74vkBSlaSUZYLm1nkHZ9lnMUVQNpThlcm0zeXXwi-ntH9tR4NvpzfwhBMS3P3B8R1xK0AEmfdRoldm5gh2mTwOT-0v5wIJi5c2QYnvsPYmubWc1ZQHmZCfvmlOAx5y5bYTq4FxSyKty5pJXdVnEPjZDbs7tEN7EZOAvSUTWdTYnPy76xMKN0gpcac3dojrbVu77UZ13S6pxEPPCvbvBItw", + "e": "AQAB" + }, + { + "kty": "RSA", + "alg": "RS256", + "use": "sig", + "kid": "e5e5dbc1799c1c2e52c685780098081b8d411fec", + "n": "0dg9rYPahs06xe7dPxISJQwAaSQpckWPCSYag-SYYIpM0NRVrNopNvXWQE5n8epQcMXv0-0b7Lfjq2NrfcYNO9aggCddr0hajnwtxKpEQ7Oiy0yKU3C_b5QrbMXkXbfDEIAXFsadpPPwCC1zE-uGoQS2groBJrZZmPLrjQnhcYj4spPHFxkXgFbk-YvYmO6c1Z1lXKBUWWKAybCVOMFZRbtO29Zh7FesOBpe2AY2vQbnjjFKEWZS2-jCYXxhr3kWrluWwguqH71e-LlyICP3BkeE0nlnY73b15lZQQZ3hpODHIjHAsBTXpYW4q45zFmcR2zLkGAeKMy3BsiNmx_NcQ", + "e": "AQAB" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/application.yaml b/src/test/resources/application.yaml index cd5512a..babeff9 100644 --- a/src/test/resources/application.yaml +++ b/src/test/resources/application.yaml @@ -39,9 +39,9 @@ security: revocation: ${providerBaseUrl}/api/provider/token/revoke openIdConnect: issuer: https://mockedserver.com - jwks: https://mockedserver.com/oauth2/v3/certs + jwks: ${providerBaseUrl}/JWKset.json userInfo: ${providerBaseUrl}/api/provider/userInfo - unsecuredTokenAllowed: true + unsecuredTokenAllowed: false oauth: redirect: http://localhost:8080/callback discoveryDocument: https://accounts.google.com/.well-known/openid-configuration @@ -52,3 +52,6 @@ security: discoveryDocument: https://diam-idp.test.inetpsa.com/am/.well-known/openid-configuration?realm=/psa/secu clientId: ${env.clientId} clientSecret: ${env.clientSecret} +proxy: + httpProxy: "http://http.internetpsa.inetpsa.com:80" + noProxy: "*.inetpsa.com" \ No newline at end of file