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