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.op;
019
020
021import java.io.IOException;
022import java.net.MalformedURLException;
023import java.net.URI;
024import java.net.URL;
025import java.util.*;
026
027import net.minidev.json.JSONObject;
028
029import com.nimbusds.jose.EncryptionMethod;
030import com.nimbusds.jose.JWEAlgorithm;
031import com.nimbusds.jose.JWSAlgorithm;
032import com.nimbusds.langtag.LangTag;
033import com.nimbusds.langtag.LangTagException;
034import com.nimbusds.oauth2.sdk.GeneralException;
035import com.nimbusds.oauth2.sdk.ParseException;
036import com.nimbusds.oauth2.sdk.as.AuthorizationServerEndpointMetadata;
037import com.nimbusds.oauth2.sdk.as.AuthorizationServerMetadata;
038import com.nimbusds.oauth2.sdk.http.HTTPRequest;
039import com.nimbusds.oauth2.sdk.http.HTTPResponse;
040import com.nimbusds.oauth2.sdk.id.Identifier;
041import com.nimbusds.oauth2.sdk.id.Issuer;
042import com.nimbusds.oauth2.sdk.util.JSONObjectUtils;
043import com.nimbusds.openid.connect.sdk.Display;
044import com.nimbusds.openid.connect.sdk.SubjectType;
045import com.nimbusds.openid.connect.sdk.assurance.evidences.IDDocumentType;
046import com.nimbusds.openid.connect.sdk.assurance.evidences.IdentityEvidenceType;
047import com.nimbusds.openid.connect.sdk.assurance.IdentityTrustFramework;
048import com.nimbusds.openid.connect.sdk.assurance.evidences.IdentityVerificationMethod;
049import com.nimbusds.openid.connect.sdk.claims.ACR;
050import com.nimbusds.openid.connect.sdk.claims.ClaimType;
051
052
053/**
054 * OpenID Provider (OP) metadata.
055 *
056 * <p>Related specifications:
057 *
058 * <ul>
059 *     <li>OpenID Connect Discovery 1.0, section 3.
060 *     <li>OpenID Connect Session Management 1.0, section 2.1 (draft 28).
061 *     <li>OpenID Connect Front-Channel Logout 1.0, section 3 (draft 02).
062 *     <li>OpenID Connect Back-Channel Logout 1.0, section 2.1 (draft 04).
063 *     <li>OpenID Connect for Identity Assurance 1.0 (draft 08).
064 *     <li>OAuth 2.0 Authorization Server Metadata (RFC 8414)
065 *     <li>OAuth 2.0 Mutual TLS Client Authentication and Certificate Bound
066 *         Access Tokens (RFC 8705)
067 *     <li>Financial-grade API: JWT Secured Authorization Response Mode for
068 *         OAuth 2.0 (JARM)
069 * </ul>
070 */
071public class OIDCProviderMetadata extends AuthorizationServerMetadata {
072
073
074        /**
075         * The registered parameter names.
076         */
077        private static final Set<String> REGISTERED_PARAMETER_NAMES;
078
079
080        static {
081                Set<String> p = new HashSet<>(AuthorizationServerMetadata.getRegisteredParameterNames());
082                p.addAll(OIDCProviderEndpointMetadata.getRegisteredParameterNames());
083                p.add("check_session_iframe");
084                p.add("end_session_endpoint");
085                p.add("acr_values_supported");
086                p.add("subject_types_supported");
087                p.add("id_token_signing_alg_values_supported");
088                p.add("id_token_encryption_alg_values_supported");
089                p.add("id_token_encryption_enc_values_supported");
090                p.add("userinfo_signing_alg_values_supported");
091                p.add("userinfo_encryption_alg_values_supported");
092                p.add("userinfo_encryption_enc_values_supported");
093                p.add("display_values_supported");
094                p.add("claim_types_supported");
095                p.add("claims_supported");
096                p.add("claims_locales_supported");
097                p.add("claims_parameter_supported");
098                p.add("backchannel_logout_supported");
099                p.add("backchannel_logout_session_supported");
100                p.add("frontchannel_logout_supported");
101                p.add("frontchannel_logout_session_supported");
102                p.add("verified_claims_supported");
103                p.add("trust_frameworks_supported");
104                p.add("evidence_supported");
105                p.add("id_documents_supported");
106                p.add("id_documents_verification_methods_supported");
107                p.add("claims_in_verified_claims_supported");
108                REGISTERED_PARAMETER_NAMES = Collections.unmodifiableSet(p);
109        }
110
111
112        /**
113         * The UserInfo endpoint.
114         */
115        private URI userInfoEndpoint;
116        
117        
118        /**
119         * The cross-origin check session iframe.
120         */
121        private URI checkSessionIframe;
122        
123        
124        /**
125         * The logout endpoint.
126         */
127        private URI endSessionEndpoint;
128
129
130        /**
131         * The supported ACRs.
132         */
133        private List<ACR> acrValues;
134
135
136        /**
137         * The supported subject types.
138         */
139        private final List<SubjectType> subjectTypes;
140
141
142        /**
143         * The supported ID token JWS algorithms.
144         */
145        private List<JWSAlgorithm> idTokenJWSAlgs;
146
147
148        /**
149         * The supported ID token JWE algorithms.
150         */
151        private List<JWEAlgorithm> idTokenJWEAlgs;
152
153
154        /**
155         * The supported ID token encryption methods.
156         */
157        private List<EncryptionMethod> idTokenJWEEncs;
158
159
160        /**
161         * The supported UserInfo JWS algorithms.
162         */
163        private List<JWSAlgorithm> userInfoJWSAlgs;
164
165
166        /**
167         * The supported UserInfo JWE algorithms.
168         */
169        private List<JWEAlgorithm> userInfoJWEAlgs;
170
171
172        /**
173         * The supported UserInfo encryption methods.
174         */
175        private List<EncryptionMethod> userInfoJWEEncs;
176
177
178        /**
179         * The supported displays.
180         */
181        private List<Display> displays;
182        
183        
184        /**
185         * The supported claim types.
186         */
187        private List<ClaimType> claimTypes;
188
189
190        /**
191         * The supported claims names.
192         */
193        private List<String> claims;
194        
195        
196        /**
197         * The supported claims locales.
198         */
199        private List<LangTag> claimsLocales;
200        
201        
202        /**
203         * If {@code true} the {@code claims} parameter is supported, else not.
204         */
205        private boolean claimsParamSupported = false;
206        
207        
208        /**
209         * If {@code true} the {@code frontchannel_logout_supported} parameter
210         * is set, else not.
211         */
212        private boolean frontChannelLogoutSupported = false;
213        
214        
215        /**
216         * If {@code true} the {@code frontchannel_logout_session_supported}
217         * parameter is set, else not.
218         */
219        private boolean frontChannelLogoutSessionSupported = false;
220        
221        
222        /**
223         * If {@code true} the {@code backchannel_logout_supported} parameter
224         * is set, else not.
225         */
226        private boolean backChannelLogoutSupported = false;
227        
228        
229        /**
230         * If {@code true} the {@code backchannel_logout_session_supported}
231         * parameter is set, else not.
232         */
233        private boolean backChannelLogoutSessionSupported = false;
234        
235        
236        /**
237         * If {@code true} verified claims are supported.
238         */
239        private boolean verifiedClaimsSupported = false;
240        
241        
242        /**
243         * The supported trust frameworks.
244         */
245        private List<IdentityTrustFramework> trustFrameworks;
246        
247        
248        /**
249         * The supported identity evidence types.
250         */
251        private List<IdentityEvidenceType> evidenceTypes;
252        
253        
254        /**
255         * The supported identity documents.
256         */
257        private List<IDDocumentType> idDocuments;
258        
259        
260        /**
261         * The supported identity verification methods.
262         */
263        private List<IdentityVerificationMethod> idVerificationMethods;
264        
265        
266        /**
267         * The supported verified claims.
268         */
269        private List<String> verifiedClaims;
270
271
272        /**
273         * Creates a new OpenID Connect provider metadata instance.
274         * 
275         * @param issuer       The issuer identifier. Must be an URI using the
276         *                     https scheme with no query or fragment 
277         *                     component. Must not be {@code null}.
278         * @param subjectTypes The supported subject types. At least one must
279         *                     be specified. Must not be {@code null}.
280         * @param jwkSetURI    The JWK set URI. Must not be {@code null}.
281         */
282        public OIDCProviderMetadata(final Issuer issuer,
283                                    final List<SubjectType> subjectTypes,
284                                    final URI jwkSetURI) {
285        
286                super(issuer);
287                
288                if (subjectTypes.size() < 1)
289                        throw new IllegalArgumentException("At least one supported subject type must be specified");
290                
291                this.subjectTypes = subjectTypes;
292
293                if (jwkSetURI == null)
294                        throw new IllegalArgumentException("The public JWK set URI must not be null");
295
296                setJWKSetURI(jwkSetURI);
297                
298                // Default OpenID Connect setting is supported
299                setSupportsRequestURIParam(true);
300        }
301        
302        
303        @Override
304        public void setMtlsEndpointAliases(AuthorizationServerEndpointMetadata mtlsEndpointAliases) {
305        
306                if (mtlsEndpointAliases != null && !(mtlsEndpointAliases instanceof OIDCProviderEndpointMetadata)) {
307                        // convert the provided endpoints to OIDC
308                        super.setMtlsEndpointAliases(new OIDCProviderEndpointMetadata(mtlsEndpointAliases));
309                } else {
310                        super.setMtlsEndpointAliases(mtlsEndpointAliases);
311                }
312        }
313        
314        @Override
315        public OIDCProviderEndpointMetadata getMtlsEndpointAliases() {
316        
317                return (OIDCProviderEndpointMetadata) super.getMtlsEndpointAliases();
318        }
319
320
321        /**
322         * Gets the registered OpenID Connect provider metadata parameter
323         * names.
324         *
325         * @return The registered OpenID Connect provider metadata parameter
326         *         names, as an unmodifiable set.
327         */
328        public static Set<String> getRegisteredParameterNames() {
329
330                return REGISTERED_PARAMETER_NAMES;
331        }
332
333
334        /**
335         * Gets the UserInfo endpoint URI. Corresponds the
336         * {@code userinfo_endpoint} metadata field.
337         *
338         * @return The UserInfo endpoint URI, {@code null} if not specified.
339         */
340        public URI getUserInfoEndpointURI() {
341
342                return userInfoEndpoint;
343        }
344
345
346        /**
347         * Sets the UserInfo endpoint URI. Corresponds the
348         * {@code userinfo_endpoint} metadata field.
349         *
350         * @param userInfoEndpoint The UserInfo endpoint URI, {@code null} if
351         *                         not specified.
352         */
353        public void setUserInfoEndpointURI(final URI userInfoEndpoint) {
354
355                this.userInfoEndpoint = userInfoEndpoint;
356        }
357        
358        
359        /**
360         * Gets the cross-origin check session iframe URI. Corresponds to the
361         * {@code check_session_iframe} metadata field.
362         * 
363         * @return The check session iframe URI, {@code null} if not specified.
364         */
365        public URI getCheckSessionIframeURI() {
366                
367                return checkSessionIframe;
368        }
369
370
371        /**
372         * Sets the cross-origin check session iframe URI. Corresponds to the
373         * {@code check_session_iframe} metadata field.
374         *
375         * @param checkSessionIframe The check session iframe URI, {@code null}
376         *                           if not specified.
377         */
378        public void setCheckSessionIframeURI(final URI checkSessionIframe) {
379
380                this.checkSessionIframe = checkSessionIframe;
381        }
382        
383        
384        /**
385         * Gets the logout endpoint URI. Corresponds to the
386         * {@code end_session_endpoint} metadata field.
387         * 
388         * @return The logoout endpoint URI, {@code null} if not specified.
389         */
390        public URI getEndSessionEndpointURI() {
391                
392                return endSessionEndpoint;
393        }
394
395
396        /**
397         * Sets the logout endpoint URI. Corresponds to the
398         * {@code end_session_endpoint} metadata field.
399         *
400         * @param endSessionEndpoint The logoout endpoint URI, {@code null} if
401         *                           not specified.
402         */
403        public void setEndSessionEndpointURI(final URI endSessionEndpoint) {
404
405                this.endSessionEndpoint = endSessionEndpoint;
406        }
407
408        /**
409         * Gets the supported Authentication Context Class References (ACRs).
410         * Corresponds to the {@code acr_values_supported} metadata field.
411         *
412         * @return The supported ACRs, {@code null} if not specified.
413         */
414        public List<ACR> getACRs() {
415
416                return acrValues;
417        }
418
419
420        /**
421         * Sets the supported Authentication Context Class References (ACRs).
422         * Corresponds to the {@code acr_values_supported} metadata field.
423         *
424         * @param acrValues The supported ACRs, {@code null} if not specified.
425         */
426        public void setACRs(final List<ACR> acrValues) {
427
428                this.acrValues = acrValues;
429        }
430
431
432        /**
433         * Gets the supported subject types. Corresponds to the
434         * {@code subject_types_supported} metadata field.
435         *
436         * @return The supported subject types.
437         */
438        public List<SubjectType> getSubjectTypes() {
439
440                return subjectTypes;
441        }
442
443
444        /**
445         * Gets the supported JWS algorithms for ID tokens. Corresponds to the 
446         * {@code id_token_signing_alg_values_supported} metadata field.
447         *
448         * @return The supported JWS algorithms, {@code null} if not specified.
449         */
450        public List<JWSAlgorithm> getIDTokenJWSAlgs() {
451
452                return idTokenJWSAlgs;
453        }
454
455
456        /**
457         * Sets the supported JWS algorithms for ID tokens. Corresponds to the
458         * {@code id_token_signing_alg_values_supported} metadata field.
459         *
460         * @param idTokenJWSAlgs The supported JWS algorithms, {@code null} if
461         *                       not specified.
462         */
463        public void setIDTokenJWSAlgs(final List<JWSAlgorithm> idTokenJWSAlgs) {
464
465                this.idTokenJWSAlgs = idTokenJWSAlgs;
466        }
467
468
469        /**
470         * Gets the supported JWE algorithms for ID tokens. Corresponds to the 
471         * {@code id_token_encryption_alg_values_supported} metadata field.
472         *
473         * @return The supported JWE algorithms, {@code null} if not specified.
474         */
475        public List<JWEAlgorithm> getIDTokenJWEAlgs() {
476
477                return idTokenJWEAlgs;
478        }
479
480
481        /**
482         * Sets the supported JWE algorithms for ID tokens. Corresponds to the
483         * {@code id_token_encryption_alg_values_supported} metadata field.
484         *
485         * @param idTokenJWEAlgs The supported JWE algorithms, {@code null} if
486         *                       not specified.
487         */
488        public void setIDTokenJWEAlgs(final List<JWEAlgorithm> idTokenJWEAlgs) {
489
490                this.idTokenJWEAlgs = idTokenJWEAlgs;
491        }
492
493
494        /**
495         * Gets the supported encryption methods for ID tokens. Corresponds to 
496         * the {@code id_token_encryption_enc_values_supported} metadata field.
497         *
498         * @return The supported encryption methods, {@code null} if not 
499         *         specified.
500         */
501        public List<EncryptionMethod> getIDTokenJWEEncs() {
502
503                return idTokenJWEEncs;
504        }
505
506
507        /**
508         * Sets the supported encryption methods for ID tokens. Corresponds to
509         * the {@code id_token_encryption_enc_values_supported} metadata field.
510         *
511         * @param idTokenJWEEncs The supported encryption methods, {@code null}
512         *                       if not specified.
513         */
514        public void setIDTokenJWEEncs(final List<EncryptionMethod> idTokenJWEEncs) {
515
516                this.idTokenJWEEncs = idTokenJWEEncs;
517        }
518
519
520        /**
521         * Gets the supported JWS algorithms for UserInfo JWTs. Corresponds to 
522         * the {@code userinfo_signing_alg_values_supported} metadata field.
523         *
524         * @return The supported JWS algorithms, {@code null} if not specified.
525         */
526        public List<JWSAlgorithm> getUserInfoJWSAlgs() {
527
528                return userInfoJWSAlgs;
529        }
530
531
532        /**
533         * Sets the supported JWS algorithms for UserInfo JWTs. Corresponds to
534         * the {@code userinfo_signing_alg_values_supported} metadata field.
535         *
536         * @param userInfoJWSAlgs The supported JWS algorithms, {@code null} if
537         *                        not specified.
538         */
539        public void setUserInfoJWSAlgs(final List<JWSAlgorithm> userInfoJWSAlgs) {
540
541                this.userInfoJWSAlgs = userInfoJWSAlgs;
542        }
543
544
545        /**
546         * Gets the supported JWE algorithms for UserInfo JWTs. Corresponds to 
547         * the {@code userinfo_encryption_alg_values_supported} metadata field.
548         *
549         * @return The supported JWE algorithms, {@code null} if not specified.
550         */
551        public List<JWEAlgorithm> getUserInfoJWEAlgs() {
552
553                return userInfoJWEAlgs;
554        }
555
556
557        /**
558         * Sets the supported JWE algorithms for UserInfo JWTs. Corresponds to
559         * the {@code userinfo_encryption_alg_values_supported} metadata field.
560         *
561         * @param userInfoJWEAlgs The supported JWE algorithms, {@code null} if
562         *                        not specified.
563         */
564        public void setUserInfoJWEAlgs(final List<JWEAlgorithm> userInfoJWEAlgs) {
565
566                this.userInfoJWEAlgs = userInfoJWEAlgs;
567        }
568
569
570        /**
571         * Gets the supported encryption methods for UserInfo JWTs. Corresponds 
572         * to the {@code userinfo_encryption_enc_values_supported} metadata 
573         * field.
574         *
575         * @return The supported encryption methods, {@code null} if not 
576         *         specified.
577         */
578        public List<EncryptionMethod> getUserInfoJWEEncs() {
579
580                return userInfoJWEEncs;
581        }
582
583
584        /**
585         * Sets the supported encryption methods for UserInfo JWTs. Corresponds
586         * to the {@code userinfo_encryption_enc_values_supported} metadata
587         * field.
588         *
589         * @param userInfoJWEEncs The supported encryption methods,
590         *                        {@code null} if not specified.
591         */
592        public void setUserInfoJWEEncs(final List<EncryptionMethod> userInfoJWEEncs) {
593
594                this.userInfoJWEEncs = userInfoJWEEncs;
595        }
596
597
598        /**
599         * Gets the supported displays. Corresponds to the 
600         * {@code display_values_supported} metadata field.
601         *
602         * @return The supported displays, {@code null} if not specified.
603         */
604        public List<Display> getDisplays() {
605
606                return displays;
607        }
608
609
610        /**
611         * Sets the supported displays. Corresponds to the
612         * {@code display_values_supported} metadata field.
613         *
614         * @param displays The supported displays, {@code null} if not
615         *                 specified.
616         */
617        public void setDisplays(final List<Display> displays) {
618
619                this.displays = displays;
620        }
621        
622        
623        /**
624         * Gets the supported claim types. Corresponds to the 
625         * {@code claim_types_supported} metadata field.
626         * 
627         * @return The supported claim types, {@code null} if not specified.
628         */
629        public List<ClaimType> getClaimTypes() {
630                
631                return claimTypes;
632        }
633
634
635        /**
636         * Sets the supported claim types. Corresponds to the
637         * {@code claim_types_supported} metadata field.
638         *
639         * @param claimTypes The supported claim types, {@code null} if not
640         *                   specified.
641         */
642        public void setClaimTypes(final List<ClaimType> claimTypes) {
643
644                this.claimTypes = claimTypes;
645        }
646
647
648        /**
649         * Gets the supported claims names. Corresponds to the 
650         * {@code claims_supported} metadata field.
651         *
652         * @return The supported claims names, {@code null} if not specified.
653         */
654        public List<String> getClaims() {
655
656                return claims;
657        }
658
659
660        /**
661         * Sets the supported claims names. Corresponds to the
662         * {@code claims_supported} metadata field.
663         *
664         * @param claims The supported claims names, {@code null} if not
665         *               specified.
666         */
667        public void setClaims(final List<String> claims) {
668
669                this.claims = claims;
670        }
671        
672        
673        /**
674         * Gets the supported claims locales. Corresponds to the
675         * {@code claims_locales_supported} metadata field.
676         * 
677         * @return The supported claims locales, {@code null} if not specified.
678         */
679        public List<LangTag> getClaimsLocales() {
680                
681                return claimsLocales;
682        }
683
684
685        /**
686         * Sets the supported claims locales. Corresponds to the
687         * {@code claims_locales_supported} metadata field.
688         *
689         * @param claimsLocales The supported claims locales, {@code null} if
690         *                      not specified.
691         */
692        public void setClaimLocales(final List<LangTag> claimsLocales) {
693
694                this.claimsLocales = claimsLocales;
695        }
696        
697        
698        /**
699         * Gets the support for the {@code claims} authorisation request
700         * parameter. Corresponds to the {@code claims_parameter_supported} 
701         * metadata field.
702         * 
703         * @return {@code true} if the {@code claim} parameter is supported,
704         *         else {@code false}.
705         */
706        public boolean supportsClaimsParam() {
707                
708                return claimsParamSupported;
709        }
710
711
712        /**
713         * Sets the support for the {@code claims} authorisation request
714         * parameter. Corresponds to the {@code claims_parameter_supported}
715         * metadata field.
716         *
717         * @param claimsParamSupported {@code true} if the {@code claim}
718         *                             parameter is supported, else
719         *                             {@code false}.
720         */
721        public void setSupportsClaimsParams(final boolean claimsParamSupported) {
722
723                this.claimsParamSupported = claimsParamSupported;
724        }
725        
726        
727        /**
728         * Gets the support for front-channel logout. Corresponds to the
729         * {@code frontchannel_logout_supported} metadata field.
730         *
731         * @return {@code true} if front-channel logout is supported, else
732         *         {@code false}.
733         */
734        public boolean supportsFrontChannelLogout() {
735                
736                return frontChannelLogoutSupported;
737        }
738        
739        
740        /**
741         * Sets the support for front-channel logout. Corresponds to the
742         * {@code frontchannel_logout_supported} metadata field.
743         *
744         * @param frontChannelLogoutSupported {@code true} if front-channel
745         *                                    logout is supported, else
746         *                                    {@code false}.
747         */
748        public void setSupportsFrontChannelLogout(final boolean frontChannelLogoutSupported) {
749        
750                this.frontChannelLogoutSupported = frontChannelLogoutSupported;
751        }
752        
753        
754        /**
755         * Gets the support for front-channel logout with a session ID.
756         * Corresponds to the {@code frontchannel_logout_session_supported}
757         * metadata field.
758         *
759         * @return {@code true} if front-channel logout with a session ID is
760         *         supported, else {@code false}.
761         */
762        public boolean supportsFrontChannelLogoutSession() {
763                
764                return frontChannelLogoutSessionSupported;
765        }
766        
767        
768        /**
769         * Sets the support for front-channel logout with a session ID.
770         * Corresponds to the {@code frontchannel_logout_session_supported}
771         * metadata field.
772         *
773         * @param frontChannelLogoutSessionSupported {@code true} if
774         *                                           front-channel logout with
775         *                                           a session ID is supported,
776         *                                           else {@code false}.
777         */
778        public void setSupportsFrontChannelLogoutSession(final boolean frontChannelLogoutSessionSupported) {
779        
780                this.frontChannelLogoutSessionSupported = frontChannelLogoutSessionSupported;
781        }
782        
783        
784        /**
785         * Gets the support for back-channel logout. Corresponds to the
786         * {@code backchannel_logout_supported} metadata field.
787         *
788         * @return {@code true} if back-channel logout is supported, else
789         *         {@code false}.
790         */
791        public boolean supportsBackChannelLogout() {
792                
793                return backChannelLogoutSupported;
794        }
795        
796        
797        /**
798         * Sets the support for back-channel logout. Corresponds to the
799         * {@code backchannel_logout_supported} metadata field.
800         *
801         * @param backChannelLogoutSupported {@code true} if back-channel
802         *                                   logout is supported, else
803         *                                   {@code false}.
804         */
805        public void setSupportsBackChannelLogout(final boolean backChannelLogoutSupported) {
806        
807                this.backChannelLogoutSupported = backChannelLogoutSupported;
808        }
809        
810        
811        /**
812         * Gets the support for back-channel logout with a session ID.
813         * Corresponds to the {@code backchannel_logout_session_supported}
814         * metadata field.
815         *
816         * @return {@code true} if back-channel logout with a session ID is
817         *         supported, else {@code false}.
818         */
819        public boolean supportsBackChannelLogoutSession() {
820                
821                return backChannelLogoutSessionSupported;
822        }
823        
824        
825        /**
826         * Sets the support for back-channel logout with a session ID.
827         * Corresponds to the {@code backchannel_logout_session_supported}
828         * metadata field.
829         *
830         * @param backChannelLogoutSessionSupported {@code true} if
831         *                                          back-channel logout with a
832         *                                          session ID is supported,
833         *                                          else {@code false}.
834         */
835        public void setSupportsBackChannelLogoutSession(final boolean backChannelLogoutSessionSupported) {
836                
837                this.backChannelLogoutSessionSupported = backChannelLogoutSessionSupported;
838        }
839        
840        
841        /**
842         * Gets support for verified claims. Corresponds to the
843         * {@code verified_claims_supported} metadata field.
844         *
845         * @return {@code true} if verified claims are supported, else
846         *         {@code false}.
847         */
848        public boolean supportsVerifiedClaims() {
849                
850                return verifiedClaimsSupported;
851        }
852        
853        
854        /**
855         * Sets support for verified claims. Corresponds to the
856         * {@code verified_claims_supported} metadata field.
857         *
858         * @param verifiedClaimsSupported {@code true} if verified claims are
859         *                                supported, else {@code false}.
860         */
861        public void setSupportsVerifiedClaims(final boolean verifiedClaimsSupported) {
862                
863                this.verifiedClaimsSupported = verifiedClaimsSupported;
864        }
865        
866        
867        /**
868         * Gets the supported identity trust frameworks. Corresponds to the
869         * {@code trust_frameworks_supported} metadata field.
870         *
871         * @return The supported identity trust frameworks, {@code null} if not
872         *         specified.
873         */
874        public List<IdentityTrustFramework> getIdentityTrustFrameworks() {
875                return trustFrameworks;
876        }
877        
878        
879        /**
880         * Sets the supported identity trust frameworks. Corresponds to the
881         * {@code trust_frameworks_supported} metadata field.
882         *
883         * @param trustFrameworks The supported identity trust frameworks,
884         *                        {@code null} if not specified.
885         */
886        public void setIdentityTrustFrameworks(final List<IdentityTrustFramework> trustFrameworks) {
887                this.trustFrameworks = trustFrameworks;
888        }
889        
890        
891        /**
892         * Gets the supported identity evidence types. Corresponds to the
893         * {@code evidence_supported} metadata field.
894         *
895         * @return The supported identity evidence types, {@code null} if not
896         *         specified.
897         */
898        public List<IdentityEvidenceType> getIdentityEvidenceTypes() {
899                return evidenceTypes;
900        }
901        
902        
903        /**
904         * Sets the supported identity evidence types. Corresponds to the
905         * {@code evidence_supported} metadata field.
906         *
907         * @param evidenceTypes The supported identity evidence types,
908         *                      {@code null} if not specified.
909         */
910        public void setIdentityEvidenceTypes(final List<IdentityEvidenceType> evidenceTypes) {
911                this.evidenceTypes = evidenceTypes;
912        }
913        
914        
915        /**
916         * Gets the supported identity document types. Corresponds to the
917         * {@code id_documents_supported} metadata field.
918         *
919         * @return The supported identity documents types, {@code null}
920         *         if not specified.
921         */
922        public List<IDDocumentType> getIdentityDocumentTypes() {
923                return idDocuments;
924        }
925        
926        
927        /**
928         * Sets the supported identity document types. Corresponds to the
929         * {@code id_documents_supported} metadata field.
930         *
931         * @param idDocuments The supported identity document types,
932         *                    {@code null} if not specified.
933         */
934        public void setIdentityDocumentTypes(List<IDDocumentType> idDocuments) {
935                this.idDocuments = idDocuments;
936        }
937        
938        
939        /**
940         * Gets the supported identity verification methods. Corresponds to the
941         * {@code id_documents_verification_methods_supported} metadata field.
942         *
943         * @return The supported identity verification methods, {@code null}
944         *         if not specified.
945         */
946        public List<IdentityVerificationMethod> getIdentityVerificationMethods() {
947                return idVerificationMethods;
948        }
949        
950        
951        /**
952         * Sets the supported identity verification methods. Corresponds to the
953         * {@code id_documents_verification_methods_supported} metadata field.
954         *
955         * @param idVerificationMethods The supported identity verification
956         *                              methods, {@code null} if not specified.
957         */
958        public void setIdentityVerificationMethods(final List<IdentityVerificationMethod> idVerificationMethods) {
959                this.idVerificationMethods = idVerificationMethods;
960        }
961        
962        
963        /**
964         * Gets the supported verified claims names. Corresponds to the
965         * {@code claims_in_verified_claims_supported} metadata field.
966         *
967         * @return The supported verified claims names, {@code null} if not
968         *         specified.
969         */
970        public List<String> getVerifiedClaims() {
971                return verifiedClaims;
972        }
973        
974        
975        /**
976         * Gets the supported verified claims names. Corresponds to the
977         * {@code claims_in_verified_claims_supported} metadata field.
978         *
979         * @param verifiedClaims The supported verified claims names,
980         *                       {@code null} if not specified.
981         */
982        public void setVerifiedClaims(final List<String> verifiedClaims) {
983                this.verifiedClaims = verifiedClaims;
984        }
985        
986        
987        /**
988         * Applies the OpenID Provider metadata defaults where no values have
989         * been specified.
990         *
991         * <ul>
992         *     <li>The response modes default to {@code ["query", "fragment"]}.
993         *     <li>The grant types default to {@code ["authorization_code",
994         *         "implicit"]}.
995         *     <li>The token endpoint authentication methods default to
996         *         {@code ["client_secret_basic"]}.
997         *     <li>The claim types default to {@code ["normal]}.
998         * </ul>
999         */
1000        public void applyDefaults() {
1001
1002                super.applyDefaults();
1003
1004                if (claimTypes == null) {
1005                        claimTypes = new ArrayList<>(1);
1006                        claimTypes.add(ClaimType.NORMAL);
1007                }
1008        }
1009
1010
1011        /**
1012         * Returns the JSON object representation of this OpenID Connect
1013         * provider metadata.
1014         *
1015         * @return The JSON object representation.
1016         */
1017        public JSONObject toJSONObject() {
1018
1019                JSONObject o = super.toJSONObject();
1020
1021                // Mandatory fields
1022
1023                List<String> stringList = new ArrayList<>(subjectTypes.size());
1024
1025                for (SubjectType st: subjectTypes)
1026                        stringList.add(st.toString());
1027
1028                o.put("subject_types_supported", stringList);
1029
1030                // Optional fields
1031
1032                if (userInfoEndpoint != null)
1033                        o.put("userinfo_endpoint", userInfoEndpoint.toString());
1034
1035                if (checkSessionIframe != null)
1036                        o.put("check_session_iframe", checkSessionIframe.toString());
1037
1038                if (endSessionEndpoint != null)
1039                        o.put("end_session_endpoint", endSessionEndpoint.toString());
1040
1041                if (acrValues != null) {
1042                        o.put("acr_values_supported", Identifier.toStringList(acrValues));
1043                }
1044
1045                if (idTokenJWSAlgs != null) {
1046
1047                        stringList = new ArrayList<>(idTokenJWSAlgs.size());
1048
1049                        for (JWSAlgorithm alg: idTokenJWSAlgs)
1050                                stringList.add(alg.getName());
1051
1052                        o.put("id_token_signing_alg_values_supported", stringList);
1053                }
1054
1055                if (idTokenJWEAlgs != null) {
1056
1057                        stringList = new ArrayList<>(idTokenJWEAlgs.size());
1058
1059                        for (JWEAlgorithm alg: idTokenJWEAlgs)
1060                                stringList.add(alg.getName());
1061
1062                        o.put("id_token_encryption_alg_values_supported", stringList);
1063                }
1064
1065                if (idTokenJWEEncs != null) {
1066
1067                        stringList = new ArrayList<>(idTokenJWEEncs.size());
1068
1069                        for (EncryptionMethod m: idTokenJWEEncs)
1070                                stringList.add(m.getName());
1071
1072                        o.put("id_token_encryption_enc_values_supported", stringList);
1073                }
1074
1075                if (userInfoJWSAlgs != null) {
1076
1077                        stringList = new ArrayList<>(userInfoJWSAlgs.size());
1078
1079                        for (JWSAlgorithm alg: userInfoJWSAlgs)
1080                                stringList.add(alg.getName());
1081
1082                        o.put("userinfo_signing_alg_values_supported", stringList);
1083                }
1084
1085                if (userInfoJWEAlgs != null) {
1086
1087                        stringList = new ArrayList<>(userInfoJWEAlgs.size());
1088
1089                        for (JWEAlgorithm alg: userInfoJWEAlgs)
1090                                stringList.add(alg.getName());
1091
1092                        o.put("userinfo_encryption_alg_values_supported", stringList);
1093                }
1094
1095                if (userInfoJWEEncs != null) {
1096
1097                        stringList = new ArrayList<>(userInfoJWEEncs.size());
1098
1099                        for (EncryptionMethod m: userInfoJWEEncs)
1100                                stringList.add(m.getName());
1101
1102                        o.put("userinfo_encryption_enc_values_supported", stringList);
1103                }
1104
1105                if (displays != null) {
1106
1107                        stringList = new ArrayList<>(displays.size());
1108
1109                        for (Display d: displays)
1110                                stringList.add(d.toString());
1111
1112                        o.put("display_values_supported", stringList);
1113                }
1114
1115                if (claimTypes != null) {
1116
1117                        stringList = new ArrayList<>(claimTypes.size());
1118
1119                        for (ClaimType ct: claimTypes)
1120                                stringList.add(ct.toString());
1121
1122                        o.put("claim_types_supported", stringList);
1123                }
1124
1125                if (claims != null)
1126                        o.put("claims_supported", claims);
1127
1128                if (claimsLocales != null) {
1129
1130                        stringList = new ArrayList<>(claimsLocales.size());
1131
1132                        for (LangTag l: claimsLocales)
1133                                stringList.add(l.toString());
1134
1135                        o.put("claims_locales_supported", stringList);
1136                }
1137
1138                if (claimsParamSupported) {
1139                        o.put("claims_parameter_supported", true);
1140                }
1141                
1142                if (supportsRequestURIParam()) {
1143                        // default true
1144                        o.remove("request_uri_parameter_supported");
1145                } else {
1146                        o.put("request_uri_parameter_supported", false);
1147                }
1148                
1149                // optional front and back-channel logout
1150                if (frontChannelLogoutSupported) {
1151                        o.put("frontchannel_logout_supported", true);
1152                }
1153                
1154                if (frontChannelLogoutSupported) {
1155                        o.put("frontchannel_logout_session_supported", frontChannelLogoutSessionSupported);
1156                }
1157                
1158                if (backChannelLogoutSupported) {
1159                        o.put("backchannel_logout_supported", true);
1160                }
1161                
1162                if (backChannelLogoutSupported) {
1163                        o.put("backchannel_logout_session_supported", backChannelLogoutSessionSupported);
1164                }
1165                
1166                if (verifiedClaimsSupported) {
1167                        o.put("verified_claims_supported", true);
1168                        if (trustFrameworks != null) {
1169                                o.put("trust_frameworks_supported", Identifier.toStringList(trustFrameworks));
1170                        }
1171                        if (evidenceTypes != null) {
1172                                o.put("evidence_supported", Identifier.toStringList(evidenceTypes));
1173                        }
1174                        if (idDocuments != null) {
1175                                o.put("id_documents_supported", Identifier.toStringList(idDocuments));
1176                        }
1177                        if (idVerificationMethods != null) {
1178                                o.put("id_documents_verification_methods_supported", Identifier.toStringList(idVerificationMethods));
1179                        }
1180                        if (verifiedClaims != null) {
1181                                o.put("claims_in_verified_claims_supported", verifiedClaims);
1182                        }
1183                }
1184                
1185                return o;
1186        }
1187        
1188        
1189        /**
1190         * Parses an OpenID Provider metadata from the specified JSON object.
1191         *
1192         * @param jsonObject The JSON object to parse. Must not be 
1193         *                   {@code null}.
1194         *
1195         * @return The OpenID Provider metadata.
1196         *
1197         * @throws ParseException If the JSON object couldn't be parsed to an
1198         *                        OpenID Provider metadata.
1199         */
1200        public static OIDCProviderMetadata parse(final JSONObject jsonObject)
1201                throws ParseException {
1202                
1203                AuthorizationServerMetadata as = AuthorizationServerMetadata.parse(jsonObject);
1204
1205                List<SubjectType> subjectTypes = new ArrayList<>();
1206                for (String v: JSONObjectUtils.getStringArray(jsonObject, "subject_types_supported")) {
1207                        subjectTypes.add(SubjectType.parse(v));
1208                }
1209                
1210                OIDCProviderMetadata op = new OIDCProviderMetadata(
1211                        as.getIssuer(),
1212                        Collections.unmodifiableList(subjectTypes),
1213                        as.getJWKSetURI());
1214
1215                // Endpoints
1216                op.setAuthorizationEndpointURI(as.getAuthorizationEndpointURI());
1217                op.setTokenEndpointURI(as.getTokenEndpointURI());
1218                op.setRegistrationEndpointURI(as.getRegistrationEndpointURI());
1219                op.setIntrospectionEndpointURI(as.getIntrospectionEndpointURI());
1220                op.setRevocationEndpointURI(as.getRevocationEndpointURI());
1221                op.setRequestObjectEndpoint(as.getRequestObjectEndpoint());
1222                op.userInfoEndpoint = JSONObjectUtils.getURI(jsonObject, "userinfo_endpoint", null);
1223                op.checkSessionIframe = JSONObjectUtils.getURI(jsonObject, "check_session_iframe", null);
1224                op.endSessionEndpoint = JSONObjectUtils.getURI(jsonObject, "end_session_endpoint", null);
1225
1226                // Capabilities
1227                op.setScopes(as.getScopes());
1228                op.setResponseTypes(as.getResponseTypes());
1229                op.setResponseModes(as.getResponseModes());
1230                op.setGrantTypes(as.getGrantTypes());
1231                
1232                op.setTokenEndpointAuthMethods(as.getTokenEndpointAuthMethods());
1233                op.setTokenEndpointJWSAlgs(as.getTokenEndpointJWSAlgs());
1234                
1235                op.setIntrospectionEndpointAuthMethods(as.getIntrospectionEndpointAuthMethods());
1236                op.setIntrospectionEndpointJWSAlgs(as.getIntrospectionEndpointJWSAlgs());
1237                
1238                op.setRevocationEndpointAuthMethods(as.getRevocationEndpointAuthMethods());
1239                op.setRevocationEndpointJWSAlgs(as.getRevocationEndpointJWSAlgs());
1240                
1241                op.setRequestObjectJWSAlgs(as.getRequestObjectJWSAlgs());
1242                op.setRequestObjectJWEAlgs(as.getRequestObjectJWEAlgs());
1243                op.setRequestObjectJWEEncs(as.getRequestObjectJWEEncs());
1244                
1245                op.setSupportsRequestParam(as.supportsRequestParam());
1246                op.setSupportsRequestURIParam(as.supportsRequestURIParam());
1247                op.setRequiresRequestURIRegistration(as.requiresRequestURIRegistration());
1248                
1249                op.setCodeChallengeMethods(as.getCodeChallengeMethods());
1250
1251                if (jsonObject.get("acr_values_supported") != null) {
1252
1253                        op.acrValues = new ArrayList<>();
1254
1255                        for (String v: JSONObjectUtils.getStringArray(jsonObject, "acr_values_supported")) {
1256
1257                                if (v != null)
1258                                        op.acrValues.add(new ACR(v));
1259                        }
1260                }
1261                
1262                // ID token
1263
1264                if (jsonObject.get("id_token_signing_alg_values_supported") != null) {
1265
1266                        op.idTokenJWSAlgs = new ArrayList<>();
1267
1268                        for (String v: JSONObjectUtils.getStringArray(jsonObject, "id_token_signing_alg_values_supported")) {
1269
1270                                if (v != null)
1271                                        op.idTokenJWSAlgs.add(JWSAlgorithm.parse(v));
1272                        }
1273                }
1274
1275
1276                if (jsonObject.get("id_token_encryption_alg_values_supported") != null) {
1277
1278                        op.idTokenJWEAlgs = new ArrayList<>();
1279
1280                        for (String v: JSONObjectUtils.getStringArray(jsonObject, "id_token_encryption_alg_values_supported")) {
1281
1282                                if (v != null)
1283                                        op.idTokenJWEAlgs.add(JWEAlgorithm.parse(v));
1284                        }
1285                }
1286
1287
1288                if (jsonObject.get("id_token_encryption_enc_values_supported") != null) {
1289
1290                        op.idTokenJWEEncs = new ArrayList<>();
1291
1292                        for (String v: JSONObjectUtils.getStringArray(jsonObject, "id_token_encryption_enc_values_supported")) {
1293
1294                                if (v != null)
1295                                        op.idTokenJWEEncs.add(EncryptionMethod.parse(v));
1296                        }
1297                }
1298
1299                // UserInfo
1300
1301                if (jsonObject.get("userinfo_signing_alg_values_supported") != null) {
1302
1303                        op.userInfoJWSAlgs = new ArrayList<>();
1304
1305                        for (String v: JSONObjectUtils.getStringArray(jsonObject, "userinfo_signing_alg_values_supported")) {
1306
1307                                if (v != null)
1308                                        op.userInfoJWSAlgs.add(JWSAlgorithm.parse(v));
1309                        }
1310                }
1311
1312
1313                if (jsonObject.get("userinfo_encryption_alg_values_supported") != null) {
1314
1315                        op.userInfoJWEAlgs = new ArrayList<>();
1316
1317                        for (String v: JSONObjectUtils.getStringArray(jsonObject, "userinfo_encryption_alg_values_supported")) {
1318
1319                                if (v != null)
1320                                        op.userInfoJWEAlgs.add(JWEAlgorithm.parse(v));
1321                        }
1322                }
1323
1324
1325                if (jsonObject.get("userinfo_encryption_enc_values_supported") != null) {
1326
1327                        op.userInfoJWEEncs = new ArrayList<>();
1328
1329                        for (String v: JSONObjectUtils.getStringArray(jsonObject, "userinfo_encryption_enc_values_supported")) {
1330
1331                                        if (v != null)
1332                                                op.userInfoJWEEncs.add(EncryptionMethod.parse(v));
1333                        }
1334                }
1335
1336                
1337                // Misc
1338
1339                if (jsonObject.get("display_values_supported") != null) {
1340
1341                        op.displays = new ArrayList<>();
1342
1343                        for (String v: JSONObjectUtils.getStringArray(jsonObject, "display_values_supported")) {
1344
1345                                if (v != null)
1346                                        op.displays.add(Display.parse(v));
1347                        }
1348                }
1349                
1350                if (jsonObject.get("claim_types_supported") != null) {
1351                        
1352                        op.claimTypes = new ArrayList<>();
1353                        
1354                        for (String v: JSONObjectUtils.getStringArray(jsonObject, "claim_types_supported")) {
1355                                
1356                                if (v != null)
1357                                        op.claimTypes.add(ClaimType.parse(v));
1358                        }
1359                }
1360
1361
1362                if (jsonObject.get("claims_supported") != null) {
1363
1364                        op.claims = new ArrayList<>();
1365
1366                        for (String v: JSONObjectUtils.getStringArray(jsonObject, "claims_supported")) {
1367
1368                                if (v != null)
1369                                        op.claims.add(v);
1370                        }
1371                }
1372                
1373                if (jsonObject.get("claims_locales_supported") != null) {
1374                        
1375                        op.claimsLocales = new ArrayList<>();
1376                        
1377                        for (String v : JSONObjectUtils.getStringArray(jsonObject, "claims_locales_supported")) {
1378                                
1379                                if (v != null) {
1380                                        
1381                                        try {
1382                                                op.claimsLocales.add(LangTag.parse(v));
1383                                        
1384                                        } catch (LangTagException e) {
1385                                                
1386                                                throw new ParseException("Invalid claims_locales_supported field: " + e.getMessage(), e);
1387                                        }
1388                                }
1389                        }
1390                }
1391                
1392                op.setUILocales(as.getUILocales());
1393                op.setServiceDocsURI(as.getServiceDocsURI());
1394                op.setPolicyURI(as.getPolicyURI());
1395                op.setTermsOfServiceURI(as.getTermsOfServiceURI());
1396                
1397                if (jsonObject.get("claims_parameter_supported") != null)
1398                        op.claimsParamSupported = JSONObjectUtils.getBoolean(jsonObject, "claims_parameter_supported");
1399                
1400                if (jsonObject.get("request_uri_parameter_supported") == null) {
1401                        op.setSupportsRequestURIParam(true);
1402                }
1403                
1404                // Optional front and back-channel logout
1405                if (jsonObject.get("frontchannel_logout_supported") != null)
1406                        op.frontChannelLogoutSupported = JSONObjectUtils.getBoolean(jsonObject, "frontchannel_logout_supported");
1407                
1408                if (op.frontChannelLogoutSupported && jsonObject.get("frontchannel_logout_session_supported") != null)
1409                        op.frontChannelLogoutSessionSupported = JSONObjectUtils.getBoolean(jsonObject, "frontchannel_logout_session_supported");
1410                
1411                if (jsonObject.get("backchannel_logout_supported") != null)
1412                        op.backChannelLogoutSupported = JSONObjectUtils.getBoolean(jsonObject, "backchannel_logout_supported");
1413                
1414                if (op.frontChannelLogoutSupported && jsonObject.get("backchannel_logout_session_supported") != null)
1415                        op.backChannelLogoutSessionSupported = JSONObjectUtils.getBoolean(jsonObject, "backchannel_logout_session_supported");
1416                
1417                if (jsonObject.get("mtls_endpoint_aliases") != null)
1418                        op.setMtlsEndpointAliases(OIDCProviderEndpointMetadata.parse(JSONObjectUtils.getJSONObject(jsonObject, "mtls_endpoint_aliases")));
1419                
1420                op.setSupportsTLSClientCertificateBoundAccessTokens(as.supportsTLSClientCertificateBoundAccessTokens());
1421                
1422                // JARM
1423                op.setAuthorizationJWSAlgs(as.getAuthorizationJWSAlgs());
1424                op.setAuthorizationJWEAlgs(as.getAuthorizationJWEAlgs());
1425                op.setAuthorizationJWEEncs(as.getAuthorizationJWEEncs());
1426                
1427                // OpenID Connect for Identity Assurance 1.0
1428                if (jsonObject.get("verified_claims_supported") != null) {
1429                        op.verifiedClaimsSupported = JSONObjectUtils.getBoolean(jsonObject, "verified_claims_supported");
1430                        if (op.verifiedClaimsSupported) {
1431                                if (jsonObject.get("trust_frameworks_supported") != null) {
1432                                        op.trustFrameworks = new LinkedList<>();
1433                                        for (String v : JSONObjectUtils.getStringList(jsonObject, "trust_frameworks_supported")) {
1434                                                op.trustFrameworks.add(new IdentityTrustFramework(v));
1435                                        }
1436                                }
1437                                if (jsonObject.get("evidence_supported") != null) {
1438                                        op.evidenceTypes = new LinkedList<>();
1439                                        for (String v: JSONObjectUtils.getStringList(jsonObject, "evidence_supported")) {
1440                                                op.evidenceTypes.add(new IdentityEvidenceType(v));
1441                                        }
1442                                }
1443                                if (jsonObject.get("id_documents_supported") != null) {
1444                                        op.idDocuments = new LinkedList<>();
1445                                        for (String v: JSONObjectUtils.getStringList(jsonObject, "id_documents_supported")) {
1446                                                op.idDocuments.add(new IDDocumentType(v));
1447                                        }
1448                                }
1449                                if (jsonObject.get("id_documents_verification_methods_supported") != null) {
1450                                        op.idVerificationMethods = new LinkedList<>();
1451                                        for (String v: JSONObjectUtils.getStringList(jsonObject, "id_documents_verification_methods_supported")) {
1452                                                op.idVerificationMethods.add(new IdentityVerificationMethod(v));
1453                                        }
1454                                }
1455                                if (jsonObject.get("claims_in_verified_claims_supported") != null) {
1456                                        op.verifiedClaims = JSONObjectUtils.getStringList(jsonObject, "claims_in_verified_claims_supported");
1457                                }
1458                        }
1459                }
1460                
1461                // Parse custom (not registered) parameters
1462                for (Map.Entry<String,?> entry: as.getCustomParameters().entrySet()) {
1463                        if (REGISTERED_PARAMETER_NAMES.contains(entry.getKey()))
1464                                continue; // skip
1465                        op.setCustomParameter(entry.getKey(), entry.getValue());
1466                }
1467
1468                return op;
1469        }
1470
1471
1472        /**
1473         * Parses an OpenID Provider metadata from the specified JSON object
1474         * string.
1475         *
1476         * @param s The JSON object sting to parse. Must not be {@code null}.
1477         *
1478         * @return The OpenID Provider metadata.
1479         *
1480         * @throws ParseException If the JSON object string couldn't be parsed
1481         *                        to an OpenID Provider metadata.
1482         */
1483        public static OIDCProviderMetadata parse(final String s)
1484                throws ParseException {
1485
1486                return parse(JSONObjectUtils.parse(s));
1487        }
1488        
1489        
1490        /**
1491         * Resolves OpenID Provider metadata URL from the specified issuer
1492         * identifier.
1493         *
1494         * @param issuer The OpenID Provider issuer identifier. Must represent
1495         *               a valid HTTPS or HTTP URL. Must not be {@code null}.
1496         *
1497         * @return The OpenID Provider metadata URL.
1498         *
1499         * @throws GeneralException If the issuer identifier is invalid.
1500         */
1501        public static URL resolveURL(final Issuer issuer)
1502                throws GeneralException {
1503                
1504                try {
1505                        URL issuerURL = new URL(issuer.getValue());
1506                        
1507                        // Validate but don't insist on HTTPS, see
1508                        // http://openid.net/specs/openid-connect-core-1_0.html#Terminology
1509                        if (issuerURL.getQuery() != null && ! issuerURL.getQuery().trim().isEmpty()) {
1510                                throw new GeneralException("The issuer identifier must not contain a query component");
1511                        }
1512                        
1513                        if (issuerURL.getPath() != null && issuerURL.getPath().endsWith("/")) {
1514                                return new URL(issuerURL + ".well-known/openid-configuration");
1515                        } else {
1516                                return new URL(issuerURL + "/.well-known/openid-configuration");
1517                        }
1518                        
1519                } catch (MalformedURLException e) {
1520                        throw new GeneralException("The issuer identifier doesn't represent a valid URL: " + e.getMessage(), e);
1521                }
1522        }
1523        
1524        
1525        /**
1526         * Resolves OpenID Provider metadata from the specified issuer
1527         * identifier. The metadata is downloaded by HTTP GET from
1528         * {@code [issuer-url]/.well-known/openid-configuration}.
1529         *
1530         * @param issuer The OpenID Provider issuer identifier. Must represent
1531         *               a valid HTTPS or HTTP URL. Must not be {@code null}.
1532         *
1533         * @return The OpenID Provider metadata.
1534         *
1535         * @throws GeneralException If the issuer identifier or the downloaded
1536         *                          metadata are invalid.
1537         * @throws IOException      On a HTTP exception.
1538         */
1539        public static OIDCProviderMetadata resolve(final Issuer issuer)
1540                throws GeneralException, IOException {
1541                
1542                return resolve(issuer, 0, 0);
1543        }
1544        
1545        
1546        /**
1547         * Resolves OpenID Provider metadata from the specified issuer
1548         * identifier. The metadata is downloaded by HTTP GET from
1549         * {@code [issuer-url]/.well-known/openid-configuration}, using the
1550         * specified HTTP timeouts.
1551         *
1552         * @param issuer         The issuer identifier. Must represent a valid
1553         *                       HTTPS or HTTP URL. Must not be {@code null}.
1554         * @param connectTimeout The HTTP connect timeout, in milliseconds.
1555         *                       Zero implies no timeout. Must not be negative.
1556         * @param readTimeout    The HTTP response read timeout, in
1557         *                       milliseconds. Zero implies no timeout. Must
1558         *                       not be negative.
1559         *
1560         * @return The OpenID Provider metadata.
1561         *
1562         * @throws GeneralException If the issuer identifier or the downloaded
1563         *                          metadata are invalid.
1564         * @throws IOException      On a HTTP exception.
1565         */
1566        public static OIDCProviderMetadata resolve(final Issuer issuer,
1567                                                   final int connectTimeout,
1568                                                   final int readTimeout)
1569                throws GeneralException, IOException {
1570                
1571                URL configURL = resolveURL(issuer);
1572                
1573                HTTPRequest httpRequest = new HTTPRequest(HTTPRequest.Method.GET, configURL);
1574                httpRequest.setConnectTimeout(connectTimeout);
1575                httpRequest.setReadTimeout(readTimeout);
1576                
1577                HTTPResponse httpResponse = httpRequest.send();
1578                
1579                if (httpResponse.getStatusCode() != 200) {
1580                        throw new IOException("Couldn't download OpenID Provider metadata from " + configURL +
1581                                ": Status code " + httpResponse.getStatusCode());
1582                }
1583                
1584                JSONObject jsonObject = httpResponse.getContentAsJSONObject();
1585                
1586                OIDCProviderMetadata op = OIDCProviderMetadata.parse(jsonObject);
1587                
1588                if (! issuer.equals(op.getIssuer())) {
1589                        throw new GeneralException("The returned issuer doesn't match the expected: " + op.getIssuer());
1590                }
1591                
1592                return op;
1593        }
1594}