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