2323import static org .jose4j .jws .AlgorithmIdentifiers .HMAC_SHA512 ;
2424import static org .jose4j .jws .AlgorithmIdentifiers .NONE ;
2525import static org .wildfly .security .http .oidc .ElytronMessages .log ;
26+ import static org .wildfly .security .http .oidc .IDToken .NONCE ;
2627import static org .wildfly .security .http .oidc .Oidc .ALLOW_QUERY_PARAMS_PROPERTY_NAME ;
2728import static org .wildfly .security .http .oidc .Oidc .CLIENT_ID ;
2829import static org .wildfly .security .http .oidc .Oidc .CODE ;
3940import static org .wildfly .security .http .oidc .Oidc .REQUEST ;
4041import static org .wildfly .security .http .oidc .Oidc .REQUEST_URI ;
4142import static org .wildfly .security .http .oidc .Oidc .SCOPE ;
43+ import static org .wildfly .security .http .oidc .Oidc .SESSION_RANDOM_VALUE ;
4244import static org .wildfly .security .http .oidc .Oidc .SESSION_STATE ;
4345import static org .wildfly .security .http .oidc .Oidc .STATE ;
4446import static org .wildfly .security .http .oidc .Oidc .UI_LOCALES ;
5961import java .security .Key ;
6062import java .security .KeyPair ;
6163import java .security .PublicKey ;
64+ import java .security .SecureRandom ;
6265import java .util .ArrayList ;
6366import java .util .Arrays ;
6467import java .util .HashSet ;
7679import org .jose4j .jwt .JwtClaims ;
7780import org .jose4j .keys .HmacKey ;
7881import org .jose4j .lang .JoseException ;
82+ import org .wildfly .common .iteration .ByteIterator ;
7983import org .wildfly .security .http .HttpConstants ;
8084
8185/**
@@ -96,6 +100,8 @@ public class OidcRequestAuthenticator {
96100 protected String refreshToken ;
97101 protected String strippedOauthParametersRequestUri ;
98102
103+ private int NONCE_SIZE = 36 ;
104+
99105 static final boolean ALLOW_QUERY_PARAMS_PROPERTY ;
100106
101107 static {
@@ -181,7 +187,7 @@ protected String getCode() {
181187 return getQueryParamValue (facade , CODE );
182188 }
183189
184- protected String getRedirectUri (String state ) {
190+ protected String getRedirectUri (String state , String cryptoValue ) {
185191 String url = getRequestUrl ();
186192 log .debugf ("callback uri: %s" , url );
187193
@@ -222,14 +228,15 @@ protected String getRedirectUri(String state) {
222228 String redirectUri = rewrittenRedirectUri (url );
223229 URIBuilder redirectUriBuilder = new URIBuilder (deployment .getAuthUrl ());
224230 redirectUriBuilder .addParameter (RESPONSE_TYPE , CODE )
225- .addParameter (CLIENT_ID , deployment .getResourceName ());
231+ .addParameter (CLIENT_ID , deployment .getResourceName ())
232+ .addParameter (NONCE , cryptoValue );
226233
227234 switch (deployment .getAuthenticationRequestFormat ()) {
228235 case REQUEST :
229236 if (deployment .getRequestParameterSupported ()) {
230237 // add request objects into request parameter
231238 try {
232- createRequestWithRequestParameter (REQUEST , redirectUriBuilder , redirectUri , state , forwardedQueryParams );
239+ createRequestWithRequestParameter (REQUEST , redirectUriBuilder , redirectUri , state , forwardedQueryParams , cryptoValue );
233240 } catch (IOException | JoseException e ) {
234241 throw log .unableToCreateRequestWithRequestParameter (e );
235242 }
@@ -242,7 +249,7 @@ protected String getRedirectUri(String state) {
242249 case REQUEST_URI :
243250 if (deployment .getRequestUriParameterSupported ()) {
244251 try {
245- createRequestWithRequestParameter (REQUEST_URI , redirectUriBuilder , redirectUri , state , forwardedQueryParams );
252+ createRequestWithRequestParameter (REQUEST_URI , redirectUriBuilder , redirectUri , state , forwardedQueryParams , cryptoValue );
246253 } catch (IOException | JoseException e ) {
247254 throw log .unableToCreateRequestUriWithRequestParameter (e );
248255 }
@@ -269,8 +276,8 @@ protected URIBuilder createOAuthRequest(URIBuilder redirectUriBuilder, String re
269276 return redirectUriBuilder ;
270277 }
271278
272- protected URIBuilder createRequestWithRequestParameter (String requestFormat , URIBuilder redirectUriBuilder , String redirectUri , String state , List <NameValuePair > forwardedQueryParams ) throws JoseException , IOException {
273- String request = convertToRequestParameter (redirectUriBuilder , redirectUri , state , forwardedQueryParams );
279+ protected URIBuilder createRequestWithRequestParameter (String requestFormat , URIBuilder redirectUriBuilder , String redirectUri , String state , List <NameValuePair > forwardedQueryParams , String cryptoValue ) throws JoseException , IOException {
280+ String request = convertToRequestParameter (redirectUriBuilder , redirectUri , state , forwardedQueryParams , cryptoValue );
274281
275282 switch (requestFormat ) {
276283 case REQUEST :
@@ -296,7 +303,8 @@ protected String getStateCode() {
296303
297304 protected AuthChallenge loginRedirect () {
298305 final String state = getStateCode ();
299- final String redirect = getRedirectUri (state );
306+ final String sessionRandomValue = generateSessionRandomValue ();
307+ final String redirect = getRedirectUri (state , Oidc .getCryptographicValue (sessionRandomValue ));
300308 if (redirect == null ) {
301309 return challenge (HttpStatus .SC_FORBIDDEN , AuthenticationError .Reason .NO_REDIRECT_URI , null );
302310 }
@@ -314,6 +322,8 @@ public boolean challenge(OidcHttpFacade exchange) {
314322 exchange .getResponse ().setStatus (HttpStatus .SC_MOVED_TEMPORARILY );
315323 exchange .getResponse ().setCookie (deployment .getStateCookieName (), state , "/" , null , -1 , deployment .getSSLRequired ().isRequired (facade .getRequest ().getRemoteAddr ()), true );
316324 exchange .getResponse ().setHeader (HttpConstants .LOCATION , redirect );
325+ exchange .getResponse ().setCookie (SESSION_RANDOM_VALUE , sessionRandomValue , "/" , null , -1 , deployment .getSSLRequired ().isRequired (facade .getRequest ().getRemoteAddr ()), true );
326+
317327 return true ;
318328 }
319329 };
@@ -336,6 +346,7 @@ protected AuthChallenge checkStateCookie() {
336346 log .warn ("state parameter was null" );
337347 return challenge (HttpStatus .SC_BAD_REQUEST , AuthenticationError .Reason .INVALID_STATE_COOKIE , null );
338348 }
349+
339350 if (!state .equals (stateCookieValue )) {
340351 log .warn ("state parameter invalid" );
341352 log .warn ("cookie: " + stateCookieValue );
@@ -441,9 +452,12 @@ protected AuthChallenge resolveCode(String code) {
441452
442453 try {
443454 TokenValidator tokenValidator = TokenValidator .builder (deployment ).build ();
444- TokenValidator .VerifiedTokens verifiedTokens = tokenValidator .parseAndVerifyToken (idTokenString , tokenString );
455+ String sessionRandValueStr = getCookieValue (SESSION_RANDOM_VALUE );
456+
457+ TokenValidator .VerifiedTokens verifiedTokens = tokenValidator .parseAndVerifyToken (idTokenString , tokenString , facade );
445458 idToken = verifiedTokens .getIdToken ();
446459 token = verifiedTokens .getAccessToken ();
460+
447461 log .debug ("Token Verification succeeded!" );
448462 } catch (OidcException e ) {
449463 log .failedVerificationOfToken (e .getMessage ());
@@ -456,6 +470,7 @@ protected AuthChallenge resolveCode(String code) {
456470 log .error ("Stale token" );
457471 return challenge (HttpStatus .SC_FORBIDDEN , AuthenticationError .Reason .STALE_TOKEN , null );
458472 }
473+
459474 log .debug ("successfully authenticated" );
460475 return null ;
461476 }
@@ -535,7 +550,7 @@ private void addScopes(String scopes, Set<String> allScopes) {
535550 }
536551 }
537552
538- private String convertToRequestParameter (URIBuilder redirectUriBuilder , String redirectUri , String state , List <NameValuePair > forwardedQueryParams ) throws JoseException , IOException {
553+ private String convertToRequestParameter (URIBuilder redirectUriBuilder , String redirectUri , String state , List <NameValuePair > forwardedQueryParams , String cryptoValue ) throws JoseException , IOException {
539554 redirectUriBuilder .addParameter (SCOPE , OIDC_SCOPE );
540555
541556 JwtClaims jwtClaims = new JwtClaims ();
@@ -545,10 +560,12 @@ private String convertToRequestParameter(URIBuilder redirectUriBuilder, String r
545560 for ( NameValuePair parameter : forwardedQueryParams ) {
546561 jwtClaims .setClaim (parameter .getName (), parameter .getValue ());
547562 }
563+
548564 jwtClaims .setClaim (STATE , state );
549565 jwtClaims .setClaim (REDIRECT_URI , redirectUri );
550566 jwtClaims .setClaim (RESPONSE_TYPE , CODE );
551567 jwtClaims .setClaim (CLIENT_ID , deployment .getResourceName ());
568+ jwtClaims .setClaim (NONCE , cryptoValue );
552569
553570 // sign JWT first before encrypting
554571 JsonWebSignature signedRequest = signRequest (jwtClaims , deployment );
@@ -622,4 +639,11 @@ private JsonWebEncryption encryptRequest(JsonWebSignature signedRequest) throws
622639 return jsonEncryption ;
623640 }
624641 }
642+
643+ private String generateSessionRandomValue () {
644+ SecureRandom random = new SecureRandom ();
645+ byte [] nonceData = new byte [NONCE_SIZE ];
646+ random .nextBytes (nonceData );
647+ return ByteIterator .ofBytes (nonceData ).base64Encode ().drainToString ();
648+ }
625649}
0 commit comments