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 sessionRandomValue ) {
185191 String url = getRequestUrl ();
186192 log .debugf ("callback uri: %s" , url );
187193
@@ -221,15 +227,17 @@ protected String getRedirectUri(String state) {
221227
222228 String redirectUri = rewrittenRedirectUri (url );
223229 URIBuilder redirectUriBuilder = new URIBuilder (deployment .getAuthUrl ());
230+ String cryptoValue = Oidc .getCryptographicValue (sessionRandomValue );
224231 redirectUriBuilder .addParameter (RESPONSE_TYPE , CODE )
225- .addParameter (CLIENT_ID , deployment .getResourceName ());
232+ .addParameter (CLIENT_ID , deployment .getResourceName ())
233+ .addParameter (NONCE , cryptoValue );
226234
227235 switch (deployment .getAuthenticationRequestFormat ()) {
228236 case REQUEST :
229237 if (deployment .getRequestParameterSupported ()) {
230238 // add request objects into request parameter
231239 try {
232- createRequestWithRequestParameter (REQUEST , redirectUriBuilder , redirectUri , state , forwardedQueryParams );
240+ createRequestWithRequestParameter (REQUEST , redirectUriBuilder , redirectUri , state , forwardedQueryParams , sessionRandomValue );
233241 } catch (IOException | JoseException e ) {
234242 throw log .unableToCreateRequestWithRequestParameter (e );
235243 }
@@ -242,7 +250,7 @@ protected String getRedirectUri(String state) {
242250 case REQUEST_URI :
243251 if (deployment .getRequestUriParameterSupported ()) {
244252 try {
245- createRequestWithRequestParameter (REQUEST_URI , redirectUriBuilder , redirectUri , state , forwardedQueryParams );
253+ createRequestWithRequestParameter (REQUEST_URI , redirectUriBuilder , redirectUri , state , forwardedQueryParams , sessionRandomValue );
246254 } catch (IOException | JoseException e ) {
247255 throw log .unableToCreateRequestUriWithRequestParameter (e );
248256 }
@@ -269,8 +277,8 @@ protected URIBuilder createOAuthRequest(URIBuilder redirectUriBuilder, String re
269277 return redirectUriBuilder ;
270278 }
271279
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 );
280+ protected URIBuilder createRequestWithRequestParameter (String requestFormat , URIBuilder redirectUriBuilder , String redirectUri , String state , List <NameValuePair > forwardedQueryParams , String sessionRandomValue ) throws JoseException , IOException {
281+ String request = convertToRequestParameter (redirectUriBuilder , redirectUri , state , forwardedQueryParams , sessionRandomValue );
274282
275283 switch (requestFormat ) {
276284 case REQUEST :
@@ -296,7 +304,8 @@ protected String getStateCode() {
296304
297305 protected AuthChallenge loginRedirect () {
298306 final String state = getStateCode ();
299- final String redirect = getRedirectUri (state );
307+ final String sessionRandomValue = generateSessionRandomValue ();
308+ final String redirect = getRedirectUri (state , sessionRandomValue );
300309 if (redirect == null ) {
301310 return challenge (HttpStatus .SC_FORBIDDEN , AuthenticationError .Reason .NO_REDIRECT_URI , null );
302311 }
@@ -314,6 +323,8 @@ public boolean challenge(OidcHttpFacade exchange) {
314323 exchange .getResponse ().setStatus (HttpStatus .SC_MOVED_TEMPORARILY );
315324 exchange .getResponse ().setCookie (deployment .getStateCookieName (), state , "/" , null , -1 , deployment .getSSLRequired ().isRequired (facade .getRequest ().getRemoteAddr ()), true );
316325 exchange .getResponse ().setHeader (HttpConstants .LOCATION , redirect );
326+ exchange .getResponse ().setCookie (SESSION_RANDOM_VALUE , sessionRandomValue , "/" , null , -1 , deployment .getSSLRequired ().isRequired (facade .getRequest ().getRemoteAddr ()), true );
327+
317328 return true ;
318329 }
319330 };
@@ -329,13 +340,20 @@ protected AuthChallenge checkStateCookie() {
329340 // reset the cookie
330341 log .debug ("** reseting application state cookie" );
331342 facade .getResponse ().resetCookie (deployment .getStateCookieName (), stateCookie .getPath ());
332- String stateCookieValue = getCookieValue (deployment .getStateCookieName ());
343+ String cookiesStr = getCookieValue (deployment .getStateCookieName ());
333344
334345 String state = getQueryParamValue (facade , STATE );
335346 if (state == null ) {
336347 log .warn ("state parameter was null" );
337348 return challenge (HttpStatus .SC_BAD_REQUEST , AuthenticationError .Reason .INVALID_STATE_COOKIE , null );
338349 }
350+
351+ String stateCookieValue = cookiesStr ;
352+ if (cookiesStr != null ) {
353+ String [] cookieArr = cookiesStr .split (";" );
354+ stateCookieValue = cookieArr [0 ];
355+ }
356+
339357 if (!state .equals (stateCookieValue )) {
340358 log .warn ("state parameter invalid" );
341359 log .warn ("cookie: " + stateCookieValue );
@@ -441,9 +459,11 @@ protected AuthChallenge resolveCode(String code) {
441459
442460 try {
443461 TokenValidator tokenValidator = TokenValidator .builder (deployment ).build ();
444- TokenValidator .VerifiedTokens verifiedTokens = tokenValidator .parseAndVerifyToken (idTokenString , tokenString );
462+ String stateCookieValue = getCookieValue (deployment .getStateCookieName ());
463+ TokenValidator .VerifiedTokens verifiedTokens = tokenValidator .parseAndVerifyToken (idTokenString , tokenString , stateCookieValue );
445464 idToken = verifiedTokens .getIdToken ();
446465 token = verifiedTokens .getAccessToken ();
466+
447467 log .debug ("Token Verification succeeded!" );
448468 } catch (OidcException e ) {
449469 log .failedVerificationOfToken (e .getMessage ());
@@ -456,6 +476,7 @@ protected AuthChallenge resolveCode(String code) {
456476 log .error ("Stale token" );
457477 return challenge (HttpStatus .SC_FORBIDDEN , AuthenticationError .Reason .STALE_TOKEN , null );
458478 }
479+
459480 log .debug ("successfully authenticated" );
460481 return null ;
461482 }
@@ -535,7 +556,7 @@ private void addScopes(String scopes, Set<String> allScopes) {
535556 }
536557 }
537558
538- private String convertToRequestParameter (URIBuilder redirectUriBuilder , String redirectUri , String state , List <NameValuePair > forwardedQueryParams ) throws JoseException , IOException {
559+ private String convertToRequestParameter (URIBuilder redirectUriBuilder , String redirectUri , String state , List <NameValuePair > forwardedQueryParams , String sessionRandomValue ) throws JoseException , IOException {
539560 redirectUriBuilder .addParameter (SCOPE , OIDC_SCOPE );
540561
541562 JwtClaims jwtClaims = new JwtClaims ();
@@ -545,10 +566,13 @@ private String convertToRequestParameter(URIBuilder redirectUriBuilder, String r
545566 for ( NameValuePair parameter : forwardedQueryParams ) {
546567 jwtClaims .setClaim (parameter .getName (), parameter .getValue ());
547568 }
569+
548570 jwtClaims .setClaim (STATE , state );
549571 jwtClaims .setClaim (REDIRECT_URI , redirectUri );
550572 jwtClaims .setClaim (RESPONSE_TYPE , CODE );
551573 jwtClaims .setClaim (CLIENT_ID , deployment .getResourceName ());
574+ String cryptoValue = Oidc .getCryptographicValue (sessionRandomValue );
575+ jwtClaims .setClaim (NONCE , String .valueOf (cryptoValue ));
552576
553577 // sign JWT first before encrypting
554578 JsonWebSignature signedRequest = signRequest (jwtClaims , deployment );
@@ -622,4 +646,11 @@ private JsonWebEncryption encryptRequest(JsonWebSignature signedRequest) throws
622646 return jsonEncryption ;
623647 }
624648 }
649+
650+ private String generateSessionRandomValue () {
651+ SecureRandom random = new SecureRandom ();
652+ byte [] nonceData = new byte [NONCE_SIZE ];
653+ random .nextBytes (nonceData );
654+ return ByteIterator .ofBytes (nonceData ).base64Encode ().drainToString ();
655+ }
625656}
0 commit comments