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