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 com.nimbusds.common.contenttype.ContentType;
022import com.nimbusds.jose.JWSObject;
023import com.nimbusds.jwt.JWT;
024import com.nimbusds.jwt.JWTClaimsSet;
025import com.nimbusds.jwt.JWTParser;
026import com.nimbusds.jwt.SignedJWT;
027import com.nimbusds.langtag.LangTag;
028import com.nimbusds.langtag.LangTagException;
029import com.nimbusds.langtag.LangTagUtils;
030import com.nimbusds.oauth2.sdk.AbstractAuthenticatedRequest;
031import com.nimbusds.oauth2.sdk.ParseException;
032import com.nimbusds.oauth2.sdk.Scope;
033import com.nimbusds.oauth2.sdk.SerializeException;
034import com.nimbusds.oauth2.sdk.auth.ClientAuthentication;
035import com.nimbusds.oauth2.sdk.auth.Secret;
036import com.nimbusds.oauth2.sdk.http.HTTPRequest;
037import com.nimbusds.oauth2.sdk.id.Identifier;
038import com.nimbusds.oauth2.sdk.rar.AuthorizationDetail;
039import com.nimbusds.oauth2.sdk.token.BearerAccessToken;
040import com.nimbusds.oauth2.sdk.util.*;
041import com.nimbusds.openid.connect.sdk.OIDCClaimsRequest;
042import com.nimbusds.openid.connect.sdk.claims.ACR;
043import net.jcip.annotations.Immutable;
044import net.minidev.json.JSONObject;
045
046import java.net.URI;
047import java.util.*;
048
049
050/**
051 * <p>CIBA request to an OpenID provider / OAuth 2.0 authorisation server
052 * backend authentication endpoint. Supports plan as well as signed (JWT)
053 * requests.
054 *
055 * <p>Example HTTP request:
056 * 
057 * <pre>
058 * POST /bc-authorize HTTP/1.1
059 * Host: server.example.com
060 * Content-Type: application/x-www-form-urlencoded
061 *
062 * scope=openid%20email%20example-scope&amp;
063 * client_notification_token=8d67dc78-7faa-4d41-aabd-67707b374255&amp;
064 * binding_message=W4SCT&amp;
065 * login_hint_token=eyJraWQiOiJsdGFjZXNidyIsImFsZyI6IkVTMjU2In0.eyJ
066 * zdWJfaWQiOnsic3ViamVjdF90eXBlIjoicGhvbmUiLCJwaG9uZSI6IisxMzMwMjg
067 * xODAwNCJ9fQ.Kk8jcUbHjJAQkRSHyDuFQr3NMEOSJEZc85VfER74tX6J9CuUllr8
068 * 9WKUHUR7MA0-mWlptMRRhdgW1ZDt7g1uwQ&amp;
069 * client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3A
070 * client-assertion-type%3Ajwt-bearer&amp;
071 * client_assertion=eyJraWQiOiJsdGFjZXNidyIsImFsZyI6IkVTMjU2In0.eyJ
072 * pc3MiOiJzNkJoZFJrcXQzIiwic3ViIjoiczZCaGRSa3F0MyIsImF1ZCI6Imh0dHB
073 * zOi8vc2VydmVyLmV4YW1wbGUuY29tIiwianRpIjoiYmRjLVhzX3NmLTNZTW80RlN
074 * 6SUoyUSIsImlhdCI6MTUzNzgxOTQ4NiwiZXhwIjoxNTM3ODE5Nzc3fQ.Ybr8mg_3
075 * E2OptOSsA8rnelYO_y1L-yFaF_j1iemM3ntB61_GN3APe5cl_-5a6cvGlP154XAK
076 * 7fL-GaZSdnd9kg
077 * </pre>
078 *
079 * <p>Related specifications:
080 *
081 * <ul>
082 *      <li>OpenID Connect CIBA Flow - Core 1.0
083 *      <li>Financial-grade API: Client Initiated Backchannel Authentication
084 *          Profile (draft 02)
085 * </ul>
086 */
087@Immutable
088public class CIBARequest extends AbstractAuthenticatedRequest {
089        
090        
091        /**
092         * The maximum allowed length of a client notification token.
093         */
094        public static final int CLIENT_NOTIFICATION_TOKEN_MAX_LENGTH = 1024;
095        
096
097        /**
098         * The registered parameter names.
099         */
100        private static final Set<String> REGISTERED_PARAMETER_NAMES;
101
102        static {
103                Set<String> p = new HashSet<>();
104
105                // Plain
106                p.add("scope");
107                p.add("client_notification_token");
108                p.add("acr_values");
109                p.add("login_hint_token");
110                p.add("id_token_hint");
111                p.add("login_hint");
112                p.add("binding_message");
113                p.add("user_code");
114                p.add("requested_expiry");
115                p.add("claims");
116                p.add("claims_locales");
117                p.add("purpose");
118                p.add("authorization_details");
119                p.add("resource");
120                p.add("request_context");
121
122                // Signed JWT
123                p.add("request");
124
125                REGISTERED_PARAMETER_NAMES = Collections.unmodifiableSet(p);
126        }
127
128        
129        /**
130         * The scope (required), must contain {@code openid}.
131         */
132        private final Scope scope;
133
134        
135        /**
136         * The client notification token, required for the CIBA ping and push
137         * token delivery modes.
138         */
139        private final BearerAccessToken clientNotificationToken;
140        
141        
142        /**
143         * Requested Authentication Context Class Reference values (optional).
144         */
145        private final List<ACR> acrValues;
146        
147        
148        /**
149         * A token containing information identifying the end-user for whom
150         * authentication is being requested (optional).
151         */
152        private final LoginHintToken loginHintToken;
153        
154        
155        /**
156         * Previously issued ID token passed as a hint to identify the end-user
157         * for whom authentication is being requested (optional).
158         */
159        private final JWT idTokenHint;
160        
161        
162        /**
163         * Login hint (email address, phone number, etc.) about the end-user
164         * for whom authentication is being requested (optional).
165         */
166        private final String loginHint;
167        
168        
169        /**
170         * Human-readable binding message for the display at the consumption
171         * and authentication devices (optional).
172         */
173        private final String bindingMessage;
174        
175        
176        /**
177         * User secret code (password, PIN, etc.) to authorise the CIBA request
178         * with the authentication device (optional).
179         */
180        private final Secret userCode;
181        
182        
183        /**
184         * Requested expiration for the {@code auth_req_id} (optional).
185         */
186        private final Integer requestedExpiry;
187        
188        
189        /**
190         * Individual claims to be returned (optional).
191         */
192        private final OIDCClaimsRequest claims;
193        
194        
195        /**
196         * The end-user's preferred languages and scripts for claims being
197         * returned (optional).
198         */
199        private final List<LangTag> claimsLocales;
200        
201        
202        /**
203         * The transaction specific purpose, for use in OpenID Connect Identity
204         * Assurance (optional).
205         */
206        private final String purpose;
207
208
209        /**
210         * The RAR details (optional).
211         */
212        private final List<AuthorizationDetail> authorizationDetails;
213        
214        
215        /**
216         * The resource URI(s) (optional).
217         */
218        private final List<URI> resources;
219
220
221        /**
222         * The request context (optional).
223         */
224        private final JSONObject requestContext;
225        
226        
227        /**
228         * Custom parameters.
229         */
230        private final Map<String,List<String>> customParams;
231        
232        
233        /**
234         * The JWT for a signed request.
235         */
236        private final SignedJWT signedRequest;
237        
238
239        /**
240         * Builder for constructing CIBA requests.
241         */
242        public static class Builder {
243
244                
245                /**
246                 * The endpoint URI (optional).
247                 */
248                private URI endpoint;
249                
250                
251                /**
252                 * The client authentication (required).
253                 */
254                private final ClientAuthentication clientAuth;
255                
256                
257                /**
258                 * The scope (required).
259                 */
260                private final Scope scope;
261                
262                
263                /**
264                 * The client notification type, required for the CIBA ping and
265                 * push token delivery modes.
266                 */
267                private BearerAccessToken clientNotificationToken;
268                
269                
270                /**
271                 * Requested Authentication Context Class Reference values
272                 * (optional).
273                 */
274                private List<ACR> acrValues;
275                
276                
277                /**
278                 * A token containing information identifying the end-user for
279                 * whom authentication is being requested (optional).
280                 */
281                private LoginHintToken loginHintToken;
282                
283                
284                /**
285                 * Previously issued ID token passed as a hint to identify the
286                 * end-user for whom authentication is being requested
287                 * (optional).
288                 */
289                private JWT idTokenHint;
290                
291                
292                /**
293                 * Identity hint (email address, phone number, etc.) about the
294                 * end-user for whom authentication is being requested
295                 * (optional).
296                 */
297                private String loginHint;
298                
299                
300                /**
301                 * Human-readable binding message for the display at the
302                 * consumption and authentication devices (optional).
303                 */
304                private String bindingMessage;
305                
306                
307                /**
308                 * User secret code (password, PIN, etc.) to authorise the CIBA
309                 * request with the authentication device (optional).
310                 */
311                private Secret userCode;
312                
313                
314                /**
315                 * Requested expiration for the {@code auth_req_id} (optional).
316                 */
317                private Integer requestedExpiry;
318                
319                
320                /**
321                 * Individual claims to be returned (optional).
322                 */
323                private OIDCClaimsRequest claims;
324                
325                
326                /**
327                 * The end-user's preferred languages and scripts for claims
328                 * being returned (optional).
329                 */
330                private List<LangTag> claimsLocales;
331                
332                
333                /**
334                 * The transaction specific purpose (optional).
335                 */
336                private String purpose;
337
338
339                /**
340                 * The RAR details (optional).
341                 */
342                private List<AuthorizationDetail> authorizationDetails;
343                
344                
345                /**
346                 * The resource URI(s) (optional).
347                 */
348                private List<URI> resources;
349
350
351                /**
352                 * The request context (optional).
353                 */
354                private JSONObject requestContext;
355                
356                
357                /**
358                 * Custom parameters.
359                 */
360                private Map<String,List<String>> customParams = new HashMap<>();
361                
362                
363                /**
364                 * The JWT for a signed request.
365                 */
366                private final SignedJWT signedRequest;
367
368                
369                /**
370                 * Creates a new CIBA request builder.
371                 *
372                 * @param clientAuth The client authentication. Must not be
373                 *                   {@code null}.
374                 * @param scope      The requested scope, {@code null} if not
375                 *                   specified.
376                 */
377                public Builder(final ClientAuthentication clientAuth,
378                               final Scope scope) {
379                        
380                        this.clientAuth = Objects.requireNonNull(clientAuth);
381                        this.scope = scope;
382                        signedRequest = null;
383                }
384                
385                
386                /**
387                 * Creates a new CIBA signed request builder.
388                 *
389                 * @param clientAuth    The client authentication. Must not be
390                 *                      {@code null}.
391                 * @param signedRequest The signed request JWT. Must not be
392                 *                      {@code null}.
393                 */
394                public Builder(final ClientAuthentication clientAuth,
395                               final SignedJWT signedRequest) {
396                        
397                        this.clientAuth = Objects.requireNonNull(clientAuth);
398                        this.signedRequest = Objects.requireNonNull(signedRequest);
399                        scope = null;
400                }
401                
402
403                /**
404                 * Creates a new CIBA request builder from the specified
405                 * request.
406                 *
407                 * @param request The CIBA request. Must not be {@code null}.
408                 */
409                public Builder(final CIBARequest request) {
410                        
411                        endpoint = request.getEndpointURI();
412                        clientAuth = request.getClientAuthentication();
413                        scope = request.getScope();
414                        clientNotificationToken = request.getClientNotificationToken();
415                        acrValues = request.getACRValues();
416                        loginHintToken = request.getLoginHintToken();
417                        idTokenHint = request.getIDTokenHint();
418                        loginHint = request.getLoginHint();
419                        bindingMessage = request.getBindingMessage();
420                        userCode = request.getUserCode();
421                        requestedExpiry = request.getRequestedExpiry();
422                        claims = request.getOIDCClaims();
423                        claimsLocales = request.getClaimsLocales();
424                        purpose = request.getPurpose();
425                        authorizationDetails = request.getAuthorizationDetails();
426                        resources = request.getResources();
427                        requestContext = request.getContext();
428                        customParams = request.getCustomParameters();
429                        signedRequest = request.getRequestJWT();
430                }
431                
432                
433                /**
434                 * Sets the client notification token, required for the CIBA
435                 * ping and push token delivery modes. Corresponds to the
436                 * {@code client_notification_token} parameter.
437                 *
438                 * @param token The client notification token, {@code null} if
439                 *              not specified.
440                 *
441                 * @return This builder.
442                 */
443                public Builder clientNotificationToken(final BearerAccessToken token) {
444                        this.clientNotificationToken = token;
445                        return this;
446                }
447
448                
449                /**
450                 * Sets the requested Authentication Context Class Reference
451                 * values. Corresponds to the optional {@code acr_values}
452                 * parameter.
453                 *
454                 * @param acrValues The requested ACR values, {@code null} if
455                 *                  not specified.
456                 *
457                 * @return This builder.
458                 */
459                public Builder acrValues(final List<ACR> acrValues) {
460                        this.acrValues = acrValues;
461                        return this;
462                }
463                
464                
465                /**
466                 * Sets the login hint token, containing information
467                 * identifying the end-user for whom authentication is being
468                 * requested. Corresponds to the {@code login_hint_token}
469                 * parameter.
470                 *
471                 * @param loginHintToken The login hint token, {@code null} if
472                 *                       not specified.
473                 *
474                 * @return This builder.
475                 */
476                public Builder loginHintToken(final LoginHintToken loginHintToken) {
477                        this.loginHintToken = loginHintToken;
478                        return this;
479                }
480
481
482                /**
483                 * Sets the login hint token string, containing information
484                 * identifying the end-user for whom authentication is being
485                 * requested. Corresponds to the {@code login_hint_token}
486                 * parameter.
487                 *
488                 * @param loginHintTokenString The login hint token string,
489                 *                             {@code null} if not specified.
490                 *
491                 * @return This builder.
492                 */
493                @Deprecated
494                public Builder loginHintTokenString(final String loginHintTokenString) {
495                        if (loginHintTokenString != null) {
496                                this.loginHintToken = new LoginHintToken(loginHintTokenString);
497                        } else {
498                                this.loginHintToken = null;
499                        }
500                        return this;
501                }
502                
503                
504                /**
505                 * Sets the ID Token hint, passed as a hint to identify the
506                 * end-user for whom authentication is being requested.
507                 * Corresponds to the {@code id_token_hint} parameter.
508                 *
509                 * @param idTokenHint The ID Token hint, {@code null} if not
510                 *                    specified.
511                 *
512                 * @return This builder.
513                 */
514                public Builder idTokenHint(final JWT idTokenHint) {
515                        this.idTokenHint = idTokenHint;
516                        return this;
517                }
518                
519                
520                /**
521                 * Sets the login hint (email address, phone number, etc.),
522                 * about the end-user for whom authentication is being
523                 * requested. Corresponds to the {@code login_hint} parameter.
524                 *
525                 * @param loginHint The login hint, {@code null} if not
526                 *                  specified.
527                 *
528                 * @return This builder.
529                 */
530                public Builder loginHint(final String loginHint) {
531                        this.loginHint = loginHint;
532                        return this;
533                }
534                
535                
536                /**
537                 * Sets the human-readable binding message for the display at
538                 * the consumption and authentication devices. Corresponds to
539                 * the {@code binding_message} parameter.
540                 *
541                 * @param bindingMessage The binding message, {@code null} if
542                 *                       not specified.
543                 *
544                 * @return This builder.
545                 */
546                public Builder bindingMessage(final String bindingMessage) {
547                        this.bindingMessage = bindingMessage;
548                        return this;
549                }
550                
551                
552                /**
553                 * Gets the user secret code (password, PIN, etc.) to authorise
554                 * the CIBA request with the authentication device. Corresponds
555                 * to the {@code user_code} parameter.
556                 *
557                 * @param userCode The user code, {@code null} if not
558                 *                 specified.
559                 *
560                 * @return This builder.
561                 */
562                public Builder userCode(final Secret userCode) {
563                        this.userCode = userCode;
564                        return this;
565                }
566                
567                
568                /**
569                 * Sets the requested expiration for the {@code auth_req_id}.
570                 * Corresponds to the {@code requested_expiry} parameter.
571                 *
572                 * @param requestedExpiry The required expiry (as positive
573                 *                        integer), {@code null} if not
574                 *                        specified.
575                 *
576                 * @return This builder.
577                 */
578                public Builder requestedExpiry(final Integer requestedExpiry) {
579                        this.requestedExpiry = requestedExpiry;
580                        return this;
581                }
582                
583                
584                /**
585                 * Sets the individual OpenID claims to be returned.
586                 * Corresponds to the optional {@code claims} parameter.
587                 *
588                 * @param claims The individual OpenID claims to be returned,
589                 *               {@code null} if not specified.
590                 *
591                 * @return This builder.
592                 */
593                public Builder claims(final OIDCClaimsRequest claims) {
594                        
595                        this.claims = claims;
596                        return this;
597                }
598                
599                
600                /**
601                 * Sets the end-user's preferred languages and scripts for the
602                 * claims being returned, ordered by preference. Corresponds to
603                 * the optional {@code claims_locales} parameter.
604                 *
605                 * @param claimsLocales The preferred claims locales,
606                 *                      {@code null} if not specified.
607                 *
608                 * @return This builder.
609                 */
610                public Builder claimsLocales(final List<LangTag> claimsLocales) {
611                        
612                        this.claimsLocales = claimsLocales;
613                        return this;
614                }
615                
616                
617                /**
618                 * Sets the transaction specific purpose. Corresponds to the
619                 * optional {@code purpose} parameter.
620                 *
621                 * @param purpose The purpose, {@code null} if not specified.
622                 *
623                 * @return This builder.
624                 */
625                public Builder purpose(final String purpose) {
626                        
627                        this.purpose = purpose;
628                        return this;
629                }
630
631
632                /**
633                 * Sets the Rich Authorisation Request (RAR) details.
634                 *
635                 * @param authorizationDetails The authorisation details,
636                 *                             {@code null} if not specified.
637                 *
638                 * @return This builder.
639                 */
640                public Builder authorizationDetails(final List<AuthorizationDetail> authorizationDetails) {
641                        this.authorizationDetails = authorizationDetails;
642                        return this;
643                }
644                
645                
646                /**
647                 * Sets the resource server URI.
648                 *
649                 * @param resource The resource URI, {@code null} if not
650                 *                 specified.
651                 *
652                 * @return This builder.
653                 */
654                public Builder resource(final URI resource) {
655                        if (resource != null) {
656                                this.resources = Collections.singletonList(resource);
657                        } else {
658                                this.resources = null;
659                        }
660                        return this;
661                }
662                
663                
664                /**
665                 * Sets the resource server URI(s).
666                 *
667                 * @param resources The resource URI(s), {@code null} if not
668                 *                  specified.
669                 *
670                 * @return This builder.
671                 */
672                public Builder resources(final URI ... resources) {
673                        if (resources != null) {
674                                this.resources = Arrays.asList(resources);
675                        } else {
676                                this.resources = null;
677                        }
678                        return this;
679                }
680
681
682                /**
683                 * Sets the request context.
684                 *
685                 * @param requestContext The request context, {@code null} if
686                 *                       not specified.
687                 *
688                 * @return This builder.
689                 */
690                public Builder context(final JSONObject requestContext) {
691                        this.requestContext = requestContext;
692                        return this;
693                }
694                
695                
696                /**
697                 * Sets a custom parameter.
698                 *
699                 * @param name   The parameter name. Must not be {@code null}.
700                 * @param values The parameter values, {@code null} if not
701                 *               specified.
702                 *
703                 * @return This builder.
704                 */
705                public Builder customParameter(final String name, final String ... values) {
706                        
707                        if (values == null || values.length == 0) {
708                                customParams.remove(name);
709                        } else {
710                                customParams.put(name, Arrays.asList(values));
711                        }
712                        
713                        return this;
714                }
715                
716                
717                /**
718                 * Sets the URI of the CIBA endpoint.
719                 *
720                 * @param endpoint The URI of the CIBA endpoint. May be
721                 *                 {@code null} if the {@link #toHTTPRequest()}
722                 *                 method is not going to be used.
723                 *
724                 * @return This builder.
725                 */
726                public Builder endpointURI(final URI endpoint) {
727                        
728                        this.endpoint = endpoint;
729                        return this;
730                }
731                
732                
733                /**
734                 * Builds a new CIBA request.
735                 *
736                 * @return The CIBA request.
737                 */
738                public CIBARequest build() {
739                        
740                        try {
741                                if (signedRequest != null) {
742                                        return new CIBARequest(
743                                                endpoint,
744                                                clientAuth,
745                                                signedRequest
746                                        );
747                                }
748                                
749                                // Plain request
750                                return new CIBARequest(
751                                        endpoint,
752                                        clientAuth,
753                                        scope,
754                                        clientNotificationToken,
755                                        acrValues,
756                                        loginHintToken,
757                                        idTokenHint,
758                                        loginHint,
759                                        bindingMessage,
760                                        userCode,
761                                        requestedExpiry,
762                                        claims,
763                                        claimsLocales,
764                                        purpose,
765                                        authorizationDetails,
766                                        resources,
767                                        requestContext,
768                                        customParams
769                                );
770                        } catch (IllegalArgumentException e) {
771                                throw new IllegalArgumentException(e.getMessage(), e);
772                        }
773                }
774        }
775        
776        
777        /**
778         * Creates a new CIBA request.
779         *
780         * @param endpoint                The URI of the CIBA endpoint. May be
781         *                                {@code null} if the
782         *                                {@link #toHTTPRequest()} method is
783         *                                not going to be used.
784         * @param clientAuth              The client authentication. Must not
785         *                                be {@code null}.
786         * @param scope                   The requested scope. Must not be
787         *                                empty or {@code null}.
788         * @param clientNotificationToken The client notification token,
789         *                                {@code null} if not specified.
790         * @param acrValues               The requested ACR values,
791         *                                {@code null} if not specified.
792         * @param loginHintTokenString    The login hint token string,
793         *                                {@code null} if not specified.
794         * @param idTokenHint             The ID Token hint, {@code null} if
795         *                                not specified.
796         * @param loginHint               The login hint, {@code null} if not
797         *                                specified.
798         * @param bindingMessage          The binding message, {@code null} if
799         *                                not specified.
800         * @param userCode                The user code, {@code null} if not
801         *                                specified.
802         * @param requestedExpiry         The required expiry (as positive
803         *                                integer), {@code null} if not
804         *                                specified.
805         * @param customParams            Custom parameters, empty or
806         *                                {@code null} if not specified.
807         */
808        @Deprecated
809        public CIBARequest(final URI endpoint,
810                           final ClientAuthentication clientAuth,
811                           final Scope scope,
812                           final BearerAccessToken clientNotificationToken,
813                           final List<ACR> acrValues,
814                           final String loginHintTokenString,
815                           final JWT idTokenHint,
816                           final String loginHint,
817                           final String bindingMessage,
818                           final Secret userCode,
819                           final Integer requestedExpiry,
820                           final Map<String, List<String>> customParams) {
821                
822                this(endpoint, clientAuth,
823                        scope, clientNotificationToken, acrValues,
824                        loginHintTokenString, idTokenHint, loginHint,
825                        bindingMessage, userCode, requestedExpiry,
826                        null, customParams);
827        }
828        
829        
830        /**
831         * Creates a new CIBA request.
832         *
833         * @param endpoint                The URI of the CIBA endpoint. May be
834         *                                {@code null} if the
835         *                                {@link #toHTTPRequest()} method is
836         *                                not going to be used.
837         * @param clientAuth              The client authentication. Must not
838         *                                be {@code null}.
839         * @param scope                   The requested scope. Must not be
840         *                                empty or {@code null}.
841         * @param clientNotificationToken The client notification token,
842         *                                {@code null} if not specified.
843         * @param acrValues               The requested ACR values,
844         *                                {@code null} if not specified.
845         * @param loginHintTokenString    The login hint token string,
846         *                                {@code null} if not specified.
847         * @param idTokenHint             The ID Token hint, {@code null} if
848         *                                not specified.
849         * @param loginHint               The login hint, {@code null} if not
850         *                                specified.
851         * @param bindingMessage          The binding message, {@code null} if
852         *                                not specified.
853         * @param userCode                The user code, {@code null} if not
854         *                                specified.
855         * @param requestedExpiry         The required expiry (as positive
856         *                                integer), {@code null} if not
857         *                                specified.
858         * @param claims                  The individual claims to be returned,
859         *                                {@code null} if not specified.
860         * @param customParams            Custom parameters, empty or
861         *                                {@code null} if not specified.
862         */
863        @Deprecated
864        public CIBARequest(final URI endpoint,
865                           final ClientAuthentication clientAuth,
866                           final Scope scope,
867                           final BearerAccessToken clientNotificationToken,
868                           final List<ACR> acrValues,
869                           final String loginHintTokenString,
870                           final JWT idTokenHint,
871                           final String loginHint,
872                           final String bindingMessage,
873                           final Secret userCode,
874                           final Integer requestedExpiry,
875                           final OIDCClaimsRequest claims,
876                           final Map<String, List<String>> customParams) {
877                
878                this(endpoint, clientAuth,
879                        scope, clientNotificationToken, acrValues,
880                        loginHintTokenString, idTokenHint, loginHint,
881                        bindingMessage, userCode, requestedExpiry,
882                        claims, null, null,
883                        null,
884                        customParams);
885        }
886        
887        
888        /**
889         * Creates a new CIBA request.
890         *
891         * @param endpoint                The URI of the CIBA endpoint. May be
892         *                                {@code null} if the
893         *                                {@link #toHTTPRequest()} method is
894         *                                not going to be used.
895         * @param clientAuth              The client authentication. Must not
896         *                                be {@code null}.
897         * @param scope                   The requested scope. Must not be
898         *                                empty or {@code null}.
899         * @param clientNotificationToken The client notification token,
900         *                                {@code null} if not specified.
901         * @param acrValues               The requested ACR values,
902         *                                {@code null} if not specified.
903         * @param loginHintTokenString    The login hint token string,
904         *                                {@code null} if not specified.
905         * @param idTokenHint             The ID Token hint, {@code null} if
906         *                                not specified.
907         * @param loginHint               The login hint, {@code null} if not
908         *                                specified.
909         * @param bindingMessage          The binding message, {@code null} if
910         *                                not specified.
911         * @param userCode                The user code, {@code null} if not
912         *                                specified.
913         * @param requestedExpiry         The required expiry (as positive
914         *                                integer), {@code null} if not
915         *                                specified.
916         * @param claims                  The individual claims to be
917         *                                returned, {@code null} if not
918         *                                specified.
919         * @param claimsLocales           The preferred languages and scripts
920         *                                for claims being returned,
921         *                                {@code null} if not specified.
922         * @param purpose                 The transaction specific purpose,
923         *                                {@code null} if not specified.
924         * @param resources               The resource URI(s), {@code null} if
925         *                                not specified.
926         * @param customParams            Custom parameters, empty or
927         *                                {@code null} if not specified.
928         */
929        @Deprecated
930        public CIBARequest(final URI endpoint,
931                           final ClientAuthentication clientAuth,
932                           final Scope scope,
933                           final BearerAccessToken clientNotificationToken,
934                           final List<ACR> acrValues,
935                           final String loginHintTokenString,
936                           final JWT idTokenHint,
937                           final String loginHint,
938                           final String bindingMessage,
939                           final Secret userCode,
940                           final Integer requestedExpiry,
941                           final OIDCClaimsRequest claims,
942                           final List<LangTag> claimsLocales,
943                           final String purpose,
944                           final List<URI> resources,
945                           final Map<String, List<String>> customParams) {
946
947                this(endpoint, clientAuth,
948                        scope, clientNotificationToken, acrValues,
949                        loginHintTokenString, idTokenHint, loginHint,
950                        bindingMessage, userCode, requestedExpiry,
951                        claims, claimsLocales, purpose, null, resources,
952                        customParams);
953        }
954
955
956        /**
957         * Creates a new CIBA request.
958         *
959         * @param endpoint                The URI of the CIBA endpoint. May be
960         *                                {@code null} if the
961         *                                {@link #toHTTPRequest()} method is
962         *                                not going to be used.
963         * @param clientAuth              The client authentication. Must not
964         *                                be {@code null}.
965         * @param scope                   The requested scope. Must not be
966         *                                empty or {@code null}.
967         * @param clientNotificationToken The client notification token,
968         *                                {@code null} if not specified.
969         * @param acrValues               The requested ACR values,
970         *                                {@code null} if not specified.
971         * @param loginHintTokenString    The login hint token string,
972         *                                {@code null} if not specified.
973         * @param idTokenHint             The ID Token hint, {@code null} if
974         *                                not specified.
975         * @param loginHint               The login hint, {@code null} if not
976         *                                specified.
977         * @param bindingMessage          The binding message, {@code null} if
978         *                                not specified.
979         * @param userCode                The user code, {@code null} if not
980         *                                specified.
981         * @param requestedExpiry         The required expiry (as positive
982         *                                integer), {@code null} if not
983         *                                specified.
984         * @param claims                  The individual claims to be
985         *                                returned, {@code null} if not
986         *                                specified.
987         * @param claimsLocales           The preferred languages and scripts
988         *                                for claims being returned,
989         *                                {@code null} if not specified.
990         * @param purpose                 The transaction specific purpose,
991         *                                {@code null} if not specified.
992         * @param authorizationDetails    The Rich Authorisation Request (RAR)
993         *                                details, {@code null} if not
994         *                                specified.
995         * @param resources               The resource URI(s), {@code null} if
996         *                                not specified.
997         * @param customParams            Custom parameters, empty or
998         *                                {@code null} if not specified.
999         */
1000        @Deprecated
1001        public CIBARequest(final URI endpoint,
1002                           final ClientAuthentication clientAuth,
1003                           final Scope scope,
1004                           final BearerAccessToken clientNotificationToken,
1005                           final List<ACR> acrValues,
1006                           final String loginHintTokenString,
1007                           final JWT idTokenHint,
1008                           final String loginHint,
1009                           final String bindingMessage,
1010                           final Secret userCode,
1011                           final Integer requestedExpiry,
1012                           final OIDCClaimsRequest claims,
1013                           final List<LangTag> claimsLocales,
1014                           final String purpose,
1015                           final List<AuthorizationDetail> authorizationDetails,
1016                           final List<URI> resources,
1017                           final Map<String, List<String>> customParams) {
1018
1019                this(endpoint, clientAuth,
1020                        scope, clientNotificationToken, acrValues,
1021                        loginHintTokenString != null ? new LoginHintToken(loginHintTokenString) : null, idTokenHint, loginHint,
1022                        bindingMessage, userCode, requestedExpiry,
1023                        claims, claimsLocales, purpose, authorizationDetails, resources, null,
1024                        customParams);
1025        }
1026
1027
1028        /**
1029         * Creates a new CIBA request.
1030         *
1031         * @param endpoint                The URI of the CIBA endpoint. May be
1032         *                                {@code null} if the
1033         *                                {@link #toHTTPRequest()} method is
1034         *                                not going to be used.
1035         * @param clientAuth              The client authentication. Must not
1036         *                                be {@code null}.
1037         * @param scope                   The requested scope. Must not be
1038         *                                empty or {@code null}.
1039         * @param clientNotificationToken The client notification token,
1040         *                                {@code null} if not specified.
1041         * @param acrValues               The requested ACR values,
1042         *                                {@code null} if not specified.
1043         * @param loginHintToken          The login hint token, {@code null} if
1044         *                                not specified.
1045         * @param idTokenHint             The ID Token hint, {@code null} if
1046         *                                not specified.
1047         * @param loginHint               The login hint, {@code null} if not
1048         *                                specified.
1049         * @param bindingMessage          The binding message, {@code null} if
1050         *                                not specified.
1051         * @param userCode                The user code, {@code null} if not
1052         *                                specified.
1053         * @param requestedExpiry         The required expiry (as positive
1054         *                                integer), {@code null} if not
1055         *                                specified.
1056         * @param claims                  The individual claims to be
1057         *                                returned, {@code null} if not
1058         *                                specified.
1059         * @param claimsLocales           The preferred languages and scripts
1060         *                                for claims being returned,
1061         *                                {@code null} if not specified.
1062         * @param purpose                 The transaction specific purpose,
1063         *                                {@code null} if not specified.
1064         * @param authorizationDetails    The Rich Authorisation Request (RAR)
1065         *                                details, {@code null} if not
1066         *                                specified.
1067         * @param resources               The resource URI(s), {@code null} if
1068         *                                not specified.
1069         * @param requestContext          The request context, {@code null} if
1070         *                                not specified.
1071         * @param customParams            Custom parameters, empty or
1072         *                                {@code null} if not specified.
1073         */
1074        public CIBARequest(final URI endpoint,
1075                           final ClientAuthentication clientAuth,
1076                           final Scope scope,
1077                           final BearerAccessToken clientNotificationToken,
1078                           final List<ACR> acrValues,
1079                           final LoginHintToken loginHintToken,
1080                           final JWT idTokenHint,
1081                           final String loginHint,
1082                           final String bindingMessage,
1083                           final Secret userCode,
1084                           final Integer requestedExpiry,
1085                           final OIDCClaimsRequest claims,
1086                           final List<LangTag> claimsLocales,
1087                           final String purpose,
1088                           final List<AuthorizationDetail> authorizationDetails,
1089                           final List<URI> resources,
1090                           final JSONObject requestContext,
1091                           final Map<String, List<String>> customParams) {
1092
1093                super(endpoint, clientAuth);
1094
1095                this.scope = scope;
1096
1097                if (clientNotificationToken != null && clientNotificationToken.getValue().length() > CLIENT_NOTIFICATION_TOKEN_MAX_LENGTH) {
1098                        throw new IllegalArgumentException("The client notification token must not exceed " + CLIENT_NOTIFICATION_TOKEN_MAX_LENGTH + " chars");
1099                }
1100                this.clientNotificationToken = clientNotificationToken;
1101
1102                this.acrValues = acrValues;
1103
1104                // https://openid.net/specs/openid-client-initiated-backchannel-authentication-core-1_0-03.html#rfc.section.7.1
1105                // As in the CIBA flow the OP does not have an interaction with
1106                // the end-user through the consumption device, it is REQUIRED
1107                // that the Client provides one (and only one) of the hints
1108                // specified above in the authentication request, that is
1109                // "login_hint_token", "id_token_hint" or "login_hint".
1110                int numHints = 0;
1111
1112                if (loginHintToken != null) numHints++;
1113                this.loginHintToken = loginHintToken;
1114
1115                if (idTokenHint != null) numHints++;
1116                this.idTokenHint = idTokenHint;
1117
1118                if (loginHint != null) numHints++;
1119                this.loginHint = loginHint;
1120
1121                if (numHints != 1) {
1122                        throw new IllegalArgumentException("One user identity hist must be provided (login_hint_token, id_token_hint or login_hint)");
1123                }
1124
1125                this.bindingMessage = bindingMessage;
1126
1127                this.userCode = userCode;
1128
1129                if (requestedExpiry != null && requestedExpiry < 1) {
1130                        throw new IllegalArgumentException("The requested expiry must be a positive integer");
1131                }
1132                this.requestedExpiry = requestedExpiry;
1133
1134                this.claims = claims;
1135
1136                if (claimsLocales != null) {
1137                        this.claimsLocales = Collections.unmodifiableList(claimsLocales);
1138                } else {
1139                        this.claimsLocales = null;
1140                }
1141
1142                this.purpose = purpose;
1143
1144                this.authorizationDetails = authorizationDetails;
1145
1146                this.resources = ResourceUtils.ensureLegalResourceURIs(resources);
1147
1148                this.requestContext = requestContext;
1149
1150                this.customParams = customParams != null ? customParams : Collections.<String, List<String>>emptyMap();
1151
1152                signedRequest = null;
1153        }
1154        
1155        
1156        /**
1157         * Creates a new CIBA signed request.
1158         *
1159         * @param endpoint      The URI of the CIBA endpoint. May be
1160         *                      {@code null} if the {@link #toHTTPRequest()}
1161         *                      method is not going to be used.
1162         * @param clientAuth    The client authentication. Must not be
1163         *                      {@code null}.
1164         * @param signedRequest The signed request JWT. Must not be
1165         *                      {@code null}.
1166         */
1167        public CIBARequest(final URI endpoint,
1168                           final ClientAuthentication clientAuth,
1169                           final SignedJWT signedRequest) {
1170                
1171                super(endpoint, clientAuth);
1172
1173                if (JWSObject.State.UNSIGNED.equals(signedRequest.getState())) {
1174                        throw new IllegalArgumentException("The request JWT must be in a signed state");
1175                }
1176                this.signedRequest = signedRequest;
1177                
1178                scope = null;
1179                clientNotificationToken = null;
1180                acrValues = null;
1181                loginHintToken = null;
1182                idTokenHint = null;
1183                loginHint = null;
1184                bindingMessage = null;
1185                userCode = null;
1186                requestedExpiry = null;
1187                claims = null;
1188                claimsLocales = null;
1189                authorizationDetails = null;
1190                purpose = null;
1191                resources = null;
1192                requestContext = null;
1193                customParams = Collections.emptyMap();
1194        }
1195
1196        
1197        /**
1198         * Returns the registered (standard) CIBA request parameter names.
1199         *
1200         * @return The registered CIBA request parameter names, as an
1201         *         unmodifiable set.
1202         */
1203        public static Set<String> getRegisteredParameterNames() {
1204
1205                return REGISTERED_PARAMETER_NAMES;
1206        }
1207
1208        
1209        /**
1210         * Returns the scope. Corresponds to the optional {@code scope}
1211         * parameter.
1212         *
1213         * @return The scope, {@code null} if not specified.
1214         */
1215        public Scope getScope() {
1216
1217                return scope;
1218        }
1219        
1220        
1221        /**
1222         * Returns the client notification token, required for the CIBA ping
1223         * and push token delivery modes. Corresponds to the
1224         * {@code client_notification_token} parameter.
1225         *
1226         * @return The client notification token, {@code null} if not
1227         *         specified.
1228         */
1229        public BearerAccessToken getClientNotificationToken() {
1230                
1231                return clientNotificationToken;
1232        }
1233        
1234        
1235        /**
1236         * Returns the requested Authentication Context Class Reference values.
1237         * Corresponds to the optional {@code acr_values} parameter.
1238         *
1239         * @return The requested ACR values, {@code null} if not specified.
1240         */
1241        public List<ACR> getACRValues() {
1242                
1243                return acrValues;
1244        }
1245        
1246        
1247        /**
1248         * Returns the hint type.
1249         *
1250         * @return The hint type.
1251         */
1252        public CIBAHintType getHintType() {
1253                
1254                if (getLoginHintTokenString() != null) {
1255                        return CIBAHintType.LOGIN_HINT_TOKEN;
1256                } else if (getIDTokenHint() != null) {
1257                        return CIBAHintType.ID_TOKEN_HINT;
1258                } else {
1259                        return CIBAHintType.LOGIN_HINT;
1260                }
1261        }
1262        
1263        
1264        /**
1265         * Returns the login hint token, containing information identifying the
1266         * end-user for whom authentication is being requested. Corresponds to
1267         * the {@code login_hint_token} parameter.
1268         *
1269         * @return The login hint token, {@code null} if not specified.
1270         */
1271        public LoginHintToken getLoginHintToken() {
1272                
1273                return loginHintToken;
1274        }
1275
1276
1277        /**
1278         * Returns the login hint token string, containing information
1279         * identifying the end-user for whom authentication is being requested.
1280         * Corresponds to the {@code login_hint_token} parameter.
1281         *
1282         * @return The login hint token string, {@code null} if not
1283         *         specified.
1284         */
1285        @Deprecated
1286        public String getLoginHintTokenString() {
1287
1288                return loginHintToken != null ? loginHintToken.getValue() : null;
1289        }
1290        
1291        
1292        /**
1293         * Returns the ID Token hint, passed as a hint to identify the end-user
1294         * for whom authentication is being requested. Corresponds to the
1295         * {@code id_token_hint} parameter.
1296         *
1297         * @return The ID Token hint, {@code null} if not specified.
1298         */
1299        public JWT getIDTokenHint() {
1300                
1301                return idTokenHint;
1302        }
1303        
1304        
1305        /**
1306         * Returns the login hint (email address, phone number, etc), about the
1307         * end-user for whom authentication is being requested. Corresponds to
1308         * the {@code login_hint} parameter.
1309         *
1310         * @return The login hint, {@code null} if not specified.
1311         */
1312        public String getLoginHint() {
1313                
1314                return loginHint;
1315        }
1316        
1317        
1318        /**
1319         * Returns the human-readable binding message for the display at the
1320         * consumption and authentication devices. Corresponds to the
1321         * {@code binding_message} parameter.
1322         *
1323         * @return The binding message, {@code null} if not specified.
1324         */
1325        public String getBindingMessage() {
1326                
1327                return bindingMessage;
1328        }
1329        
1330        
1331        /**
1332         * Returns the user secret code (password, PIN, etc.) to authorise the
1333         * CIBA request with the authentication device. Corresponds to the
1334         * {@code user_code} parameter.
1335         *
1336         * @return The user code, {@code null} if not specified.
1337         */
1338        public Secret getUserCode() {
1339                
1340                return userCode;
1341        }
1342        
1343        
1344        /**
1345         * Returns the requested expiration for the {@code auth_req_id}.
1346         * Corresponds to the {@code requested_expiry} parameter.
1347         *
1348         * @return The required expiry (as positive integer), {@code null} if
1349         *         not specified.
1350         */
1351        public Integer getRequestedExpiry() {
1352                
1353                return requestedExpiry;
1354        }
1355        
1356        
1357        /**
1358         * Returns the individual claims to be returned. Corresponds to the
1359         * optional {@code claims} parameter.
1360         *
1361         * @return The individual claims to be returned, {@code null} if not
1362         *         specified.
1363         */
1364        public OIDCClaimsRequest getOIDCClaims() {
1365                
1366                return claims;
1367        }
1368        
1369        
1370        /**
1371         * Returns the end-user's preferred languages and scripts for the
1372         * claims being returned, ordered by preference. Corresponds to the
1373         * optional {@code claims_locales} parameter.
1374         *
1375         * @return The preferred claims locales, {@code null} if not specified.
1376         */
1377        public List<LangTag> getClaimsLocales() {
1378                
1379                return claimsLocales;
1380        }
1381        
1382        
1383        /**
1384         * Returns the transaction specific purpose. Corresponds to the
1385         * optional {@code purpose} parameter.
1386         *
1387         * @return The purpose, {@code null} if not specified.
1388         */
1389        public String getPurpose() {
1390                
1391                return purpose;
1392        }
1393
1394
1395        /**
1396         * Returns the Rich Authorisation Request (RAR) details.
1397         *
1398         * @return The authorisation details, {@code null} if not specified.
1399         */
1400        public List<AuthorizationDetail> getAuthorizationDetails() {
1401
1402                return authorizationDetails;
1403        }
1404        
1405        
1406        /**
1407         * Returns the resource server URI.
1408         *
1409         * @return The resource URI(s), {@code null} if not specified.
1410         */
1411        public List<URI> getResources() {
1412                
1413                return resources;
1414        }
1415
1416
1417        /**
1418         * Returns the request context.
1419         *
1420         * @return The request context, {@code null} if not specified.
1421         */
1422        public JSONObject getContext() {
1423
1424                return requestContext;
1425        }
1426
1427        /**
1428         * Returns the additional custom parameters.
1429         *
1430         * @return The additional custom parameters as an unmodifiable map,
1431         *         empty map if none.
1432         */
1433        public Map<String, List<String>> getCustomParameters() {
1434                
1435                return customParams;
1436        }
1437        
1438        
1439        /**
1440         * Returns the specified custom parameter.
1441         *
1442         * @param name The parameter name. Must not be {@code null}.
1443         *
1444         * @return The parameter value(s), {@code null} if not specified.
1445         */
1446        public List<String> getCustomParameter(final String name) {
1447                
1448                return customParams.get(name);
1449        }
1450        
1451        
1452        /**
1453         * Returns {@code true} if this request is signed.
1454         *
1455         * @return {@code true} for a signed request, {@code false} for a plain
1456         *         request.
1457         */
1458        public boolean isSigned() {
1459                
1460                return signedRequest != null;
1461        }
1462        
1463        
1464        /**
1465         * Returns the JWT for a signed request.
1466         *
1467         * @return The request JWT.
1468         */
1469        public SignedJWT getRequestJWT() {
1470                
1471                return signedRequest;
1472        }
1473        
1474        
1475        /**
1476         * Returns the for parameters for this CIBA request. Parameters which
1477         * are part of the client authentication are not included.
1478         *
1479         * @return The parameters.
1480         */
1481        public Map<String, List<String>> toParameters() {
1482                
1483                // Put custom params first, so they may be overwritten by std params
1484                Map<String, List<String>> params = new LinkedHashMap<>(getCustomParameters());
1485                
1486                if (isSigned()) {
1487                        params.put("request", Collections.singletonList(signedRequest.serialize()));
1488                        return params;
1489                }
1490
1491                if (CollectionUtils.isNotEmpty(getScope())) {
1492                        params.put("scope", Collections.singletonList(getScope().toString()));
1493                }
1494                
1495                if (getClientNotificationToken() != null) {
1496                        params.put("client_notification_token", Collections.singletonList(getClientNotificationToken().getValue()));
1497                }
1498                if (getACRValues() != null) {
1499                        params.put("acr_values", Identifier.toStringList(getACRValues()));
1500                }
1501                if (getLoginHintToken() != null) {
1502                        params.put("login_hint_token", Collections.singletonList(getLoginHintToken().getValue()));
1503                }
1504                if (getIDTokenHint() != null) {
1505                        params.put("id_token_hint", Collections.singletonList(getIDTokenHint().serialize()));
1506                }
1507                if (getLoginHint() != null) {
1508                        params.put("login_hint", Collections.singletonList(getLoginHint()));
1509                }
1510                if (getBindingMessage() != null) {
1511                        params.put("binding_message", Collections.singletonList(getBindingMessage()));
1512                }
1513                if (getUserCode() != null) {
1514                        params.put("user_code", Collections.singletonList(getUserCode().getValue()));
1515                }
1516                if (getRequestedExpiry() != null) {
1517                        params.put("requested_expiry", Collections.singletonList(getRequestedExpiry().toString()));
1518                }
1519                if (getOIDCClaims() != null) {
1520                        params.put("claims", Collections.singletonList(getOIDCClaims().toJSONString()));
1521                }
1522                if (CollectionUtils.isNotEmpty(getClaimsLocales())) {
1523                        params.put("claims_locales", Collections.singletonList(LangTagUtils.concat(getClaimsLocales())));
1524                }
1525                if (getPurpose() != null) {
1526                        params.put("purpose", Collections.singletonList(purpose));
1527                }
1528                if (getAuthorizationDetails() != null) {
1529                        params.put("authorization_details", Collections.singletonList(AuthorizationDetail.toJSONString(getAuthorizationDetails())));
1530                }
1531                if (getContext() != null) {
1532                        params.put("request_context", Collections.singletonList(getContext().toJSONString()));
1533                }
1534                if (CollectionUtils.isNotEmpty(getResources())) {
1535                        params.put("resource", URIUtils.toStringList(getResources(), true));
1536                }
1537                
1538                return params;
1539        }
1540        
1541        
1542        /**
1543         * Returns the parameters for this CIBA request as a JSON Web Token
1544         * (JWT) claims set. Intended for creating a signed CIBA request.
1545         *
1546         * @return The parameters as JWT claim set.
1547         */
1548        public JWTClaimsSet toJWTClaimsSet() {
1549                
1550                if (isSigned()) {
1551                        throw new IllegalStateException();
1552                }
1553                
1554                return JWTClaimsSetUtils.toJWTClaimsSet(toParameters());
1555        }
1556        
1557        
1558        /**
1559         * Returns the matching HTTP request.
1560         *
1561         * @return The HTTP request.
1562         */
1563        @Override
1564        public HTTPRequest toHTTPRequest() {
1565
1566                if (getEndpointURI() == null)
1567                        throw new SerializeException("The endpoint URI is not specified");
1568
1569                HTTPRequest httpRequest = new HTTPRequest(HTTPRequest.Method.POST, getEndpointURI());
1570                httpRequest.setEntityContentType(ContentType.APPLICATION_URLENCODED);
1571
1572                getClientAuthentication().applyTo(httpRequest);
1573
1574                Map<String, List<String>> params;
1575                try {
1576                        params = new LinkedHashMap<>(httpRequest.getBodyAsFormParameters());
1577                } catch (ParseException e) {
1578                        throw new SerializeException(e.getMessage(), e);
1579                }
1580                params.putAll(toParameters());
1581                httpRequest.setBody(URLUtils.serializeParameters(params));
1582                
1583                return httpRequest;
1584        }
1585
1586        
1587        /**
1588         * Parses a CIBA request from the specified HTTP request.
1589         *
1590         * @param httpRequest The HTTP request. Must not be {@code null}.
1591         *
1592         * @return The CIBA request.
1593         *
1594         * @throws ParseException If parsing failed.
1595         */
1596        public static CIBARequest parse(final HTTPRequest httpRequest) throws ParseException {
1597
1598                // Only HTTP POST accepted
1599                URI uri = httpRequest.getURI();
1600                httpRequest.ensureMethod(HTTPRequest.Method.POST);
1601                httpRequest.ensureEntityContentType(ContentType.APPLICATION_URLENCODED);
1602                
1603                ClientAuthentication clientAuth = ClientAuthentication.parse(httpRequest);
1604                
1605                if (clientAuth == null) {
1606                        throw new ParseException("Missing required client authentication");
1607                }
1608                
1609                Map<String, List<String>> params = httpRequest.getBodyAsFormParameters();
1610                
1611                String v;
1612                
1613                if (params.containsKey("request")) {
1614                        // Signed request
1615                        v = MultivaluedMapUtils.getFirstValue(params, "request");
1616                        
1617                        if (StringUtils.isBlank(v)) {
1618                                throw new ParseException("Empty request parameter");
1619                        }
1620                        
1621                        SignedJWT signedRequest;
1622                        try {
1623                                signedRequest = SignedJWT.parse(v);
1624                        } catch (java.text.ParseException e) {
1625                                throw new ParseException("Invalid request JWT: " + e.getMessage(), e);
1626                        }
1627                        
1628                        try {
1629                                return new CIBARequest(uri, clientAuth, signedRequest);
1630                        } catch (IllegalArgumentException e) {
1631                                throw new ParseException(e.getMessage(), e);
1632                        }
1633                }
1634                
1635                
1636                // Plain request
1637                
1638                // Parse required scope
1639                v = MultivaluedMapUtils.getFirstValue(params, "scope");
1640                Scope scope = Scope.parse(v);
1641
1642                v = MultivaluedMapUtils.getFirstValue(params, "client_notification_token");
1643                BearerAccessToken clientNotificationToken = null;
1644                if (StringUtils.isNotBlank(v)) {
1645                        clientNotificationToken = new BearerAccessToken(v);
1646                }
1647                
1648                v = MultivaluedMapUtils.getFirstValue(params, "acr_values");
1649                List<ACR> acrValues = null;
1650                if (StringUtils.isNotBlank(v)) {
1651                        acrValues = new LinkedList<>();
1652                        StringTokenizer st = new StringTokenizer(v, " ");
1653                        while (st.hasMoreTokens()) {
1654                                acrValues.add(new ACR(st.nextToken()));
1655                        }
1656                }
1657                
1658                v = MultivaluedMapUtils.getFirstValue(params, "login_hint_token");
1659                LoginHintToken loginHintToken = null;
1660                if (StringUtils.isNotBlank(v)) {
1661                        loginHintToken = new LoginHintToken(v);
1662                }
1663                
1664                v = MultivaluedMapUtils.getFirstValue(params, "id_token_hint");
1665                JWT idTokenHint = null;
1666                if (StringUtils.isNotBlank(v)) {
1667                        try {
1668                                idTokenHint = JWTParser.parse(v);
1669                        } catch (java.text.ParseException e) {
1670                                throw new ParseException("Invalid id_token_hint parameter: " + e.getMessage());
1671                        }
1672                }
1673                
1674                String loginHint = MultivaluedMapUtils.getFirstValue(params, "login_hint");
1675                
1676                v = MultivaluedMapUtils.getFirstValue(params, "user_code");
1677                
1678                Secret userCode = null;
1679                if (StringUtils.isNotBlank(v)) {
1680                        userCode = new Secret(v);
1681                }
1682                
1683                String bindingMessage = MultivaluedMapUtils.getFirstValue(params, "binding_message");
1684                
1685                v = MultivaluedMapUtils.getFirstValue(params, "requested_expiry");
1686                
1687                Integer requestedExpiry = null;
1688                if (StringUtils.isNotBlank(v)) {
1689                        try {
1690                                requestedExpiry = Integer.valueOf(v);
1691                        } catch (NumberFormatException e) {
1692                                throw new ParseException("The requested_expiry parameter must be an integer");
1693                        }
1694                }
1695                
1696                v = MultivaluedMapUtils.getFirstValue(params, "claims");
1697                OIDCClaimsRequest claims = null;
1698                if (StringUtils.isNotBlank(v)) {
1699                        try {
1700                                claims = OIDCClaimsRequest.parse(v);
1701                        } catch (ParseException e) {
1702                                throw new ParseException("Invalid claims parameter: " + e.getMessage(), e);
1703                        }
1704                }
1705                
1706                
1707                List<LangTag> claimsLocales;
1708                try {
1709                        claimsLocales = LangTagUtils.parseLangTagList(MultivaluedMapUtils.getFirstValue(params, "claims_locales"));
1710                } catch (LangTagException e) {
1711                        throw new ParseException("Invalid claims_locales parameter: " + e.getMessage(), e);
1712                }
1713                
1714                String purpose = MultivaluedMapUtils.getFirstValue(params, "purpose");
1715
1716                List<AuthorizationDetail> authorizationDetails = null;
1717                v = MultivaluedMapUtils.getFirstValue(params, "authorization_details");
1718                if (StringUtils.isNotBlank(v)) {
1719                        authorizationDetails = AuthorizationDetail.parseList(v);
1720                }
1721                
1722                List<URI> resources = ResourceUtils.parseResourceURIs(params.get("resource"));
1723
1724                JSONObject requestContext = null;
1725                v = MultivaluedMapUtils.getFirstValue(params, "request_context");
1726                if (StringUtils.isNotBlank(v)) {
1727                        try {
1728                                requestContext = JSONObjectUtils.parse(v);
1729                        } catch (ParseException e) {
1730                                throw new ParseException("Invalid request_context parameter", e);
1731                        }
1732                }
1733                
1734                // Parse additional custom parameters
1735                Map<String,List<String>> customParams = null;
1736                
1737                for (Map.Entry<String,List<String>> p: params.entrySet()) {
1738                        
1739                        if (! REGISTERED_PARAMETER_NAMES.contains(p.getKey()) && ! clientAuth.getFormParameterNames().contains(p.getKey())) {
1740                                // We have a custom parameter
1741                                if (customParams == null) {
1742                                        customParams = new HashMap<>();
1743                                }
1744                                customParams.put(p.getKey(), p.getValue());
1745                        }
1746                }
1747
1748                try {
1749                        return new CIBARequest(
1750                                uri, clientAuth,
1751                                scope, clientNotificationToken, acrValues,
1752                                loginHintToken, idTokenHint, loginHint,
1753                                bindingMessage, userCode, requestedExpiry,
1754                                claims, claimsLocales, purpose, authorizationDetails,
1755                                resources, requestContext,
1756                                customParams);
1757                } catch (IllegalArgumentException e) {
1758                        throw new ParseException(e.getMessage());
1759                }
1760        }
1761}