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