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