001/*
002 * oauth2-oidc-sdk
003 *
004 * Copyright 2012-2016, Connect2id Ltd and contributors.
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use
007 * this file except in compliance with the License. You may obtain a copy of the
008 * License at
009 *
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software distributed
013 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
014 * CONDITIONS OF ANY KIND, either express or implied. See the License for the
015 * specific language governing permissions and limitations under the License.
016 */
017
018package com.nimbusds.oauth2.sdk.ciba;
019
020
021import java.net.URI;
022import java.util.*;
023
024import net.jcip.annotations.Immutable;
025
026import com.nimbusds.common.contenttype.ContentType;
027import com.nimbusds.jose.JWSObject;
028import com.nimbusds.jwt.JWT;
029import com.nimbusds.jwt.JWTClaimsSet;
030import com.nimbusds.jwt.JWTParser;
031import com.nimbusds.jwt.SignedJWT;
032import com.nimbusds.oauth2.sdk.AbstractAuthenticatedRequest;
033import com.nimbusds.oauth2.sdk.ParseException;
034import com.nimbusds.oauth2.sdk.Scope;
035import com.nimbusds.oauth2.sdk.SerializeException;
036import com.nimbusds.oauth2.sdk.auth.ClientAuthentication;
037import com.nimbusds.oauth2.sdk.auth.Secret;
038import com.nimbusds.oauth2.sdk.http.HTTPRequest;
039import com.nimbusds.oauth2.sdk.id.Identifier;
040import com.nimbusds.oauth2.sdk.token.BearerAccessToken;
041import com.nimbusds.oauth2.sdk.util.*;
042import com.nimbusds.openid.connect.sdk.OIDCClaimsRequest;
043import com.nimbusds.openid.connect.sdk.claims.ACR;
044
045
046/**
047 * <p>CIBA request to an OpenID provider / OAuth 2.0 authorisation server
048 * backend authentication endpoint. Supports plan as well as signed (JWT)
049 * requests.
050 *
051 * <p>Example HTTP request:
052 * 
053 * <pre>
054 * POST /bc-authorize HTTP/1.1
055 * Host: server.example.com
056 * Content-Type: application/x-www-form-urlencoded
057 *
058 * scope=openid%20email%20example-scope&amp;
059 * client_notification_token=8d67dc78-7faa-4d41-aabd-67707b374255&amp;
060 * binding_message=W4SCT&amp;
061 * login_hint_token=eyJraWQiOiJsdGFjZXNidyIsImFsZyI6IkVTMjU2In0.eyJ
062 * zdWJfaWQiOnsic3ViamVjdF90eXBlIjoicGhvbmUiLCJwaG9uZSI6IisxMzMwMjg
063 * xODAwNCJ9fQ.Kk8jcUbHjJAQkRSHyDuFQr3NMEOSJEZc85VfER74tX6J9CuUllr8
064 * 9WKUHUR7MA0-mWlptMRRhdgW1ZDt7g1uwQ&amp;
065 * client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3A
066 * client-assertion-type%3Ajwt-bearer&amp;
067 * client_assertion=eyJraWQiOiJsdGFjZXNidyIsImFsZyI6IkVTMjU2In0.eyJ
068 * pc3MiOiJzNkJoZFJrcXQzIiwic3ViIjoiczZCaGRSa3F0MyIsImF1ZCI6Imh0dHB
069 * zOi8vc2VydmVyLmV4YW1wbGUuY29tIiwianRpIjoiYmRjLVhzX3NmLTNZTW80RlN
070 * 6SUoyUSIsImlhdCI6MTUzNzgxOTQ4NiwiZXhwIjoxNTM3ODE5Nzc3fQ.Ybr8mg_3
071 * E2OptOSsA8rnelYO_y1L-yFaF_j1iemM3ntB61_GN3APe5cl_-5a6cvGlP154XAK
072 * 7fL-GaZSdnd9kg
073 * </pre>
074 *
075 * <p>Related specifications:
076 *
077 * <ul>
078 *      <li>OpenID Connect CIBA Flow - Core 1.0, section 7.1.
079 * </ul>
080 */
081@Immutable
082public class CIBARequest extends AbstractAuthenticatedRequest {
083        
084        
085        /**
086         * The maximum allowed length of a client notification token.
087         */
088        public static final int CLIENT_NOTIFICATION_TOKEN_MAX_LENGTH = 1024;
089        
090
091        /**
092         * The registered parameter names.
093         */
094        private static final Set<String> REGISTERED_PARAMETER_NAMES;
095
096        static {
097                Set<String> p = new HashSet<>();
098
099                // Plain
100                p.add("scope");
101                p.add("client_notification_token");
102                p.add("acr_values");
103                p.add("login_hint_token");
104                p.add("id_token_hint");
105                p.add("login_hint");
106                p.add("binding_message");
107                p.add("user_code");
108                p.add("requested_expiry");
109                p.add("claims");
110                
111                // Signed JWT
112                p.add("request");
113
114                REGISTERED_PARAMETER_NAMES = Collections.unmodifiableSet(p);
115        }
116
117        
118        /**
119         * The scope (required), must contain {@code openid}.
120         */
121        private final Scope scope;
122
123        
124        /**
125         * The client notification token, required for the CIBA ping and push
126         * token delivery modes.
127         */
128        private final BearerAccessToken clientNotificationToken;
129        
130        
131        /**
132         * Requested Authentication Context Class Reference values (optional).
133         */
134        private final List<ACR> acrValues;
135        
136        
137        /**
138         * A token containing information identifying the end-user for whom
139         * authentication is being requested (optional).
140         */
141        private final String loginHintTokenString;
142        
143        
144        /**
145         * Previously issued ID token passed as a hint to identify the end-user
146         * for whom authentication is being requested (optional).
147         */
148        private final JWT idTokenHint;
149        
150        
151        /**
152         * Login hint (email address, phone number, etc) about the end-user for
153         * whom authentication is being requested (optional).
154         */
155        private final String loginHint;
156        
157        
158        /**
159         * Human-readable binding message for the display at the consumption
160         * and authentication devices (optional).
161         */
162        private final String bindingMessage;
163        
164        
165        /**
166         * User secret code (password, PIN, etc.) to authorise the CIBA request
167         * with the authentication device (optional).
168         */
169        private final Secret userCode;
170        
171        
172        /**
173         * Requested expiration for the {@code auth_req_id} (optional).
174         */
175        private final Integer requestedExpiry;
176        
177        
178        /**
179         * Individual claims to be returned (optional).
180         */
181        private final OIDCClaimsRequest claims;
182        
183        
184        /**
185         * Custom parameters.
186         */
187        private final Map<String,List<String>> customParams;
188        
189        
190        /**
191         * The JWT for a signed request.
192         */
193        private final SignedJWT signedRequest;
194        
195
196        /**
197         * Builder for constructing CIBA requests.
198         */
199        public static class Builder {
200
201                
202                /**
203                 * The endpoint URI (optional).
204                 */
205                private URI uri;
206                
207                
208                /**
209                 * The client authentication (required).
210                 */
211                private final ClientAuthentication clientAuth;
212                
213                
214                /**
215                 * The scope (required).
216                 */
217                private final Scope scope;
218                
219                
220                /**
221                 * The client notification type, required for the CIBA ping and
222                 * push token delivery modes.
223                 */
224                private BearerAccessToken clientNotificationToken;
225                
226                
227                /**
228                 * Requested Authentication Context Class Reference values
229                 * (optional).
230                 */
231                private List<ACR> acrValues;
232                
233                
234                /**
235                 * A token containing information identifying the end-user for
236                 * whom authentication is being requested (optional).
237                 */
238                private String loginHintTokenString;
239                
240                
241                /**
242                 * Previously issued ID token passed as a hint to identify the
243                 * end-user for whom authentication is being requested
244                 * (optional).
245                 */
246                private JWT idTokenHint;
247                
248                
249                /**
250                 * Identity hint (email address, phone number, etc) about the
251                 * end-user for whom authentication is being requested
252                 * (optional).
253                 */
254                private String loginHint;
255                
256                
257                /**
258                 * Human readable binding message for the display at the
259                 * consumption and authentication devices (optional).
260                 */
261                private String bindingMessage;
262                
263                
264                /**
265                 * User secret code (password, PIN, etc) to authorise the CIBA
266                 * request with the authentication device (optional).
267                 */
268                private Secret userCode;
269                
270                
271                /**
272                 * Requested expiration for the {@code auth_req_id} (optional).
273                 */
274                private Integer requestedExpiry;
275                
276                
277                /**
278                 * Individual claims to be returned (optional).
279                 */
280                private OIDCClaimsRequest claims;
281                
282                
283                /**
284                 * Custom parameters.
285                 */
286                private Map<String,List<String>> customParams = new HashMap<>();
287                
288                
289                /**
290                 * The JWT for a signed request.
291                 */
292                private final SignedJWT signedRequest;
293
294                
295                /**
296                 * Creates a new CIBA request builder.
297                 *
298                 * @param clientAuth The client authentication. Must not be
299                 *                   {@code null}.
300                 * @param scope      The requested scope. Must not be empty or
301                 *                   {@code null}.
302                 */
303                public Builder(final ClientAuthentication clientAuth,
304                               final Scope scope) {
305                        
306                        if (clientAuth == null) {
307                                throw new IllegalArgumentException("The client authentication must not be null");
308                        }
309                        this.clientAuth = clientAuth;
310                        
311                        if (CollectionUtils.isEmpty(scope)) {
312                                throw new IllegalArgumentException("The scope must not be null or empty");
313                        }
314                        this.scope = scope;
315                        
316                        signedRequest = null;
317                }
318                
319                
320                /**
321                 * Creates a new CIBA signed request builder.
322                 *
323                 * @param clientAuth    The client authentication. Must not be
324                 *                      {@code null}.
325                 * @param signedRequest The signed request JWT. Must not be
326                 *                      {@code null}.
327                 */
328                public Builder(final ClientAuthentication clientAuth,
329                               final SignedJWT signedRequest) {
330                        
331                        if (clientAuth == null) {
332                                throw new IllegalArgumentException("The client authentication must not be null");
333                        }
334                        this.clientAuth = clientAuth;
335                        
336                        if (signedRequest == null) {
337                                throw new IllegalArgumentException("The signed request JWT must not be null");
338                        }
339                        this.signedRequest = signedRequest;
340                        
341                        scope = null;
342                }
343                
344
345                /**
346                 * Creates a new CIBA request builder from the specified
347                 * request.
348                 *
349                 * @param request The CIBA request. Must not be {@code null}.
350                 */
351                public Builder(final CIBARequest request) {
352                        
353                        uri = request.getEndpointURI();
354                        clientAuth = request.getClientAuthentication();
355                        scope = request.getScope();
356                        clientNotificationToken = request.getClientNotificationToken();
357                        acrValues = request.getACRValues();
358                        loginHintTokenString = request.getLoginHintTokenString();
359                        idTokenHint = request.getIDTokenHint();
360                        loginHint = request.getLoginHint();
361                        bindingMessage = request.getBindingMessage();
362                        userCode = request.getUserCode();
363                        requestedExpiry = request.getRequestedExpiry();
364                        claims = request.getOIDCClaims();
365                        customParams = request.getCustomParameters();
366                        signedRequest = request.getRequestJWT();
367                }
368                
369                
370                /**
371                 * Sets the client notification token, required for the CIBA
372                 * ping and push token delivery modes. Corresponds to the
373                 * {@code client_notification_token} parameter.
374                 *
375                 * @param token The client notification token, {@code null} if
376                 *              not specified.
377                 *
378                 * @return This builder.
379                 */
380                public Builder clientNotificationToken(final BearerAccessToken token) {
381                        this.clientNotificationToken = token;
382                        return this;
383                }
384
385                
386                /**
387                 * Sets the requested Authentication Context Class Reference
388                 * values. Corresponds to the optional {@code acr_values}
389                 * parameter.
390                 *
391                 * @param acrValues The requested ACR values, {@code null} if
392                 *                  not specified.
393                 *
394                 * @return This builder.
395                 */
396                public Builder acrValues(final List<ACR> acrValues) {
397                        this.acrValues = acrValues;
398                        return this;
399                }
400                
401                
402                /**
403                 * Sets the login hint token string, containing information
404                 * identifying the end-user for whom authentication is being requested.
405                 * Corresponds to the {@code login_hint_token} parameter.
406                 *
407                 * @param loginHintTokenString The login hint token string,
408                 *                             {@code null} if not specified.
409                 *
410                 * @return This builder.
411                 */
412                public Builder loginHintTokenString(final String loginHintTokenString) {
413                        this.loginHintTokenString = loginHintTokenString;
414                        return this;
415                }
416                
417                
418                /**
419                 * Sets the ID Token hint, passed as a hint to identify the
420                 * end-user for whom authentication is being requested.
421                 * Corresponds to the {@code id_token_hint} parameter.
422                 *
423                 * @param idTokenHint The ID Token hint, {@code null} if not
424                 *                    specified.
425                 *
426                 * @return This builder.
427                 */
428                public Builder idTokenHint(final JWT idTokenHint) {
429                        this.idTokenHint = idTokenHint;
430                        return this;
431                }
432                
433                
434                /**
435                 * Sets the login hint (email address, phone number, etc),
436                 * about the end-user for whom authentication is being
437                 * requested. Corresponds to the {@code login_hint} parameter.
438                 *
439                 * @param loginHint The login hint, {@code null} if not
440                 *                  specified.
441                 *
442                 * @return This builder.
443                 */
444                public Builder loginHint(final String loginHint) {
445                        this.loginHint = loginHint;
446                        return this;
447                }
448                
449                
450                /**
451                 * Sets the human readable binding message for the display at
452                 * the consumption and authentication devices. Corresponds to
453                 * the {@code binding_message} parameter.
454                 *
455                 * @param bindingMessage The binding message, {@code null} if
456                 *                       not specified.
457                 *
458                 * @return This builder.
459                 */
460                public Builder bindingMessage(final String bindingMessage) {
461                        this.bindingMessage = bindingMessage;
462                        return this;
463                }
464                
465                
466                /**
467                 * Gets the user secret code (password, PIN, etc) to authorise
468                 * the CIBA request with the authentication device. Corresponds
469                 * to the {@code user_code} parameter.
470                 *
471                 * @param userCode The user code, {@code null} if not
472                 *                 specified.
473                 *
474                 * @return This builder.
475                 */
476                public Builder userCode(final Secret userCode) {
477                        this.userCode = userCode;
478                        return this;
479                }
480                
481                
482                /**
483                 * Sets the requested expiration for the {@code auth_req_id}.
484                 * Corresponds to the {@code requested_expiry} parameter.
485                 *
486                 * @param requestedExpiry The required expiry (as positive
487                 *                        integer), {@code null} if not
488                 *                        specified.
489                 *
490                 * @return This builder.
491                 */
492                public Builder requestedExpiry(final Integer requestedExpiry) {
493                        this.requestedExpiry = requestedExpiry;
494                        return this;
495                }
496                
497                
498                /**
499                 * Sets the individual OpenID claims to be returned.
500                 * Corresponds to the optional {@code claims} parameter.
501                 *
502                 * @param claims The individual OpenID claims to be returned,
503                 *               {@code null} if not specified.
504                 *
505                 * @return This builder.
506                 */
507                public Builder claims(final OIDCClaimsRequest claims) {
508                        
509                        this.claims = claims;
510                        return this;
511                }
512                
513                
514                /**
515                 * Sets a custom parameter.
516                 *
517                 * @param name   The parameter name. Must not be {@code null}.
518                 * @param values The parameter values, {@code null} if not
519                 *               specified.
520                 *
521                 * @return This builder.
522                 */
523                public Builder customParameter(final String name, final String ... values) {
524                        
525                        if (values == null || values.length == 0) {
526                                customParams.remove(name);
527                        } else {
528                                customParams.put(name, Arrays.asList(values));
529                        }
530                        
531                        return this;
532                }
533                
534                
535                /**
536                 * Sets the URI of the endpoint (HTTP or HTTPS) for which the
537                 * request is intended.
538                 *
539                 * @param uri The endpoint URI, {@code null} if not specified.
540                 *
541                 * @return This builder.
542                 */
543                public Builder endpointURI(final URI uri) {
544                        
545                        this.uri = uri;
546                        return this;
547                }
548                
549                
550                /**
551                 * Builds a new CIBA request.
552                 *
553                 * @return The CIBA request.
554                 */
555                public CIBARequest build() {
556                        
557                        try {
558                                if (signedRequest != null) {
559                                        return new CIBARequest(
560                                                uri,
561                                                clientAuth,
562                                                signedRequest
563                                        );
564                                }
565                                
566                                // Plain request
567                                return new CIBARequest(
568                                        uri,
569                                        clientAuth,
570                                        scope,
571                                        clientNotificationToken,
572                                        acrValues,
573                                        loginHintTokenString,
574                                        idTokenHint,
575                                        loginHint,
576                                        bindingMessage,
577                                        userCode,
578                                        requestedExpiry,
579                                        claims,
580                                        customParams
581                                );
582                        } catch (IllegalArgumentException e) {
583                                throw new IllegalArgumentException(e.getMessage(), e);
584                        }
585                }
586        }
587        
588        
589        /**
590         * Creates a new CIBA request.
591         *
592         * @param uri                     The endpoint URI, {@code null} if not
593         *                                specified.
594         * @param clientAuth              The client authentication. Must not
595         *                                be {@code null}.
596         * @param scope                   The requested scope. Must not be
597         *                                empty or {@code null}.
598         * @param clientNotificationToken The client notification token,
599         *                                {@code null} if not specified.
600         * @param acrValues               The requested ACR values,
601         *                                {@code null} if not specified.
602         * @param loginHintTokenString    The login hint token string,
603         *                                {@code null} if not specified.
604         * @param idTokenHint             The ID Token hint, {@code null} if
605         *                                not specified.
606         * @param loginHint               The login hint, {@code null} if not
607         *                                specified.
608         * @param bindingMessage          The binding message, {@code null} if
609         *                                not specified.
610         * @param userCode                The user code, {@code null} if not
611         *                                specified.
612         * @param requestedExpiry         The required expiry (as positive
613         *                                integer), {@code null} if not
614         *                                specified.
615         * @param customParams            Custom parameters, empty or
616         *                                {@code null} if not specified.
617         */
618        @Deprecated
619        public CIBARequest(final URI uri,
620                           final ClientAuthentication clientAuth,
621                           final Scope scope,
622                           final BearerAccessToken clientNotificationToken,
623                           final List<ACR> acrValues,
624                           final String loginHintTokenString,
625                           final JWT idTokenHint,
626                           final String loginHint,
627                           final String bindingMessage,
628                           final Secret userCode,
629                           final Integer requestedExpiry,
630                           final Map<String, List<String>> customParams) {
631                
632                this(uri, clientAuth,
633                        scope, clientNotificationToken, acrValues,
634                        loginHintTokenString, idTokenHint, loginHint,
635                        bindingMessage, userCode, requestedExpiry,
636                        null, customParams);
637        }
638        
639        
640        /**
641         * Creates a new CIBA request.
642         *
643         * @param uri                     The endpoint URI, {@code null} if not
644         *                                specified.
645         * @param clientAuth              The client authentication. Must not
646         *                                be {@code null}.
647         * @param scope                   The requested scope. Must not be
648         *                                empty or {@code null}.
649         * @param clientNotificationToken The client notification token,
650         *                                {@code null} if not specified.
651         * @param acrValues               The requested ACR values,
652         *                                {@code null} if not specified.
653         * @param loginHintTokenString    The login hint token string,
654         *                                {@code null} if not specified.
655         * @param idTokenHint             The ID Token hint, {@code null} if
656         *                                not specified.
657         * @param loginHint               The login hint, {@code null} if not
658         *                                specified.
659         * @param bindingMessage          The binding message, {@code null} if
660         *                                not specified.
661         * @param userCode                The user code, {@code null} if not
662         *                                specified.
663         * @param requestedExpiry         The required expiry (as positive
664         *                                integer), {@code null} if not
665         *                                specified.
666         * @param claims                  The individual OpenID claims to be
667         *                                returned. Corresponds to the optional
668         *                                {@code claims} parameter.
669         *                                {@code null} if not specified.
670         * @param customParams            Custom parameters, empty or
671         *                                {@code null} if not specified.
672         */
673        public CIBARequest(final URI uri,
674                           final ClientAuthentication clientAuth,
675                           final Scope scope,
676                           final BearerAccessToken clientNotificationToken,
677                           final List<ACR> acrValues,
678                           final String loginHintTokenString,
679                           final JWT idTokenHint,
680                           final String loginHint,
681                           final String bindingMessage,
682                           final Secret userCode,
683                           final Integer requestedExpiry,
684                           final OIDCClaimsRequest claims,
685                           final Map<String, List<String>> customParams) {
686                
687                super(uri, clientAuth);
688                
689                if (CollectionUtils.isEmpty(scope)) {
690                        throw new IllegalArgumentException("The scope must not be null or empty");
691                }
692                this.scope = scope;
693                
694                if (clientNotificationToken != null && clientNotificationToken.getValue().length() > CLIENT_NOTIFICATION_TOKEN_MAX_LENGTH) {
695                        throw new IllegalArgumentException("The client notification token must not exceed " + CLIENT_NOTIFICATION_TOKEN_MAX_LENGTH + " chars");
696                }
697                this.clientNotificationToken = clientNotificationToken;
698                
699                this.acrValues = acrValues;
700                
701                // https://openid.net/specs/openid-client-initiated-backchannel-authentication-core-1_0-03.html#rfc.section.7.1
702                // As in the CIBA flow the OP does not have an interaction with
703                // the end-user through the consumption device, it is REQUIRED
704                // that the Client provides one (and only one) of the hints
705                // specified above in the authentication request, that is
706                // "login_hint_token", "id_token_hint" or "login_hint".
707                int numHints = 0;
708                
709                if (loginHintTokenString != null) numHints++;
710                this.loginHintTokenString = loginHintTokenString;
711                
712                if (idTokenHint != null) numHints++;
713                this.idTokenHint = idTokenHint;
714                
715                if (loginHint != null) numHints++;
716                this.loginHint = loginHint;
717                
718                if (numHints != 1) {
719                        throw new IllegalArgumentException("One user identity hist must be provided (login_hint_token, id_token_hint or login_hint)");
720                }
721                
722                this.bindingMessage = bindingMessage;
723                
724                this.userCode = userCode;
725                
726                if (requestedExpiry != null && requestedExpiry < 1) {
727                        throw new IllegalArgumentException("The requested expiry must be a positive integer");
728                }
729                this.requestedExpiry = requestedExpiry;
730                
731                this.claims = claims;
732                
733                this.customParams = customParams != null ? customParams : Collections.<String, List<String>>emptyMap();
734                
735                signedRequest = null;
736        }
737        
738        
739        /**
740         * Creates a new CIBA signed request.
741         *
742         * @param uri           The endpoint URI, {@code null} if not
743         *                      specified.
744         * @param clientAuth    The client authentication. Must not be
745         *                      {@code null}.
746         * @param signedRequest The signed request JWT. Must not be
747         *                      {@code null}.
748         */
749        public CIBARequest(final URI uri,
750                           final ClientAuthentication clientAuth,
751                           final SignedJWT signedRequest) {
752                
753                super(uri, clientAuth);
754                
755                if (signedRequest == null) {
756                        throw new IllegalArgumentException("The signed request JWT must not be null");
757                }
758                if (JWSObject.State.UNSIGNED.equals(signedRequest.getState())) {
759                        throw new IllegalArgumentException("The request JWT must be in a signed state");
760                }
761                this.signedRequest = signedRequest;
762                
763                scope = null;
764                clientNotificationToken = null;
765                acrValues = null;
766                loginHintTokenString = null;
767                idTokenHint = null;
768                loginHint = null;
769                bindingMessage = null;
770                userCode = null;
771                requestedExpiry = null;
772                claims = null;
773                customParams = Collections.emptyMap();
774        }
775
776        
777        /**
778         * Returns the registered (standard) CIBA request parameter names.
779         *
780         * @return The registered CIBA request parameter names, as a
781         *         unmodifiable set.
782         */
783        public static Set<String> getRegisteredParameterNames() {
784
785                return REGISTERED_PARAMETER_NAMES;
786        }
787
788        
789        /**
790         * Returns the scope. Corresponds to the optional {@code scope}
791         * parameter.
792         *
793         * @return The scope, {@code null} for a {@link #isSigned signed
794         *         request}.
795         */
796        public Scope getScope() {
797
798                return scope;
799        }
800        
801        
802        /**
803         * Returns the client notification token, required for the CIBA ping
804         * and push token delivery modes. Corresponds to the
805         * {@code client_notification_token} parameter.
806         *
807         * @return The client notification token, {@code null} if not
808         *         specified.
809         */
810        public BearerAccessToken getClientNotificationToken() {
811                
812                return clientNotificationToken;
813        }
814        
815        
816        /**
817         * Returns the requested Authentication Context Class Reference values.
818         * Corresponds to the optional {@code acr_values} parameter.
819         *
820         * @return The requested ACR values, {@code null} if not specified.
821         */
822        public List<ACR> getACRValues() {
823                
824                return acrValues;
825        }
826        
827        
828        /**
829         * Returns the login hint token string, containing information
830         * identifying the end-user for whom authentication is being requested.
831         * Corresponds to the {@code login_hint_token} parameter.
832         *
833         * @return The login hint token string, {@code null} if not
834         *         specified.
835         */
836        public String getLoginHintTokenString() {
837                
838                return loginHintTokenString;
839        }
840        
841        
842        /**
843         * Returns the ID Token hint, passed as a hint to identify the end-user
844         * for whom authentication is being requested. Corresponds to the
845         * {@code id_token_hint} parameter.
846         *
847         * @return The ID Token hint, {@code null} if not specified.
848         */
849        public JWT getIDTokenHint() {
850                
851                return idTokenHint;
852        }
853        
854        
855        /**
856         * Returns the login hint (email address, phone number, etc), about the
857         * end-user for whom authentication is being requested. Corresponds to
858         * the {@code login_hint} parameter.
859         *
860         * @return The login hint, {@code null} if not specified.
861         */
862        public String getLoginHint() {
863                
864                return loginHint;
865        }
866        
867        
868        /**
869         * Returns the human-readable binding message for the display at the
870         * consumption and authentication devices. Corresponds to the
871         * {@code binding_message} parameter.
872         *
873         * @return The binding message, {@code null} if not specified.
874         */
875        public String getBindingMessage() {
876                
877                return bindingMessage;
878        }
879        
880        
881        /**
882         * Returns the user secret code (password, PIN, etc) to authorise the
883         * CIBA request with the authentication device. Corresponds to the
884         * {@code user_code} parameter.
885         *
886         * @return The user code, {@code null} if not specified.
887         */
888        public Secret getUserCode() {
889                
890                return userCode;
891        }
892        
893        
894        /**
895         * Returns the requested expiration for the {@code auth_req_id}.
896         * Corresponds to the {@code requested_expiry} parameter.
897         *
898         * @return The required expiry (as positive integer), {@code null} if
899         *         not specified.
900         */
901        public Integer getRequestedExpiry() {
902                
903                return requestedExpiry;
904        }
905        
906        
907        /**
908         * Returns the individual OpenID claims to be returned. Corresponds to
909         * the optional {@code claims} parameter.
910         *
911         * @return The individual claims to be returned, {@code null} if not
912         *         specified.
913         */
914        public OIDCClaimsRequest getOIDCClaims() {
915                
916                return claims;
917        }
918        
919        
920        /**
921         * Returns the additional custom parameters.
922         *
923         * @return The additional custom parameters as a unmodifiable map,
924         *         empty map if none.
925         */
926        public Map<String, List<String>> getCustomParameters() {
927                
928                return customParams;
929        }
930        
931        
932        /**
933         * Returns the specified custom parameter.
934         *
935         * @param name The parameter name. Must not be {@code null}.
936         *
937         * @return The parameter value(s), {@code null} if not specified.
938         */
939        public List<String> getCustomParameter(final String name) {
940                
941                return customParams.get(name);
942        }
943        
944        
945        /**
946         * Returns {@code true} if this request is signed.
947         *
948         * @return {@code true} for a signed request, {@code false} for a plain
949         *         request.
950         */
951        public boolean isSigned() {
952                
953                return signedRequest != null;
954        }
955        
956        
957        /**
958         * Returns the JWT for a signed request.
959         *
960         * @return The request JWT.
961         */
962        public SignedJWT getRequestJWT() {
963                
964                return signedRequest;
965        }
966        
967        
968        /**
969         * Returns the for parameters for this CIBA request. Parameters which
970         * are part of the client authentication are not included.
971         *
972         * @return The parameters.
973         */
974        public Map<String, List<String>> toParameters() {
975                
976                // Put custom params first, so they may be overwritten by std params
977                Map<String, List<String>> params = new LinkedHashMap<>(getCustomParameters());
978                
979                if (isSigned()) {
980                        params.put("request", Collections.singletonList(signedRequest.serialize()));
981                        return params;
982                }
983                
984                params.put("scope", Collections.singletonList(getScope().toString()));
985                
986                if (getClientNotificationToken() != null) {
987                        params.put("client_notification_token", Collections.singletonList(getClientNotificationToken().getValue()));
988                }
989                if (getACRValues() != null) {
990                        params.put("acr_values", Identifier.toStringList(getACRValues()));
991                }
992                if (getLoginHintTokenString() != null) {
993                        params.put("login_hint_token", Collections.singletonList(getLoginHintTokenString()));
994                }
995                if (getIDTokenHint() != null) {
996                        params.put("id_token_hint", Collections.singletonList(getIDTokenHint().serialize()));
997                }
998                if (getLoginHint() != null) {
999                        params.put("login_hint", Collections.singletonList(getLoginHint()));
1000                }
1001                if (getBindingMessage() != null) {
1002                        params.put("binding_message", Collections.singletonList(getBindingMessage()));
1003                }
1004                if (getUserCode() != null) {
1005                        params.put("user_code", Collections.singletonList(getUserCode().getValue()));
1006                }
1007                if (getRequestedExpiry() != null) {
1008                        params.put("requested_expiry", Collections.singletonList(getRequestedExpiry().toString()));
1009                }
1010                if (getOIDCClaims() != null) {
1011                        params.put("claims", Collections.singletonList(getOIDCClaims().toJSONString()));
1012                }
1013                
1014                return params;
1015        }
1016        
1017        
1018        /**
1019         * Returns the parameters for this CIBA request as a JSON Web Token
1020         * (JWT) claims set. Intended for creating a signed CIBA request.
1021         *
1022         * @return The parameters as JWT claim set.
1023         */
1024        public JWTClaimsSet toJWTClaimsSet() {
1025                
1026                if (isSigned()) {
1027                        throw new IllegalStateException();
1028                }
1029                
1030                return JWTClaimsSetUtils.toJWTClaimsSet(toParameters());
1031        }
1032        
1033        
1034        /**
1035         * Returns the matching HTTP request.
1036         *
1037         * @return The HTTP request.
1038         */
1039        @Override
1040        public HTTPRequest toHTTPRequest() {
1041
1042                if (getEndpointURI() == null)
1043                        throw new SerializeException("The endpoint URI is not specified");
1044
1045                HTTPRequest httpRequest = new HTTPRequest(HTTPRequest.Method.POST, getEndpointURI());
1046                httpRequest.setEntityContentType(ContentType.APPLICATION_URLENCODED);
1047
1048                getClientAuthentication().applyTo(httpRequest);
1049
1050                Map<String, List<String>> params = httpRequest.getQueryParameters();
1051                params.putAll(toParameters());
1052                httpRequest.setQuery(URLUtils.serializeParameters(params));
1053                
1054                return httpRequest;
1055        }
1056
1057        
1058        /**
1059         * Parses a CIBA request from the specified HTTP request.
1060         *
1061         * @param httpRequest The HTTP request. Must not be {@code null}.
1062         *
1063         * @return The CIBA request.
1064         *
1065         * @throws ParseException If parsing failed.
1066         */
1067        public static CIBARequest parse(final HTTPRequest httpRequest) throws ParseException {
1068
1069                // Only HTTP POST accepted
1070                URI uri = httpRequest.getURI();
1071                httpRequest.ensureMethod(HTTPRequest.Method.POST);
1072                httpRequest.ensureEntityContentType(ContentType.APPLICATION_URLENCODED);
1073                
1074                ClientAuthentication clientAuth = ClientAuthentication.parse(httpRequest);
1075                
1076                if (clientAuth == null) {
1077                        throw new ParseException("Missing required client authentication");
1078                }
1079                
1080                Map<String, List<String>> params = httpRequest.getQueryParameters();
1081                
1082                String v;
1083                
1084                if (params.containsKey("request")) {
1085                        // Signed request
1086                        v = MultivaluedMapUtils.getFirstValue(params, "request");
1087                        
1088                        if (StringUtils.isBlank(v)) {
1089                                throw new ParseException("Empty request parameter");
1090                        }
1091                        
1092                        SignedJWT signedRequest;
1093                        try {
1094                                signedRequest = SignedJWT.parse(v);
1095                        } catch (java.text.ParseException e) {
1096                                throw new ParseException("Invalid request JWT: " + e.getMessage(), e);
1097                        }
1098                        
1099                        try {
1100                                return new CIBARequest(uri, clientAuth, signedRequest);
1101                        } catch (IllegalArgumentException e) {
1102                                throw new ParseException(e.getMessage(), e);
1103                        }
1104                }
1105                
1106                
1107                // Plain request
1108                
1109                // Parse required scope
1110                v = MultivaluedMapUtils.getFirstValue(params, "scope");
1111                Scope scope = Scope.parse(v);
1112
1113                v = MultivaluedMapUtils.getFirstValue(params, "client_notification_token");
1114                BearerAccessToken clientNotificationToken = null;
1115                if (StringUtils.isNotBlank(v)) {
1116                        clientNotificationToken = new BearerAccessToken(v);
1117                }
1118                
1119                v = MultivaluedMapUtils.getFirstValue(params, "acr_values");
1120                List<ACR> acrValues = null;
1121                if (StringUtils.isNotBlank(v)) {
1122                        acrValues = new LinkedList<>();
1123                        StringTokenizer st = new StringTokenizer(v, " ");
1124                        while (st.hasMoreTokens()) {
1125                                acrValues.add(new ACR(st.nextToken()));
1126                        }
1127                }
1128                
1129                String loginHintTokenString = MultivaluedMapUtils.getFirstValue(params, "login_hint_token");
1130                
1131                v = MultivaluedMapUtils.getFirstValue(params, "id_token_hint");
1132                JWT idTokenHint = null;
1133                if (StringUtils.isNotBlank(v)) {
1134                        try {
1135                                idTokenHint = JWTParser.parse(v);
1136                        } catch (java.text.ParseException e) {
1137                                throw new ParseException("Invalid id_token_hint parameter: " + e.getMessage());
1138                        }
1139                }
1140                
1141                String loginHint = MultivaluedMapUtils.getFirstValue(params, "login_hint");
1142                
1143                v = MultivaluedMapUtils.getFirstValue(params, "user_code");
1144                
1145                Secret userCode = null;
1146                if (StringUtils.isNotBlank(v)) {
1147                        userCode = new Secret(v);
1148                }
1149                
1150                String bindingMessage = MultivaluedMapUtils.getFirstValue(params, "binding_message");
1151                
1152                v = MultivaluedMapUtils.getFirstValue(params, "requested_expiry");
1153                
1154                Integer requestedExpiry = null;
1155                if (StringUtils.isNotBlank(v)) {
1156                        try {
1157                                requestedExpiry = Integer.valueOf(v);
1158                        } catch (NumberFormatException e) {
1159                                throw new ParseException("The requested_expiry parameter must be an integer");
1160                        }
1161                }
1162                
1163                v = MultivaluedMapUtils.getFirstValue(params, "claims");
1164                OIDCClaimsRequest claims = null;
1165                if (StringUtils.isNotBlank(v)) {
1166                        try {
1167                                claims = OIDCClaimsRequest.parse(v);
1168                        } catch (ParseException e) {
1169                                throw new ParseException("Invalid claims parameter: " + e.getMessage(), e);
1170                        }
1171                }
1172                
1173                // Parse additional custom parameters
1174                Map<String,List<String>> customParams = null;
1175                
1176                for (Map.Entry<String,List<String>> p: params.entrySet()) {
1177                        
1178                        if (! REGISTERED_PARAMETER_NAMES.contains(p.getKey()) && ! clientAuth.getFormParameterNames().contains(p.getKey())) {
1179                                // We have a custom parameter
1180                                if (customParams == null) {
1181                                        customParams = new HashMap<>();
1182                                }
1183                                customParams.put(p.getKey(), p.getValue());
1184                        }
1185                }
1186
1187                try {
1188                        return new CIBARequest(
1189                                uri, clientAuth,
1190                                scope, clientNotificationToken, acrValues,
1191                                loginHintTokenString, idTokenHint, loginHint,
1192                                bindingMessage, userCode, requestedExpiry,
1193                                claims,
1194                                customParams);
1195                } catch (IllegalArgumentException e) {
1196                        throw new ParseException(e.getMessage());
1197                }
1198        }
1199}