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.openid.connect.sdk.rp;
019
020
021import java.net.URI;
022import java.net.URISyntaxException;
023import java.util.*;
024
025import com.nimbusds.oauth2.sdk.ErrorObject;
026import net.minidev.json.JSONArray;
027import net.minidev.json.JSONObject;
028
029import com.nimbusds.jose.EncryptionMethod;
030import com.nimbusds.jose.JWEAlgorithm;
031import com.nimbusds.jose.JWSAlgorithm;
032import com.nimbusds.oauth2.sdk.ParseException;
033import com.nimbusds.oauth2.sdk.ciba.BackChannelTokenDeliveryMode;
034import com.nimbusds.oauth2.sdk.client.ClientMetadata;
035import com.nimbusds.oauth2.sdk.client.RegistrationError;
036import com.nimbusds.oauth2.sdk.util.CollectionUtils;
037import com.nimbusds.oauth2.sdk.util.JSONObjectUtils;
038import com.nimbusds.oauth2.sdk.util.URIUtils;
039import com.nimbusds.openid.connect.sdk.SubjectType;
040import com.nimbusds.openid.connect.sdk.assurance.evidences.attachment.HashAlgorithm;
041import com.nimbusds.openid.connect.sdk.claims.ACR;
042import com.nimbusds.openid.connect.sdk.id.SectorID;
043
044
045/**
046 * OpenID Connect client metadata.
047 *
048 * <p>Related specifications:
049 *
050 * <ul>
051 *     <li>OpenID Connect Dynamic Client Registration 1.0, section 2.
052 *     <li>OpenID Connect Session Management 1.0, section 5.1.1 (draft 28).
053 *     <li>OpenID Connect Front-Channel Logout 1.0, section 2 (draft 02).
054 *     <li>OpenID Connect Back-Channel Logout 1.0, section 2.2 (draft 07).
055 *     <li>OpenID Connect for Identity Assurance 1.0 (draft 12).
056 *     <li>OpenID Connect Federation 1.0 (draft 14).
057 *     <li>OAuth 2.0 Dynamic Client Registration Protocol (RFC 7591), section
058 *         2.
059 *     <li>OAuth 2.0 Mutual TLS Client Authentication and Certificate Bound
060 *         Access Tokens (RFC 8705), sections 2.1.2 and 3.4.
061 *     <li>Financial-grade API: JWT Secured Authorization Response Mode for
062 *         OAuth 2.0 (JARM)
063 *     <li>OAuth 2.0 Pushed Authorization Requests (RFC 9126)
064 *     <li>OAuth 2.0 Rich Authorization Requests (RFC 9396), section 10.
065 * </ul>
066 */
067public class OIDCClientMetadata extends ClientMetadata {
068
069
070        /**
071         * The registered parameter names.
072         */
073        private static final Set<String> REGISTERED_PARAMETER_NAMES;
074
075
076        static {
077                // Start with the base OAuth 2.0 client params
078                Set<String> p = new HashSet<>(ClientMetadata.getRegisteredParameterNames());
079
080                // OIDC params
081                p.add("application_type");
082                p.add("subject_type");
083                p.add("sector_identifier_uri");
084                p.add("id_token_signed_response_alg");
085                p.add("id_token_encrypted_response_alg");
086                p.add("id_token_encrypted_response_enc");
087                p.add("userinfo_signed_response_alg");
088                p.add("userinfo_encrypted_response_alg");
089                p.add("userinfo_encrypted_response_enc");
090                p.add("default_max_age");
091                p.add("require_auth_time");
092                p.add("default_acr_values");
093                p.add("initiate_login_uri");
094
095                // OIDC session
096                p.add("post_logout_redirect_uris");
097                
098                // OIDC logout
099                p.add("frontchannel_logout_uri");
100                p.add("frontchannel_logout_session_required");
101                p.add("backchannel_logout_uri");
102                p.add("backchannel_logout_session_required");
103                
104                // OIDC identity assurance
105                p.add("digest_algorithm");
106
107                REGISTERED_PARAMETER_NAMES = Collections.unmodifiableSet(p);
108        }
109
110
111        /**
112         * The client application type.
113         */
114        private ApplicationType applicationType;
115
116
117        /**
118         * The subject identifier type for responses to this client.
119         */
120        private SubjectType subjectType;
121
122
123        /**
124         * Sector identifier URI.
125         */
126        private URI sectorIDURI;
127
128
129        /**
130         * The JSON Web Signature (JWS) algorithm required for the ID Tokens
131         * issued to this client.
132         */
133        private JWSAlgorithm idTokenJWSAlg;
134
135
136        /**
137         * The JSON Web Encryption (JWE) algorithm required for the ID Tokens
138         * issued to this client.
139         */
140        private JWEAlgorithm idTokenJWEAlg;
141
142
143        /**
144         * The JSON Web Encryption (JWE) method required for the ID Tokens
145         * issued to this client.
146         */
147        private EncryptionMethod idTokenJWEEnc;
148
149
150        /**
151         * The JSON Web Signature (JWS) algorithm required for the UserInfo
152         * responses to this client.
153         */
154        private JWSAlgorithm userInfoJWSAlg;
155
156
157        /**
158         * The JSON Web Encryption (JWE) algorithm required for the UserInfo
159         * responses to this client.
160         */
161        private JWEAlgorithm userInfoJWEAlg;
162
163
164        /**
165         * The JSON Web Encryption (JWE) method required for the UserInfo
166         * responses to this client.
167         */
168        private EncryptionMethod userInfoJWEEnc;
169
170
171        /**
172         * The default max authentication age, in seconds. If not specified 0.
173         */
174        private int defaultMaxAge = -1;
175
176
177        /**
178         * If {@code true} the {@code auth_time} claim in the ID Token is
179         * required by default.
180         */
181        private boolean requiresAuthTime;
182
183
184        /**
185         * The default Authentication Context Class Reference (ACR) values, by
186         * order of preference.
187         */
188        private List<ACR> defaultACRs;
189
190
191        /**
192         * Authorisation server initiated login HTTPS URI.
193         */
194        private URI initiateLoginURI;
195
196
197        /**
198         * Logout redirection URIs.
199         */
200        private Set<URI> postLogoutRedirectURIs;
201        
202        
203        /**
204         * Front-channel logout URI.
205         */
206        private URI frontChannelLogoutURI;
207        
208        
209        /**
210         * Indicates requirement for a session identifier on front-channel
211         * logout.
212         */
213        private boolean frontChannelLogoutSessionRequired = false;
214        
215        
216        /**
217         * Back-channel logout URI.
218         */
219        private URI backChannelLogoutURI;
220        
221        
222        /**
223         * Indicates requirement for a session identifier on back-channel
224         * logout.
225         */
226        private boolean backChannelLogoutSessionRequired = false;
227        
228        
229        /**
230         * The digest algorithms for external attachments in OpenID Connect
231         * for Identity Assurance 1.0.
232         */
233        private HashAlgorithm attachmentDigestAlg;
234
235
236        /** 
237         * Creates a new OpenID Connect client metadata instance.
238         */
239        public OIDCClientMetadata() {
240
241                super();
242        }
243        
244        
245        /**
246         * Creates a new OpenID Connect client metadata instance from the
247         * specified base OAuth 2.0 client metadata.
248         * 
249         * @param metadata The base OAuth 2.0 client metadata. Must not be
250         *                 {@code null}.
251         */
252        public OIDCClientMetadata(final ClientMetadata metadata) {
253                
254                super(metadata);
255        }
256        
257        
258        /**
259         * Creates a shallow copy of the specified OpenID Connect client
260         * metadata instance.
261         *
262         * @param metadata The client metadata to copy. Must not be
263         *                 {@code null}.
264         */
265        public OIDCClientMetadata(final OIDCClientMetadata metadata) {
266                
267                super(metadata);
268                applicationType = metadata.getApplicationType();
269                subjectType = metadata.getSubjectType();
270                sectorIDURI = metadata.getSectorIDURI();
271                idTokenJWSAlg = metadata.getIDTokenJWSAlg();
272                idTokenJWEAlg = metadata.getIDTokenJWEAlg();
273                idTokenJWEEnc = metadata.getIDTokenJWEEnc();
274                userInfoJWSAlg = metadata.getUserInfoJWSAlg();
275                userInfoJWEAlg = metadata.getUserInfoJWEAlg();
276                userInfoJWEEnc = metadata.getUserInfoJWEEnc();
277                defaultMaxAge = metadata.getDefaultMaxAge();
278                requiresAuthTime = metadata.requiresAuthTime();
279                defaultACRs = metadata.getDefaultACRs();
280                initiateLoginURI = metadata.getInitiateLoginURI();
281                postLogoutRedirectURIs = metadata.getPostLogoutRedirectionURIs();
282                frontChannelLogoutURI = metadata.getFrontChannelLogoutURI();
283                frontChannelLogoutSessionRequired = metadata.requiresFrontChannelLogoutSession();
284                backChannelLogoutURI = metadata.getBackChannelLogoutURI();
285                backChannelLogoutSessionRequired = metadata.requiresBackChannelLogoutSession();
286                attachmentDigestAlg = metadata.getAttachmentDigestAlg();
287        }
288
289
290        /**
291         * Gets the registered (standard) OpenID Connect client metadata
292         * parameter names.
293         *
294         * @return The registered OpenID Connect parameter names, as an
295         *         unmodifiable set.
296         */
297        public static Set<String> getRegisteredParameterNames() {
298
299                return REGISTERED_PARAMETER_NAMES;
300        }
301
302
303        /**
304         * Gets the client application type. Corresponds to the
305         * {@code application_type} client metadata field.
306         *
307         * @return The client application type, {@code null} if not specified.
308         */
309        public ApplicationType getApplicationType() {
310
311                return applicationType;
312        }
313
314
315        /**
316         * Sets the client application type. Corresponds to the
317         * {@code application_type} client metadata field.
318         *
319         * @param applicationType The client application type, {@code null} if
320         *                        not specified.
321         */
322        public void setApplicationType(final ApplicationType applicationType) {
323
324                this.applicationType = applicationType;
325        }
326
327
328        /**
329         * Gets the subject identifier type for responses to this client. 
330         * Corresponds to the {@code subject_type} client metadata field.
331         *
332         * @return The subject identifier type, {@code null} if not specified.
333         */
334        public SubjectType getSubjectType() {
335
336                return subjectType;
337        }
338
339
340        /**
341         * Sets the subject identifier type for responses to this client. 
342         * Corresponds to the {@code subject_type} client metadata field.
343         *
344         * @param subjectType The subject identifier type, {@code null} if not 
345         *                    specified.
346         */
347        public void setSubjectType(final SubjectType subjectType) {
348
349                this.subjectType = subjectType;
350        }
351
352
353        /**
354         * Gets the sector identifier URI. Corresponds to the 
355         * {@code sector_identifier_uri} client metadata field.
356         *
357         * @return The sector identifier URI, {@code null} if not specified.
358         */
359        public URI getSectorIDURI() {
360
361                return sectorIDURI;
362        }
363
364
365        /**
366         * Sets the sector identifier URI. Corresponds to the 
367         * {@code sector_identifier_uri} client metadata field. If set the URI
368         * will be checked for having an {@code https} scheme and a host
369         * component unless the URI is an URN.
370         *
371         * @param sectorIDURI The sector identifier URI, {@code null} if not 
372         *                    specified.
373         *
374         * @throws IllegalArgumentException If the URI was found to be illegal.
375         */
376        public void setSectorIDURI(final URI sectorIDURI) {
377
378                if (sectorIDURI != null && ! "urn".equalsIgnoreCase(sectorIDURI.getScheme())) {
379                        SectorID.ensureHTTPScheme(sectorIDURI);
380                        SectorID.ensureHostComponent(sectorIDURI);
381                }
382
383                this.sectorIDURI = sectorIDURI;
384        }
385
386
387        /**
388         * Resolves the sector identifier from the client metadata.
389         *
390         * @return The sector identifier, {@code null} if the subject type is
391         *         set to public.
392         *
393         * @throws IllegalStateException If resolution failed due to incomplete
394         *                               or inconsistent metadata.
395         */
396        public SectorID resolveSectorID() {
397
398                if (! SubjectType.PAIRWISE.equals(getSubjectType())) {
399                        // subject type is not pairwise or null
400                        return null;
401                }
402
403                // The sector identifier URI has priority
404                if (getSectorIDURI() != null) {
405                        return new SectorID(getSectorIDURI());
406                }
407
408                if (CollectionUtils.isNotEmpty(getRedirectionURIs()) && getBackChannelTokenDeliveryMode() != null) {
409                        throw new IllegalStateException(
410                                "Couldn't resolve sector ID: " +
411                                "A sector_identifier_uri is required when both redirect_uris and CIBA backchannel_token_delivery_mode are present"
412                        );
413                }
414                
415                // Code and/or implicit OAuth 2.0 grant
416                if (CollectionUtils.isNotEmpty(getRedirectionURIs())) {
417                        if (getRedirectionURIs().size() > 1) {
418                                throw new IllegalStateException(
419                                        "Couldn't resolve sector ID: " +
420                                        "More than one URI in redirect_uris, sector_identifier_uri not specified"
421                                );
422                        }
423                        return new SectorID(getRedirectionURIs().iterator().next());
424                }
425                
426                // CIBA OAuth 2.0 grant
427                if (BackChannelTokenDeliveryMode.POLL.equals(getBackChannelTokenDeliveryMode()) ||
428                    BackChannelTokenDeliveryMode.PING.equals(getBackChannelTokenDeliveryMode())) {
429                        
430                        if (getJWKSetURI() == null) {
431                                throw new IllegalStateException(
432                                        "Couldn't resolve sector ID: " +
433                                        "A jwks_uri is required for CIBA poll or ping backchannel_token_delivery_mode"
434                                );
435                        }
436                        return new SectorID(getJWKSetURI());
437                }
438                if (BackChannelTokenDeliveryMode.PUSH.equals(getBackChannelTokenDeliveryMode())) {
439                        
440                        if (getBackChannelClientNotificationEndpoint() == null) {
441                                throw new IllegalStateException(
442                                        "Couldn't resolve sector ID: " +
443                                        "A backchannel_client_notification_endpoint is required for CIBA push backchannel_token_delivery_mode"
444                                );
445                        }
446                        return new SectorID(getBackChannelClientNotificationEndpoint());
447                }
448                
449                throw new IllegalStateException("Couldn't resolve sector ID");
450        }
451
452
453        /**
454         * Gets the JSON Web Signature (JWS) algorithm required for the ID 
455         * Tokens issued to this client. Corresponds to the 
456         * {@code id_token_signed_response_alg} client metadata field.
457         *
458         * @return The JWS algorithm, {@code null} if not specified.
459         */
460        public JWSAlgorithm getIDTokenJWSAlg() {
461
462                return idTokenJWSAlg;
463        }
464
465
466        /**
467         * Sets the JSON Web Signature (JWS) algorithm required for the ID 
468         * Tokens issued to this client. Corresponds to the 
469         * {@code id_token_signed_response_alg} client metadata field.
470         *
471         * @param idTokenJWSAlg The JWS algorithm, {@code null} if not 
472         *                      specified.
473         */
474        public void setIDTokenJWSAlg(final JWSAlgorithm idTokenJWSAlg) {
475
476                this.idTokenJWSAlg = idTokenJWSAlg;
477        }
478
479
480        /**
481         * Gets the JSON Web Encryption (JWE) algorithm required for the ID 
482         * Tokens issued to this client. Corresponds to the 
483         * {@code id_token_encrypted_response_alg} client metadata field.
484         *
485         * @return The JWE algorithm, {@code null} if not specified.
486         */
487        public JWEAlgorithm getIDTokenJWEAlg() {
488
489                return idTokenJWEAlg;
490        }
491
492
493        /**
494         * Sets the JSON Web Encryption (JWE) algorithm required for the ID 
495         * Tokens issued to this client. Corresponds to the 
496         * {@code id_token_encrypted_response_alg} client metadata field.
497         *
498         * @param idTokenJWEAlg The JWE algorithm, {@code null} if not 
499         *                      specified.
500         */
501        public void setIDTokenJWEAlg(final JWEAlgorithm idTokenJWEAlg) {
502
503                this.idTokenJWEAlg = idTokenJWEAlg;
504        }
505
506
507        /**
508         * Gets the JSON Web Encryption (JWE) method required for the ID Tokens
509         * issued to this client. Corresponds to the 
510         * {@code id_token_encrypted_response_enc} client metadata field.
511         *
512         * @return The JWE method, {@code null} if not specified.
513         */
514        public EncryptionMethod getIDTokenJWEEnc() {
515
516                return idTokenJWEEnc;
517        }
518
519
520        /**
521         * Sets the JSON Web Encryption (JWE) method required for the ID Tokens
522         * issued to this client. Corresponds to the 
523         * {@code id_token_encrypted_response_enc} client metadata field.
524         *
525         * @param idTokenJWEEnc The JWE method, {@code null} if not specified.
526         */
527        public void setIDTokenJWEEnc(final EncryptionMethod idTokenJWEEnc) {
528
529                this.idTokenJWEEnc = idTokenJWEEnc;
530        }
531
532
533        /**
534         * Gets the JSON Web Signature (JWS) algorithm required for the 
535         * UserInfo responses to this client. Corresponds to the 
536         * {@code userinfo_signed_response_alg} client metadata field.
537         *
538         * @return The JWS algorithm, {@code null} if not specified.
539         */
540        public JWSAlgorithm getUserInfoJWSAlg() {
541
542                return userInfoJWSAlg;
543        }
544
545
546        /**
547         * Sets the JSON Web Signature (JWS) algorithm required for the 
548         * UserInfo responses to this client. Corresponds to the
549         * {@code userinfo_signed_response_alg} client metadata field.
550         *
551         * @param userInfoJWSAlg The JWS algorithm, {@code null} if not 
552         *                       specified.
553         */
554        public void setUserInfoJWSAlg(final JWSAlgorithm userInfoJWSAlg) {
555
556                this.userInfoJWSAlg = userInfoJWSAlg;
557        }
558
559
560        /**
561         * Gets the JSON Web Encryption (JWE) algorithm required for the 
562         * UserInfo responses to this client. Corresponds to the 
563         * {@code userinfo_encrypted_response_alg} client metadata field.
564         *
565         * @return The JWE algorithm, {@code null} if not specified.
566         */
567        public JWEAlgorithm getUserInfoJWEAlg() {
568
569                return userInfoJWEAlg;
570        }
571
572
573        /**
574         * Sets the JSON Web Encryption (JWE) algorithm required for the 
575         * UserInfo responses to this client. Corresponds to the 
576         * {@code userinfo_encrypted_response_alg} client metadata field.
577         *
578         * @param userInfoJWEAlg The JWE algorithm, {@code null} if not
579         *                       specified.
580         */
581        public void setUserInfoJWEAlg(final JWEAlgorithm userInfoJWEAlg) {
582
583                this.userInfoJWEAlg = userInfoJWEAlg;
584        }
585
586
587        /**
588         * Gets the JSON Web Encryption (JWE) method required for the UserInfo
589         * responses to this client. Corresponds to the 
590         * {@code userinfo_encrypted_response_enc} client metadata field.
591         *
592         * @return The JWE method, {@code null} if not specified.
593         */
594        public EncryptionMethod getUserInfoJWEEnc() {
595
596                return userInfoJWEEnc;
597        }
598
599
600        /**
601         * Sets the JSON Web Encryption (JWE) method required for the UserInfo
602         * responses to this client. Corresponds to the 
603         * {@code userinfo_encrypted_response_enc} client metadata field.
604         *
605         * @param userInfoJWEEnc The JWE method, {@code null} if not specified.
606         */
607        public void setUserInfoJWEEnc(final EncryptionMethod userInfoJWEEnc) {
608
609                this.userInfoJWEEnc = userInfoJWEEnc;
610        }
611
612
613        /**
614         * Gets the default maximum authentication age. Corresponds to the 
615         * {@code default_max_age} client metadata field.
616         *
617         * @return The default max authentication age, in seconds. If not
618         *         specified -1.
619         */
620        public int getDefaultMaxAge() {
621
622                return defaultMaxAge;
623        }
624
625
626        /**
627         * Sets the default maximum authentication age. Corresponds to the 
628         * {@code default_max_age} client metadata field.
629         *
630         * @param defaultMaxAge The default max authentication age, in seconds.
631         *                      If not specified -1.
632         */
633        public void setDefaultMaxAge(final int defaultMaxAge) {
634
635                this.defaultMaxAge = defaultMaxAge;
636        }
637
638
639        /**
640         * Gets the default requirement for the {@code auth_time} claim in the
641         * ID Token. Corresponds to the {@code require_auth_time} client 
642         * metadata field.
643         *
644         * @return If {@code true} the {@code auth_Time} claim in the ID Token 
645         *         is required by default.
646         */
647        public boolean requiresAuthTime() {
648
649                return requiresAuthTime;
650        }
651
652
653        /**
654         * Sets the default requirement for the {@code auth_time} claim in the
655         * ID Token. Corresponds to the {@code require_auth_time} client 
656         * metadata field.
657         *
658         * @param requiresAuthTime If {@code true} the {@code auth_Time} claim 
659         *                         in the ID Token is required by default.
660         */
661        public void requiresAuthTime(final boolean requiresAuthTime) {
662
663                this.requiresAuthTime = requiresAuthTime;
664        }
665
666
667        /**
668         * Gets the default Authentication Context Class Reference (ACR) 
669         * values. Corresponds to the {@code default_acr_values} client 
670         * metadata field.
671         *
672         * @return The default ACR values, by order of preference, 
673         *         {@code null} if not specified.
674         */
675        public List<ACR> getDefaultACRs() {
676
677                return defaultACRs;
678        }
679
680
681        /**
682         * Sets the default Authentication Context Class Reference (ACR)
683         * values. Corresponds to the {@code default_acr_values} client 
684         * metadata field.
685         *
686         * @param defaultACRs The default ACRs, by order of preference, 
687         *                    {@code null} if not specified.
688         */
689        public void setDefaultACRs(final List<ACR> defaultACRs) {
690
691                this.defaultACRs = defaultACRs;
692        }
693
694
695        /**
696         * Gets the HTTPS URI that the authorisation server can call to
697         * initiate a login at the client. Corresponds to the 
698         * {@code initiate_login_uri} client metadata field.
699         *
700         * @return The login URI, {@code null} if not specified.
701         */
702        public URI getInitiateLoginURI() {
703
704                return initiateLoginURI;
705        }
706
707
708        /**
709         * Sets the HTTPS URI that the authorisation server can call to
710         * initiate a login at the client. Corresponds to the 
711         * {@code initiate_login_uri} client metadata field.
712         *
713         * @param loginURI The login URI, {@code null} if not specified. The
714         *                 URI scheme must be https.
715         */
716        public void setInitiateLoginURI(final URI loginURI) {
717                
718                URIUtils.ensureSchemeIsHTTPS(loginURI);
719                this.initiateLoginURI = loginURI;
720        }
721
722
723        /**
724         * Gets the post logout redirection URIs. Corresponds to the
725         * {@code post_logout_redirect_uris} client metadata field.
726         *
727         * @return The logout redirection URIs, {@code null} if not specified.
728         */
729        public Set<URI> getPostLogoutRedirectionURIs() {
730
731                return postLogoutRedirectURIs;
732        }
733
734
735        /**
736         * Sets the post logout redirection URIs. Corresponds to the
737         * {@code post_logout_redirect_uris} client metadata field.
738         *
739         * @param logoutURIs The post logout redirection URIs, {@code null} if
740         *                   not specified.
741         */
742        public void setPostLogoutRedirectionURIs(final Set<URI> logoutURIs) {
743
744                if (logoutURIs != null) {
745                        for (URI uri: logoutURIs) {
746                                URIUtils.ensureSchemeIsNotProhibited(uri, PROHIBITED_REDIRECT_URI_SCHEMES);
747                        }
748                }
749                postLogoutRedirectURIs = logoutURIs;
750        }
751        
752        
753        /**
754         * Gets the front-channel logout URI. Corresponds to the
755         * {@code frontchannel_logout_uri} client metadata field.
756         *
757         * @return The front-channel logout URI, {@code null} if not specified.
758         */
759        public URI getFrontChannelLogoutURI() {
760                
761                return frontChannelLogoutURI;
762        }
763        
764        
765        /**
766         * Sets the front-channel logout URI. Corresponds to the
767         * {@code frontchannel_logout_uri} client metadata field.
768         *
769         * @param frontChannelLogoutURI The front-channel logout URI,
770         *                              {@code null} if not specified.
771         */
772        public void setFrontChannelLogoutURI(final URI frontChannelLogoutURI) {
773                
774                if (frontChannelLogoutURI != null && frontChannelLogoutURI.getScheme() == null) {
775                        throw new IllegalArgumentException("Missing URI scheme");
776                }
777                
778                this.frontChannelLogoutURI = frontChannelLogoutURI;
779        }
780        
781        
782        /**
783         * Gets the requirement for a session identifier on front-channel
784         * logout. Corresponds to
785         * the {@code frontchannel_logout_session_required} client metadata
786         * field.
787         *
788         * @return {@code true} if a session identifier is required, else
789         *         {@code false}.
790         */
791        public boolean requiresFrontChannelLogoutSession() {
792                
793                return frontChannelLogoutSessionRequired;
794        }
795        
796        
797        /**
798         * Sets the requirement for a session identifier on front-channel
799         * logout. Corresponds to
800         * the {@code frontchannel_logout_session_required} client metadata
801         * field.
802         *
803         * @param requiresSession  {@code true} if a session identifier is
804         *                         required, else {@code false}.
805         */
806        public void requiresFrontChannelLogoutSession(boolean requiresSession) {
807                
808                frontChannelLogoutSessionRequired = requiresSession;
809        }
810        
811        
812        /**
813         * Gets the back-channel logout URI. Corresponds to the
814         * {@code backchannel_logout_uri} client metadata field.
815         *
816         * @return The back-channel logout URI, {@code null} if not specified.
817         */
818        public URI getBackChannelLogoutURI() {
819                
820                return backChannelLogoutURI;
821        }
822        
823        
824        /**
825         * Sets the back-channel logout URI. Corresponds to the
826         * {@code backchannel_logout_uri} client metadata field.
827         *
828         * @param backChannelLogoutURI The back-channel logout URI,
829         *                             {@code null} if not specified. The URI
830         *                             scheme must be https or http.
831         */
832        public void setBackChannelLogoutURI(final URI backChannelLogoutURI) {
833                
834                URIUtils.ensureSchemeIsHTTPSorHTTP(backChannelLogoutURI);
835                this.backChannelLogoutURI = backChannelLogoutURI;
836        }
837        
838        
839        /**
840         * Gets the requirement for a session identifier on back-channel
841         * logout. Corresponds to
842         * the {@code backchannel_logout_session_required} client metadata
843         * field.
844         *
845         * @return {@code true} if a session identifier is required, else
846         *         {@code false}.
847         */
848        public boolean requiresBackChannelLogoutSession() {
849                
850                return backChannelLogoutSessionRequired;
851        }
852        
853        
854        /**
855         * Sets the requirement for a session identifier on back-channel
856         * logout. Corresponds to
857         * the {@code backchannel_logout_session_required} client metadata
858         * field.
859         *
860         * @param requiresSession {@code true} if a session identifier is
861         *                        required, else {@code false}.
862         */
863        public void requiresBackChannelLogoutSession(final boolean requiresSession) {
864                
865                backChannelLogoutSessionRequired = requiresSession;
866        }
867        
868        
869        /**
870         * Gets the digest algorithm for the external evidence attachments in
871         * OpenID Connect for Identity Assurance 1.0. Corresponds to the
872         * {@code digest_algorithm} client metadata field.
873         *
874         * @return The digest algorithm, {@code null} if not specified.
875         */
876        public HashAlgorithm getAttachmentDigestAlg() {
877                
878                return attachmentDigestAlg;
879        }
880        
881        
882        /**
883         * Sets the digest algorithm for the external evidence attachments in
884         * OpenID Connect for Identity Assurance 1.0. Corresponds to the
885         * {@code digest_algorithm} client metadata field.
886         *
887         * @param hashAlg The digest algorithm, {@code null} if not specified.
888         */
889        public void setAttachmentDigestAlg(final HashAlgorithm hashAlg) {
890                
891                attachmentDigestAlg = hashAlg;
892        }
893        
894        
895        /**
896         * Applies the client metadata defaults where no values have been
897         * specified.
898         * 
899         * <ul>
900         *     <li>The response types default to {@code ["code"]}.
901         *     <li>The grant types default to {@code "authorization_code".}
902         *     <li>The client authentication method defaults to
903         *         "client_secret_basic".
904         *     <li>The application type defaults to
905         *         {@link ApplicationType#WEB}.
906         *     <li>The ID token JWS algorithm defaults to "RS256".
907         * </ul>
908         */
909        @Override
910        public void applyDefaults() {
911                
912                super.applyDefaults();
913
914                if (applicationType == null) {
915                        applicationType = ApplicationType.WEB;
916                }
917                
918                if (idTokenJWSAlg == null) {
919                        idTokenJWSAlg = JWSAlgorithm.RS256;
920                }
921        }
922
923
924        @Override
925        public JSONObject toJSONObject(boolean includeCustomFields) {
926
927                JSONObject o = super.toJSONObject(includeCustomFields);
928
929                o.putAll(getCustomFields());
930
931                if (applicationType != null)
932                        o.put("application_type", applicationType.toString());
933
934                if (subjectType != null)
935                        o.put("subject_type", subjectType.toString());
936
937
938                if (sectorIDURI != null)
939                        o.put("sector_identifier_uri", sectorIDURI.toString());
940
941
942                if (idTokenJWSAlg != null)
943                        o.put("id_token_signed_response_alg", idTokenJWSAlg.getName());
944
945
946                if (idTokenJWEAlg != null)
947                        o.put("id_token_encrypted_response_alg", idTokenJWEAlg.getName());
948
949
950                if (idTokenJWEEnc != null)
951                        o.put("id_token_encrypted_response_enc", idTokenJWEEnc.getName());
952
953
954                if (userInfoJWSAlg != null)
955                        o.put("userinfo_signed_response_alg", userInfoJWSAlg.getName());
956
957
958                if (userInfoJWEAlg != null)
959                        o.put("userinfo_encrypted_response_alg", userInfoJWEAlg.getName());
960
961
962                if (userInfoJWEEnc != null)
963                        o.put("userinfo_encrypted_response_enc", userInfoJWEEnc.getName());
964
965
966                if (defaultMaxAge > 0)
967                        o.put("default_max_age", defaultMaxAge);
968
969
970                if (requiresAuthTime())
971                        o.put("require_auth_time", requiresAuthTime);
972
973
974                if (defaultACRs != null) {
975
976                        JSONArray acrList = new JSONArray();
977                        
978                        for (ACR acr: defaultACRs) {
979                                acrList.add(acr.getValue());
980                        }
981                        o.put("default_acr_values", acrList);
982                }
983
984
985                if (initiateLoginURI != null)
986                        o.put("initiate_login_uri", initiateLoginURI.toString());
987
988
989                if (postLogoutRedirectURIs != null) {
990
991                        JSONArray uriList = new JSONArray();
992
993                        for (URI uri: postLogoutRedirectURIs)
994                                uriList.add(uri.toString());
995
996                        o.put("post_logout_redirect_uris", uriList);
997                }
998                
999                if (frontChannelLogoutURI != null) {
1000                        o.put("frontchannel_logout_uri", frontChannelLogoutURI.toString());
1001                        o.put("frontchannel_logout_session_required", frontChannelLogoutSessionRequired);
1002                }
1003                
1004                if (backChannelLogoutURI != null) {
1005                        o.put("backchannel_logout_uri", backChannelLogoutURI.toString());
1006                        o.put("backchannel_logout_session_required", backChannelLogoutSessionRequired);
1007                }
1008                
1009                if (attachmentDigestAlg != null) {
1010                        o.put("digest_algorithm", attachmentDigestAlg.getValue());
1011                }
1012
1013                return o;
1014        }
1015
1016
1017        /**
1018         * Parses an OpenID Connect client metadata instance from the specified
1019         * JSON object.
1020         *
1021         * @param jsonObject The JSON object to parse. Must not be 
1022         *                   {@code null}.
1023         *
1024         * @return The OpenID Connect client metadata.
1025         *
1026         * @throws ParseException If the JSON object couldn't be parsed to an
1027         *                        OpenID Connect client metadata instance.
1028         */
1029        public static OIDCClientMetadata parse(final JSONObject jsonObject)
1030                throws ParseException {
1031
1032                ClientMetadata baseMetadata = ClientMetadata.parse(jsonObject);
1033                
1034                OIDCClientMetadata metadata = new OIDCClientMetadata(baseMetadata);
1035
1036                // Parse the OIDC-specific fields from the custom OAuth 2.0 dyn
1037                // reg fields
1038
1039                JSONObject oidcFields = baseMetadata.getCustomFields();
1040
1041                try {
1042                        if (jsonObject.get("application_type") != null) {
1043                                metadata.setApplicationType(JSONObjectUtils.getEnum(jsonObject, "application_type", ApplicationType.class));
1044                                oidcFields.remove("application_type");
1045                        }
1046
1047                        if (jsonObject.get("subject_type") != null) {
1048                                metadata.setSubjectType(JSONObjectUtils.getEnum(jsonObject, "subject_type", SubjectType.class));
1049                                oidcFields.remove("subject_type");
1050                        }
1051
1052                        if (jsonObject.get("sector_identifier_uri") != null) {
1053                                try {
1054                                        metadata.setSectorIDURI(JSONObjectUtils.getURI(jsonObject, "sector_identifier_uri"));
1055                                } catch (IllegalArgumentException e) {
1056                                        throw new ParseException("Invalid sector_identifier_uri parameter: " + e.getMessage());
1057                                }
1058                                oidcFields.remove("sector_identifier_uri");
1059                        }
1060
1061                        if (jsonObject.get("id_token_signed_response_alg") != null) {
1062                                metadata.setIDTokenJWSAlg(JWSAlgorithm.parse(
1063                                        JSONObjectUtils.getString(jsonObject, "id_token_signed_response_alg")));
1064
1065                                oidcFields.remove("id_token_signed_response_alg");
1066                        }
1067
1068                        if (jsonObject.get("id_token_encrypted_response_alg") != null) {
1069                                metadata.setIDTokenJWEAlg(JWEAlgorithm.parse(
1070                                        JSONObjectUtils.getString(jsonObject, "id_token_encrypted_response_alg")));
1071
1072                                oidcFields.remove("id_token_encrypted_response_alg");
1073                        }
1074
1075                        if (jsonObject.get("id_token_encrypted_response_enc") != null) {
1076                                metadata.setIDTokenJWEEnc(EncryptionMethod.parse(
1077                                        JSONObjectUtils.getString(jsonObject, "id_token_encrypted_response_enc")));
1078
1079                                oidcFields.remove("id_token_encrypted_response_enc");
1080                        }
1081
1082                        if (jsonObject.get("userinfo_signed_response_alg") != null) {
1083                                metadata.setUserInfoJWSAlg(JWSAlgorithm.parse(
1084                                        JSONObjectUtils.getString(jsonObject, "userinfo_signed_response_alg")));
1085
1086                                oidcFields.remove("userinfo_signed_response_alg");
1087                        }
1088
1089                        if (jsonObject.get("userinfo_encrypted_response_alg") != null) {
1090                                metadata.setUserInfoJWEAlg(JWEAlgorithm.parse(
1091                                        JSONObjectUtils.getString(jsonObject, "userinfo_encrypted_response_alg")));
1092
1093                                oidcFields.remove("userinfo_encrypted_response_alg");
1094                        }
1095
1096                        if (jsonObject.get("userinfo_encrypted_response_enc") != null) {
1097                                metadata.setUserInfoJWEEnc(EncryptionMethod.parse(
1098                                        JSONObjectUtils.getString(jsonObject, "userinfo_encrypted_response_enc")));
1099
1100                                oidcFields.remove("userinfo_encrypted_response_enc");
1101                        }
1102
1103                        if (jsonObject.get("default_max_age") != null) {
1104                                metadata.setDefaultMaxAge(JSONObjectUtils.getInt(jsonObject, "default_max_age"));
1105                                oidcFields.remove("default_max_age");
1106                        }
1107
1108                        if (jsonObject.get("require_auth_time") != null) {
1109                                metadata.requiresAuthTime(JSONObjectUtils.getBoolean(jsonObject, "require_auth_time"));
1110                                oidcFields.remove("require_auth_time");
1111                        }
1112
1113                        if (jsonObject.get("default_acr_values") != null) {
1114
1115                                List<ACR> acrValues = new LinkedList<>();
1116
1117                                for (String acrString : JSONObjectUtils.getStringArray(jsonObject, "default_acr_values"))
1118                                        acrValues.add(new ACR(acrString));
1119
1120                                metadata.setDefaultACRs(acrValues);
1121
1122                                oidcFields.remove("default_acr_values");
1123                        }
1124
1125                        if (jsonObject.get("initiate_login_uri") != null) {
1126                                try {
1127                                        metadata.setInitiateLoginURI(JSONObjectUtils.getURI(jsonObject, "initiate_login_uri"));
1128                                } catch (IllegalArgumentException e) {
1129                                        throw new ParseException("Invalid initiate_login_uri parameter: " + e.getMessage());
1130                                }
1131                                oidcFields.remove("initiate_login_uri");
1132                        }
1133
1134                        if (jsonObject.get("post_logout_redirect_uris") != null) {
1135
1136                                Set<URI> logoutURIs = new LinkedHashSet<>();
1137
1138                                for (String uriString : JSONObjectUtils.getStringArray(jsonObject, "post_logout_redirect_uris")) {
1139
1140                                        try {
1141                                                logoutURIs.add(new URI(uriString));
1142                                        } catch (URISyntaxException e) {
1143                                                throw new ParseException("Invalid post_logout_redirect_uris parameter");
1144                                        }
1145                                }
1146
1147                                try {
1148                                        metadata.setPostLogoutRedirectionURIs(logoutURIs);
1149                                } catch (IllegalArgumentException e) {
1150                                        throw new ParseException("Invalid post_logout_redirect_uris parameter: " + e.getMessage());
1151                                }
1152                                oidcFields.remove("post_logout_redirect_uris");
1153                        }
1154                        
1155                        if (jsonObject.get("frontchannel_logout_uri") != null) {
1156                                
1157                                try {
1158                                        metadata.setFrontChannelLogoutURI(JSONObjectUtils.getURI(jsonObject, "frontchannel_logout_uri"));
1159                                } catch (IllegalArgumentException e) {
1160                                        throw new ParseException("Invalid frontchannel_logout_uri parameter: " + e.getMessage());
1161                                }
1162                                oidcFields.remove("frontchannel_logout_uri");
1163                        
1164                                if (jsonObject.get("frontchannel_logout_session_required") != null) {
1165                                        metadata.requiresFrontChannelLogoutSession(JSONObjectUtils.getBoolean(jsonObject, "frontchannel_logout_session_required"));
1166                                        oidcFields.remove("frontchannel_logout_session_required");
1167                                }
1168                        }
1169                        
1170                        
1171                        if (jsonObject.get("backchannel_logout_uri") != null) {
1172                                
1173                                try {
1174                                        metadata.setBackChannelLogoutURI(JSONObjectUtils.getURI(jsonObject, "backchannel_logout_uri"));
1175                                } catch (IllegalArgumentException e) {
1176                                        throw new ParseException("Invalid backchannel_logout_uri parameter: " + e.getMessage());
1177                                }
1178                                oidcFields.remove("backchannel_logout_uri");
1179                                
1180                                if (jsonObject.get("backchannel_logout_session_required") != null) {
1181                                        metadata.requiresBackChannelLogoutSession(JSONObjectUtils.getBoolean(jsonObject, "backchannel_logout_session_required"));
1182                                        oidcFields.remove("backchannel_logout_session_required");
1183                                }
1184                        }
1185                        
1186                        if (jsonObject.get("digest_algorithm") != null) {
1187                                metadata.setAttachmentDigestAlg(new HashAlgorithm(JSONObjectUtils.getString(jsonObject, "digest_algorithm")));
1188                                oidcFields.remove("digest_algorithm");
1189                        }
1190                        
1191                } catch (ParseException e) {
1192                        // Insert client_client_metadata error code so that it
1193                        // can be reported back to the client if we have a
1194                        // registration event
1195                        throw new ParseException(
1196                                e.getMessage(),
1197                                RegistrationError.INVALID_CLIENT_METADATA.appendDescription(ErrorObject.removeIllegalChars(": " + e.getMessage())),
1198                                e.getCause());
1199                }
1200
1201                // The remaining fields are custom
1202                metadata.setCustomFields(oidcFields);
1203
1204                return metadata;
1205        }
1206}