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