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