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