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