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