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 sessionRandomValueHash ) {
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 , sessionRandomValueHash );
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 , sessionRandomValueHash );
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 , sessionRandomValueHash );
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 sessionRandomValueHash ) throws JoseException , IOException {
280+ String request = convertToRequestParameter (redirectUriBuilder , redirectUri , state , forwardedQueryParams , sessionRandomValueHash );
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,11 @@ protected AuthChallenge resolveCode(String code) {
441452
442453 try {
443454 TokenValidator tokenValidator = TokenValidator .builder (deployment ).build ();
444- TokenValidator .VerifiedTokens verifiedTokens = tokenValidator .parseAndVerifyToken (idTokenString , tokenString );
455+
456+ TokenValidator .VerifiedTokens verifiedTokens = tokenValidator .parseAndVerifyToken (idTokenString , tokenString , facade );
445457 idToken = verifiedTokens .getIdToken ();
446458 token = verifiedTokens .getAccessToken ();
459+
447460 log .debug ("Token Verification succeeded!" );
448461 } catch (OidcException e ) {
449462 log .failedVerificationOfToken (e .getMessage ());
@@ -456,6 +469,7 @@ protected AuthChallenge resolveCode(String code) {
456469 log .error ("Stale token" );
457470 return challenge (HttpStatus .SC_FORBIDDEN , AuthenticationError .Reason .STALE_TOKEN , null );
458471 }
472+
459473 log .debug ("successfully authenticated" );
460474 return null ;
461475 }
@@ -535,7 +549,7 @@ private void addScopes(String scopes, Set<String> allScopes) {
535549 }
536550 }
537551
538- private String convertToRequestParameter (URIBuilder redirectUriBuilder , String redirectUri , String state , List <NameValuePair > forwardedQueryParams ) throws JoseException , IOException {
552+ private String convertToRequestParameter (URIBuilder redirectUriBuilder , String redirectUri , String state , List <NameValuePair > forwardedQueryParams , String sessionRandomValueHash ) throws JoseException , IOException {
539553 redirectUriBuilder .addParameter (SCOPE , OIDC_SCOPE );
540554
541555 JwtClaims jwtClaims = new JwtClaims ();
@@ -545,10 +559,12 @@ private String convertToRequestParameter(URIBuilder redirectUriBuilder, String r
545559 for ( NameValuePair parameter : forwardedQueryParams ) {
546560 jwtClaims .setClaim (parameter .getName (), parameter .getValue ());
547561 }
562+
548563 jwtClaims .setClaim (STATE , state );
549564 jwtClaims .setClaim (REDIRECT_URI , redirectUri );
550565 jwtClaims .setClaim (RESPONSE_TYPE , CODE );
551566 jwtClaims .setClaim (CLIENT_ID , deployment .getResourceName ());
567+ jwtClaims .setClaim (NONCE , sessionRandomValueHash );
552568
553569 // sign JWT first before encrypting
554570 JsonWebSignature signedRequest = signRequest (jwtClaims , deployment );
@@ -622,4 +638,11 @@ private JsonWebEncryption encryptRequest(JsonWebSignature signedRequest) throws
622638 return jsonEncryption ;
623639 }
624640 }
641+
642+ private String generateSessionRandomValue () {
643+ SecureRandom random = new SecureRandom ();
644+ byte [] nonceData = new byte [NONCE_SIZE ];
645+ random .nextBytes (nonceData );
646+ return ByteIterator .ofBytes (nonceData ).base64Encode ().drainToString ();
647+ }
625648}
0 commit comments