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.oauth2.sdk.client;
019
020
021import java.net.URI;
022import java.net.URISyntaxException;
023import java.util.*;
024import javax.mail.internet.AddressException;
025import javax.mail.internet.InternetAddress;
026
027import com.nimbusds.jose.EncryptionMethod;
028import com.nimbusds.jose.JWEAlgorithm;
029import com.nimbusds.jose.JWSAlgorithm;
030import com.nimbusds.jose.jwk.JWKSet;
031import com.nimbusds.langtag.LangTag;
032import com.nimbusds.langtag.LangTagUtils;
033import com.nimbusds.oauth2.sdk.GrantType;
034import com.nimbusds.oauth2.sdk.ParseException;
035import com.nimbusds.oauth2.sdk.ResponseType;
036import com.nimbusds.oauth2.sdk.Scope;
037import com.nimbusds.oauth2.sdk.auth.ClientAuthenticationMethod;
038import com.nimbusds.oauth2.sdk.id.SoftwareID;
039import com.nimbusds.oauth2.sdk.id.SoftwareVersion;
040import com.nimbusds.oauth2.sdk.util.JSONObjectUtils;
041import net.minidev.json.JSONArray;
042import net.minidev.json.JSONObject;
043
044
045/**
046 * Client metadata.
047 * 
048 * <p>Example client metadata, serialised to a JSON object:
049 * 
050 * <pre>
051 * {
052 *  "redirect_uris"              : ["https://client.example.org/callback",
053 *                                  "https://client.example.org/callback2"],
054 *  "client_name"                : "My Example Client",
055 *  "client_name#ja-Jpan-JP"     : "\u30AF\u30E9\u30A4\u30A2\u30F3\u30C8\u540D",
056 *  "token_endpoint_auth_method" : "client_secret_basic",
057 *  "scope"                      : "read write dolphin",
058 *  "logo_uri"                   : "https://client.example.org/logo.png",
059 *  "jwks_uri"                   : "https://client.example.org/my_public_keys.jwks"
060 * }
061 * </pre>
062 * 
063 * <p>Related specifications:
064 *
065 * <ul>
066 *     <li>OAuth 2.0 Dynamic Client Registration Protocol (RFC 7591), section
067 *         2.
068 *     <li>OAuth 2.0 Mutual TLS Client Authentication and Certificate Bound
069 *         Access Tokens (draft-ietf-oauth-mtls-12), sections 2.1.2 and 3.4.
070 *     <li>Financial-grade API: JWT Secured Authorization Response Mode for
071 *         OAuth 2.0 (JARM).
072 * </ul>
073 */
074public class ClientMetadata {
075
076
077        /**
078         * The registered parameter names.
079         */
080        private static final Set<String> REGISTERED_PARAMETER_NAMES;
081
082
083        static {
084                Set<String> p = new HashSet<>();
085
086                p.add("redirect_uris");
087                p.add("scope");
088                p.add("response_types");
089                p.add("grant_types");
090                p.add("contacts");
091                p.add("client_name");
092                p.add("logo_uri");
093                p.add("client_uri");
094                p.add("policy_uri");
095                p.add("tos_uri");
096                p.add("token_endpoint_auth_method");
097                p.add("token_endpoint_auth_signing_alg");
098                p.add("jwks_uri");
099                p.add("jwks");
100                p.add("request_uris");
101                p.add("request_object_signing_alg");
102                p.add("request_object_encryption_alg");
103                p.add("request_object_encryption_enc");
104                p.add("software_id");
105                p.add("software_version");
106                p.add("tls_client_certificate_bound_access_tokens");
107                p.add("tls_client_auth_subject_dn");
108                p.add("authorization_signed_response_alg");
109                p.add("authorization_encrypted_response_alg");
110                p.add("authorization_encrypted_response_enc");
111
112                REGISTERED_PARAMETER_NAMES = Collections.unmodifiableSet(p);
113        }
114        
115        
116        /**
117         * Redirect URIs.
118         */
119        private Set<URI> redirectURIs;
120
121
122        /**
123         * The client OAuth 2.0 scope.
124         */
125        private Scope scope;
126
127
128        /**
129         * The expected OAuth 2.0 response types.
130         */
131        private Set<ResponseType> responseTypes;
132
133
134        /**
135         * The expected OAuth 2.0 grant types.
136         */
137        private Set<GrantType> grantTypes;
138
139
140        /**
141         * Administrator email contacts for the client.
142         */
143        private List<String> contacts;
144
145
146        /**
147         * The client name.
148         */
149        private final Map<LangTag,String> nameEntries;
150
151
152        /**
153         * The client application logo.
154         */
155        private final Map<LangTag,URI> logoURIEntries;
156
157
158        /**
159         * The client URI entries.
160         */
161        private final Map<LangTag,URI> uriEntries;
162
163
164        /**
165         * The client policy for use of end-user data.
166         */
167        private Map<LangTag,URI> policyURIEntries;
168
169
170        /**
171         * The client terms of service.
172         */
173        private final Map<LangTag,URI> tosURIEntries;
174
175
176        /**
177         * Token endpoint authentication method.
178         */
179        private ClientAuthenticationMethod authMethod;
180
181
182        /**
183         * The JSON Web Signature (JWS) algorithm required for
184         * {@code private_key_jwt} and {@code client_secret_jwt}
185         * authentication at the Token endpoint.
186         */
187        private JWSAlgorithm authJWSAlg;
188
189
190        /**
191         * URI for this client's JSON Web Key (JWK) set containing key(s) that
192         * are used in signing requests to the server and key(s) for encrypting
193         * responses.
194         */
195        private URI jwkSetURI;
196
197
198        /**
199         * Client's JSON Web Key (JWK) set containing key(s) that are used in
200         * signing requests to the server and key(s) for encrypting responses.
201         * Intended as an alternative to {@link #jwkSetURI} for native clients.
202         */
203        private JWKSet jwkSet;
204        
205        
206        /**
207         * Pre-registered request object URIs.
208         */
209        private Set<URI> requestObjectURIs;
210        
211        
212        /**
213         * The JSON Web Signature (JWS) algorithm required for request objects
214         * sent by this client.
215         */
216        private JWSAlgorithm requestObjectJWSAlg;
217        
218        
219        /**
220         * The JSON Web Encryption (JWE) algorithm required for request objects
221         * sent by this client.
222         */
223        private JWEAlgorithm requestObjectJWEAlg;
224        
225        
226        /**
227         * The JSON Web Encryption (JWE) method required for request objects
228         * sent by this client.
229         */
230        private EncryptionMethod requestObjectJWEEnc;
231
232
233        /**
234         * Identifier for the OAuth 2.0 client software.
235         */
236        private SoftwareID softwareID;
237
238
239        /**
240         * Version identifier for the OAuth 2.0 client software.
241         */
242        private SoftwareVersion softwareVersion;
243        
244        
245        /**
246         * Preference for TLS client certificate bound access tokens.
247         */
248        private boolean tlsClientCertificateBoundAccessTokens = false;
249        
250        
251        /**
252         * The expected subject distinguished name (DN) of the client X.509
253         * certificate the in mutual TLS authentication.
254         */
255        private String tlsClientAuthSubjectDN = null;
256        
257        
258        /**
259         * The JWS algorithm for JWT-encoded authorisation responses.
260         */
261        private JWSAlgorithm authzJWSAlg;
262        
263        
264        /**
265         * The JWE algorithm for JWT-encoded authorisation responses.
266         */
267        private JWEAlgorithm authzJWEAlg;
268        
269        
270        /**
271         * The encryption method for JWT-encoded authorisation responses.
272         */
273        private EncryptionMethod authzJWEEnc;
274
275
276        /**
277         * The custom metadata fields.
278         */
279        private JSONObject customFields;
280
281
282        /**
283         * Creates a new OAuth 2.0 client metadata instance.
284         */
285        public ClientMetadata() {
286
287                nameEntries = new HashMap<>();
288                logoURIEntries = new HashMap<>();
289                uriEntries = new HashMap<>();
290                policyURIEntries = new HashMap<>();
291                policyURIEntries = new HashMap<>();
292                tosURIEntries = new HashMap<>();
293                customFields = new JSONObject();
294        }
295
296
297        /**
298         * Creates a shallow copy of the specified OAuth 2.0 client metadata
299         * instance.
300         *
301         * @param metadata The client metadata to copy. Must not be
302         *                 {@code null}.
303         */
304        public ClientMetadata(final ClientMetadata metadata) {
305
306                redirectURIs = metadata.redirectURIs;
307                scope = metadata.scope;
308                responseTypes = metadata.responseTypes;
309                grantTypes = metadata.grantTypes;
310                contacts = metadata.contacts;
311                nameEntries = metadata.nameEntries;
312                logoURIEntries = metadata.logoURIEntries;
313                uriEntries = metadata.uriEntries;
314                policyURIEntries = metadata.policyURIEntries;
315                tosURIEntries = metadata.tosURIEntries;
316                authMethod = metadata.authMethod;
317                authJWSAlg = metadata.authJWSAlg;
318                jwkSetURI = metadata.jwkSetURI;
319                jwkSet = metadata.getJWKSet();
320                requestObjectURIs = metadata.requestObjectURIs;
321                requestObjectJWSAlg = metadata.requestObjectJWSAlg;
322                requestObjectJWEAlg = metadata.requestObjectJWEAlg;
323                requestObjectJWEEnc = metadata.requestObjectJWEEnc;
324                softwareID = metadata.softwareID;
325                softwareVersion = metadata.softwareVersion;
326                tlsClientCertificateBoundAccessTokens = metadata.tlsClientCertificateBoundAccessTokens;
327                tlsClientAuthSubjectDN = metadata.tlsClientAuthSubjectDN;
328                authzJWSAlg = metadata.authzJWSAlg;
329                authzJWEAlg = metadata.authzJWEAlg;
330                authzJWEEnc = metadata.authzJWEEnc;
331                customFields = metadata.customFields;
332        }
333
334
335        /**
336         * Gets the registered (standard) OAuth 2.0 client metadata parameter
337         * names.
338         *
339         * @return The registered parameter names, as an unmodifiable set.
340         */
341        public static Set<String> getRegisteredParameterNames() {
342
343                return REGISTERED_PARAMETER_NAMES;
344        }
345
346
347        /**
348         * Gets the redirection URIs for this client. Corresponds to the
349         * {@code redirect_uris} client metadata field.
350         *
351         * @return The redirection URIs, {@code null} if not specified.
352         */
353        public Set<URI> getRedirectionURIs() {
354
355                return redirectURIs;
356        }
357        
358        
359        /**
360         * Gets one of the redirection URIs for this client. Corresponds to the
361         * {@code redirect_uris} client metadata field.
362         *
363         * @return The redirection URI, {@code null} if not specified.
364         */
365        public URI getRedirectionURI() {
366                
367                if (redirectURIs != null && ! redirectURIs.isEmpty()) {
368                        return redirectURIs.iterator().next();
369                } else {
370                        return null;
371                }
372        }
373
374
375        /**
376         * Gets the redirection URIs for this client as strings. Corresponds to
377         * the {@code redirect_uris} client metadata field.
378         *
379         * <p>This short-hand method is intended to enable string-based URI
380         * comparison.
381         *
382         * @return The redirection URIs as strings, {@code null} if not
383         *         specified.
384         */
385        public Set<String> getRedirectionURIStrings() {
386
387                if (redirectURIs == null)
388                        return null;
389
390                Set<String> uriStrings = new HashSet<>();
391
392                for (URI uri: redirectURIs)
393                        uriStrings.add(uri.toString());
394
395                return uriStrings;
396        }
397
398
399        /**
400         * Sets the redirection URIs for this client. Corresponds to the
401         * {@code redirect_uris} client metadata field.
402         *
403         * @param redirectURIs The redirection URIs, {@code null} if not
404         *                     specified. Valid redirection URIs must not
405         *                     contain a fragment.
406         */
407        public void setRedirectionURIs(final Set<URI> redirectURIs) {
408
409                if (redirectURIs != null) {
410                        // check URIs
411                        for (URI uri: redirectURIs) {
412                                if (uri == null) {
413                                        throw new IllegalArgumentException("The redirect_uri must not be null");
414                                }
415                                if (uri.getFragment() != null) {
416                                        throw new IllegalArgumentException("The redirect_uri must not contain fragment");
417                                }
418                        }
419                        this.redirectURIs = redirectURIs;
420                } else {
421                        this.redirectURIs = null;
422                }
423        }
424
425
426        /**
427         * Sets a single redirection URI for this client. Corresponds to the
428         * {@code redirect_uris} client metadata field.
429         *
430         * @param redirectURI The redirection URIs, {@code null} if not
431         *                    specified. A valid redirection URI must not
432         *                    contain a fragment.
433         */
434        public void setRedirectionURI(final URI redirectURI) {
435
436                setRedirectionURIs(redirectURI != null ? Collections.singleton(redirectURI) : null);
437        }
438
439
440        /**
441         * Gets the scope values that the client can use when requesting access
442         * tokens. Corresponds to the {@code scope} client metadata field.
443         *
444         * @return The scope, {@code null} if not specified.
445         */
446        public Scope getScope() {
447
448                return scope;
449        }
450
451
452        /**
453         * Checks if the scope matadata field is set and contains the specified
454         * scope value.
455         *
456         * @param scopeValue The scope value. Must not be {@code null}.
457         *
458         * @return {@code true} if the scope value is contained, else
459         *         {@code false}.
460         */
461        public boolean hasScopeValue(final Scope.Value scopeValue) {
462
463                return scope != null && scope.contains(scopeValue);
464        }
465
466
467        /**
468         * Sets the scope values that the client can use when requesting access
469         * tokens. Corresponds to the {@code scope} client metadata field.
470         *
471         * @param scope The scope, {@code null} if not specified.
472         */
473        public void setScope(final Scope scope) {
474
475                this.scope = scope;
476        }
477
478
479        /**
480         * Gets the expected OAuth 2.0 response types. Corresponds to the
481         * {@code response_types} client metadata field.
482         *
483         * @return The response types, {@code null} if not specified.
484         */
485        public Set<ResponseType> getResponseTypes() {
486
487                return responseTypes;
488        }
489
490
491        /**
492         * Sets the expected OAuth 2.0 response types. Corresponds to the
493         * {@code response_types} client metadata field.
494         *
495         * @param responseTypes The response types, {@code null} if not
496         *                      specified.
497         */
498        public void setResponseTypes(final Set<ResponseType> responseTypes) {
499
500                this.responseTypes = responseTypes;
501        }
502
503
504        /**
505         * Gets the expected OAuth 2.0 grant types. Corresponds to the
506         * {@code grant_types} client metadata field.
507         *
508         * @return The grant types, {@code null} if not specified.
509         */
510        public Set<GrantType> getGrantTypes() {
511
512                return grantTypes;
513        }
514
515
516        /**
517         * Sets the expected OAuth 2.0 grant types. Corresponds to the
518         * {@code grant_types} client metadata field.
519         *
520         * @param grantTypes The grant types, {@code null} if not specified.
521         */
522        public void setGrantTypes(final Set<GrantType> grantTypes) {
523
524                this.grantTypes = grantTypes;
525        }
526
527
528        /**
529         * Gets the administrator email contacts for the client. Corresponds to
530         * the {@code contacts} client metadata field.
531         *
532         * <p>Use {@link #getEmailContacts()} instead.
533         *
534         * @return The administrator email contacts, {@code null} if not
535         *         specified.
536         */
537        @Deprecated
538        public List<InternetAddress> getContacts() {
539
540                if (contacts == null)
541                        return null;
542                
543                List<InternetAddress> addresses = new LinkedList<>();
544                for (String s: contacts) {
545                        if (s == null) continue;
546                        try {
547                                addresses.add(new InternetAddress(s, false));
548                        } catch (AddressException e) {
549                                // ignore
550                        }
551                }
552                return addresses;
553        }
554
555
556        /**
557         * Sets the administrator email contacts for the client. Corresponds to
558         * the {@code contacts} client metadata field.
559         *
560         * <p>Use {@link #setEmailContacts(List)} instead.
561         *
562         * @param contacts The administrator email contacts, {@code null} if
563         *                 not specified.
564         */
565        @Deprecated
566        public void setContacts(final List<InternetAddress> contacts) {
567
568                if (contacts == null) {
569                        this.contacts = null;
570                        return;
571                }
572                
573                List<String> addresses = new LinkedList<>();
574                for (InternetAddress a: contacts) {
575                        if (a != null) {
576                                addresses.add(a.toString());
577                        }
578                }
579                this.contacts = addresses;
580        }
581
582
583        /**
584         * Gets the administrator email contacts for the client. Corresponds to
585         * the {@code contacts} client metadata field.
586         *
587         * @return The administrator email contacts, {@code null} if not
588         *         specified.
589         */
590        public List<String> getEmailContacts() {
591
592                return contacts;
593        }
594
595
596        /**
597         * Sets the administrator email contacts for the client. Corresponds to
598         * the {@code contacts} client metadata field.
599         *
600         * @param contacts The administrator email contacts, {@code null} if
601         *                 not specified.
602         */
603        public void setEmailContacts(final List<String> contacts) {
604
605                this.contacts = contacts;
606        }
607
608
609        /**
610         * Gets the client name. Corresponds to the {@code client_name} client
611         * metadata field, with no language tag.
612         *
613         * @return The client name, {@code null} if not specified.
614         */
615        public String getName() {
616
617                return getName(null);
618        }
619
620
621        /**
622         * Gets the client name. Corresponds to the {@code client_name} client
623         * metadata field, with an optional language tag.
624         *
625         * @param langTag The language tag of the entry, {@code null} to get
626         *                the non-tagged entry.
627         *
628         * @return The client name, {@code null} if not specified.
629         */
630        public String getName(final LangTag langTag) {
631
632                return nameEntries.get(langTag);
633        }
634
635
636        /**
637         * Gets the client name entries. Corresponds to the {@code client_name}
638         * client metadata field.
639         *
640         * @return The client name entries, empty map if none.
641         */
642        public Map<LangTag,String> getNameEntries() {
643
644                return nameEntries;
645        }
646
647
648        /**
649         * Sets the client name. Corresponds to the {@code client_name} client
650         * metadata field, with no language tag.
651         *
652         * @param name The client name, {@code null} if not specified.
653         */
654        public void setName(final String name) {
655
656                nameEntries.put(null, name);
657        }
658
659
660        /**
661         * Sets the client name. Corresponds to the {@code client_name} client
662         * metadata field, with an optional language tag.
663         *
664         * @param name    The client name. Must not be {@code null}.
665         * @param langTag The language tag, {@code null} if not specified.
666         */
667        public void setName(final String name, final LangTag langTag) {
668
669                nameEntries.put(langTag, name);
670        }
671
672
673        /**
674         * Gets the client application logo. Corresponds to the
675         * {@code logo_uri} client metadata field, with no language
676         * tag.
677         *
678         * @return The logo URI, {@code null} if not specified.
679         */
680        public URI getLogoURI() {
681
682                return getLogoURI(null);
683        }
684
685
686        /**
687         * Gets the client application logo. Corresponds to the
688         * {@code logo_uri} client metadata field, with an optional
689         * language tag.
690         *
691         * @param langTag The language tag, {@code null} if not specified.
692         *
693         * @return The logo URI, {@code null} if not specified.
694         */
695        public URI getLogoURI(final LangTag langTag) {
696
697                return logoURIEntries.get(langTag);
698        }
699
700
701        /**
702         * Gets the client application logo entries. Corresponds to the
703         * {@code logo_uri} client metadata field.
704         *
705         * @return The logo URI entries, empty map if none.
706         */
707        public Map<LangTag,URI> getLogoURIEntries() {
708
709                return logoURIEntries;
710        }
711
712
713        /**
714         * Sets the client application logo. Corresponds to the
715         * {@code logo_uri} client metadata field, with no language
716         * tag.
717         *
718         * @param logoURI The logo URI, {@code null} if not specified.
719         */
720        public void setLogoURI(final URI logoURI) {
721
722                logoURIEntries.put(null, logoURI);
723        }
724
725
726        /**
727         * Sets the client application logo. Corresponds to the
728         * {@code logo_uri} client metadata field, with an optional
729         * language tag.
730         *
731         * @param logoURI The logo URI. Must not be {@code null}.
732         * @param langTag The language tag, {@code null} if not specified.
733         */
734        public void setLogoURI(final URI logoURI, final LangTag langTag) {
735
736                logoURIEntries.put(langTag, logoURI);
737        }
738
739
740        /**
741         * Gets the client home page. Corresponds to the {@code client_uri}
742         * client metadata field, with no language tag.
743         *
744         * @return The client URI, {@code null} if not specified.
745         */
746        public URI getURI() {
747
748                return getURI(null);
749        }
750
751
752        /**
753         * Gets the client home page. Corresponds to the {@code client_uri}
754         * client metadata field, with an optional language tag.
755         *
756         * @param langTag The language tag, {@code null} if not specified.
757         *
758         * @return The client URI, {@code null} if not specified.
759         */
760        public URI getURI(final LangTag langTag) {
761
762                return uriEntries.get(langTag);
763        }
764
765
766        /**
767         * Gets the client home page entries. Corresponds to the
768         * {@code client_uri} client metadata field.
769         *
770         * @return The client URI entries, empty map if none.
771         */
772        public Map<LangTag,URI> getURIEntries() {
773
774                return uriEntries;
775        }
776
777
778        /**
779         * Sets the client home page. Corresponds to the {@code client_uri}
780         * client metadata field, with no language tag.
781         *
782         * @param uri The client URI, {@code null} if not specified.
783         */
784        public void setURI(final URI uri) {
785
786                uriEntries.put(null, uri);
787        }
788
789
790        /**
791         * Sets the client home page. Corresponds to the {@code client_uri}
792         * client metadata field, with an optional language tag.
793         *
794         * @param uri     The URI. Must not be {@code null}.
795         * @param langTag The language tag, {@code null} if not specified.
796         */
797        public void setURI(final URI uri, final LangTag langTag) {
798
799                uriEntries.put(langTag, uri);
800        }
801
802
803        /**
804         * Gets the client policy for use of end-user data. Corresponds to the
805         * {@code policy_uri} client metadata field, with no language
806         * tag.
807         *
808         * @return The policy URI, {@code null} if not specified.
809         */
810        public URI getPolicyURI() {
811
812                return getPolicyURI(null);
813        }
814
815
816        /**
817         * Gets the client policy for use of end-user data. Corresponds to the
818         * {@code policy_uri} client metadata field, with an optional
819         * language tag.
820         *
821         * @param langTag The language tag, {@code null} if not specified.
822         *
823         * @return The policy URI, {@code null} if not specified.
824         */
825        public URI getPolicyURI(final LangTag langTag) {
826
827                return policyURIEntries.get(langTag);
828        }
829
830
831        /**
832         * Gets the client policy entries for use of end-user data.
833         * Corresponds to the {@code policy_uri} client metadata field.
834         *
835         * @return The policy URI entries, empty map if none.
836         */
837        public Map<LangTag,URI> getPolicyURIEntries() {
838
839                return policyURIEntries;
840        }
841
842
843        /**
844         * Sets the client policy for use of end-user data. Corresponds to the
845         * {@code policy_uri} client metadata field, with no language
846         * tag.
847         *
848         * @param policyURI The policy URI, {@code null} if not specified.
849         */
850        public void setPolicyURI(final URI policyURI) {
851
852                policyURIEntries.put(null, policyURI);
853        }
854
855
856        /**
857         * Sets the client policy for use of end-user data. Corresponds to the
858         * {@code policy_uri} client metadata field, with an optional
859         * language tag.
860         *
861         * @param policyURI The policy URI. Must not be {@code null}.
862         * @param langTag   The language tag, {@code null} if not specified.
863         */
864        public void setPolicyURI(final URI policyURI, final LangTag langTag) {
865
866                policyURIEntries.put(langTag, policyURI);
867        }
868
869
870        /**
871         * Gets the client's terms of service. Corresponds to the
872         * {@code tos_uri} client metadata field, with no language
873         * tag.
874         *
875         * @return The terms of service URI, {@code null} if not specified.
876         */
877        public URI getTermsOfServiceURI() {
878
879                return getTermsOfServiceURI(null);
880        }
881
882
883        /**
884         * Gets the client's terms of service. Corresponds to the
885         * {@code tos_uri} client metadata field, with an optional
886         * language tag.
887         *
888         * @param langTag The language tag, {@code null} if not specified.
889         *
890         * @return The terms of service URI, {@code null} if not specified.
891         */
892        public URI getTermsOfServiceURI(final LangTag langTag) {
893
894                return tosURIEntries.get(langTag);
895        }
896
897
898        /**
899         * Gets the client's terms of service entries. Corresponds to the
900         * {@code tos_uri} client metadata field.
901         *
902         * @return The terms of service URI entries, empty map if none.
903         */
904        public Map<LangTag,URI> getTermsOfServiceURIEntries() {
905
906                return tosURIEntries;
907        }
908
909
910        /**
911         * Sets the client's terms of service. Corresponds to the
912         * {@code tos_uri} client metadata field, with no language
913         * tag.
914         *
915         * @param tosURI The terms of service URI, {@code null} if not
916         *               specified.
917         */
918        public void setTermsOfServiceURI(final URI tosURI) {
919
920                tosURIEntries.put(null, tosURI);
921        }
922
923
924        /**
925         * Sets the client's terms of service. Corresponds to the
926         * {@code tos_uri} client metadata field, with an optional
927         * language tag.
928         *
929         * @param tosURI  The terms of service URI. Must not be {@code null}.
930         * @param langTag The language tag, {@code null} if not specified.
931         */
932        public void setTermsOfServiceURI(final URI tosURI, final LangTag langTag) {
933
934                tosURIEntries.put(langTag, tosURI);
935        }
936
937
938        /**
939         * Gets the Token endpoint authentication method. Corresponds to the
940         * {@code token_endpoint_auth_method} client metadata field.
941         *
942         * @return The Token endpoint authentication method, {@code null} if
943         *         not specified.
944         */
945        public ClientAuthenticationMethod getTokenEndpointAuthMethod() {
946
947                return authMethod;
948        }
949
950
951        /**
952         * Sets the Token endpoint authentication method. Corresponds to the
953         * {@code token_endpoint_auth_method} client metadata field.
954         *
955         * @param authMethod The Token endpoint authentication  method,
956         *                   {@code null} if not specified.
957         */
958        public void setTokenEndpointAuthMethod(final ClientAuthenticationMethod authMethod) {
959
960                this.authMethod = authMethod;
961        }
962
963
964        /**
965         * Gets the JSON Web Signature (JWS) algorithm required for
966         * {@code private_key_jwt} and {@code client_secret_jwt}
967         * authentication at the Token endpoint. Corresponds to the
968         * {@code token_endpoint_auth_signing_alg} client metadata field.
969         *
970         * @return The JWS algorithm, {@code null} if not specified.
971         */
972        public JWSAlgorithm getTokenEndpointAuthJWSAlg() {
973
974                return authJWSAlg;
975        }
976
977
978        /**
979         * Sets the JSON Web Signature (JWS) algorithm required for
980         * {@code private_key_jwt} and {@code client_secret_jwt}
981         * authentication at the Token endpoint. Corresponds to the
982         * {@code token_endpoint_auth_signing_alg} client metadata field.
983         *
984         * @param authJWSAlg The JWS algorithm, {@code null} if not specified.
985         */
986        public void setTokenEndpointAuthJWSAlg(final JWSAlgorithm authJWSAlg) {
987
988                this.authJWSAlg = authJWSAlg;
989        }
990
991
992        /**
993         * Gets the URI for this client's JSON Web Key (JWK) set containing
994         * key(s) that are used in signing requests to the server and key(s)
995         * for encrypting responses. Corresponds to the {@code jwks_uri} client
996         * metadata field.
997         *
998         * @return The JWK set URI, {@code null} if not specified.
999         */
1000        public URI getJWKSetURI() {
1001
1002                return jwkSetURI;
1003        }
1004
1005
1006        /**
1007         * Sets the URI for this client's JSON Web Key (JWK) set containing
1008         * key(s) that are used in signing requests to the server and key(s)
1009         * for encrypting responses. Corresponds to the {@code jwks_uri} client
1010         * metadata field.
1011         *
1012         * @param jwkSetURI The JWK set URI, {@code null} if not specified.
1013         */
1014        public void setJWKSetURI(final URI jwkSetURI) {
1015
1016                this.jwkSetURI = jwkSetURI;
1017        }
1018
1019
1020        /**
1021         * Gets this client's JSON Web Key (JWK) set containing key(s) that are
1022         * used in signing requests to the server and key(s) for encrypting
1023         * responses. Intended as an alternative to {@link #getJWKSetURI} for
1024         * native clients. Corresponds to the {@code jwks} client metadata
1025         * field.
1026         *
1027         * @return The JWK set, {@code null} if not specified.
1028         */
1029        public JWKSet getJWKSet() {
1030
1031                return jwkSet;
1032        }
1033
1034
1035        /**
1036         * Sets this client's JSON Web Key (JWK) set containing key(s) that are
1037         * used in signing requests to the server and key(s) for encrypting
1038         * responses. Intended as an alternative to {@link #getJWKSetURI} for
1039         * native clients. Corresponds to the {@code jwks} client metadata
1040         * field.
1041         *
1042         * @param jwkSet The JWK set, {@code null} if not specified.
1043         */
1044        public void setJWKSet(final JWKSet jwkSet) {
1045
1046                this.jwkSet = jwkSet;
1047        }
1048        
1049        
1050        /**
1051         * Gets the pre-registered request object URIs. Corresponds to the
1052         * {@code request_uris} client metadata field.
1053         *
1054         * @return The request object URIs, {@code null} if not specified.
1055         */
1056        public Set<URI> getRequestObjectURIs() {
1057                
1058                return requestObjectURIs;
1059        }
1060        
1061        
1062        /**
1063         * Sets the pre-registered request object URIs. Corresponds to the
1064         * {@code request_uris} client metadata field.
1065         *
1066         * @param requestObjectURIs The request object URIs, {@code null} if
1067         *                          not specified.
1068         */
1069        public void setRequestObjectURIs(final Set<URI> requestObjectURIs) {
1070                
1071                this.requestObjectURIs = requestObjectURIs;
1072        }
1073        
1074        
1075        /**
1076         * Gets the JSON Web Signature (JWS) algorithm required for request
1077         * objects sent by this client. Corresponds to the
1078         * {@code request_object_signing_alg} client metadata field.
1079         *
1080         * @return The JWS algorithm, {@code null} if not specified.
1081         */
1082        public JWSAlgorithm getRequestObjectJWSAlg() {
1083                
1084                return requestObjectJWSAlg;
1085        }
1086        
1087        
1088        /**
1089         * Sets the JSON Web Signature (JWS) algorithm required for request
1090         * objects sent by this client. Corresponds to the
1091         * {@code request_object_signing_alg} client metadata field.
1092         *
1093         * @param requestObjectJWSAlg The JWS algorithm, {@code null} if not
1094         *                            specified.
1095         */
1096        public void setRequestObjectJWSAlg(final JWSAlgorithm requestObjectJWSAlg) {
1097                
1098                this.requestObjectJWSAlg = requestObjectJWSAlg;
1099        }
1100        
1101        
1102        /**
1103         * Gets the JSON Web Encryption (JWE) algorithm required for request
1104         * objects sent by this client. Corresponds to the
1105         * {@code request_object_encryption_alg} client metadata field.
1106         *
1107         * @return The JWE algorithm, {@code null} if not specified.
1108         */
1109        public JWEAlgorithm getRequestObjectJWEAlg() {
1110                
1111                return requestObjectJWEAlg;
1112        }
1113        
1114        
1115        /**
1116         * Sets the JSON Web Encryption (JWE) algorithm required for request
1117         * objects sent by this client. Corresponds to the
1118         * {@code request_object_encryption_alg} client metadata field.
1119         *
1120         * @param requestObjectJWEAlg The JWE algorithm, {@code null} if not
1121         *                            specified.
1122         */
1123        public void setRequestObjectJWEAlg(final JWEAlgorithm requestObjectJWEAlg) {
1124                
1125                this.requestObjectJWEAlg = requestObjectJWEAlg;
1126        }
1127        
1128        
1129        /**
1130         * Gets the JSON Web Encryption (JWE) method required for request
1131         * objects sent by this client. Corresponds to the
1132         * {@code request_object_encryption_enc} client metadata field.
1133         *
1134         * @return The JWE method, {@code null} if not specified.
1135         */
1136        public EncryptionMethod getRequestObjectJWEEnc() {
1137                
1138                return requestObjectJWEEnc;
1139        }
1140        
1141        
1142        /**
1143         * Sets the JSON Web Encryption (JWE) method required for request
1144         * objects sent by this client. Corresponds to the
1145         * {@code request_object_encryption_enc} client metadata field.
1146         *
1147         * @param requestObjectJWEEnc The JWE method, {@code null} if not
1148         *                            specified.
1149         */
1150        public void setRequestObjectJWEEnc(final EncryptionMethod requestObjectJWEEnc) {
1151                
1152                this.requestObjectJWEEnc = requestObjectJWEEnc;
1153        }
1154
1155
1156        /**
1157         * Gets the identifier for the OAuth 2.0 client software. Corresponds
1158         * to the {@code software_id} client metadata field.
1159         *
1160         * @return The software identifier, {@code null} if not specified.
1161         */
1162        public SoftwareID getSoftwareID() {
1163
1164                return softwareID;
1165        }
1166
1167
1168        /**
1169         * Sets the identifier for the OAuth 2.0 client software. Corresponds
1170         * to the {@code software_id} client metadata field.
1171         *
1172         * @param softwareID The software identifier, {@code null} if not
1173         *                   specified.
1174         */
1175        public void setSoftwareID(final SoftwareID softwareID) {
1176
1177                this.softwareID = softwareID;
1178        }
1179
1180
1181        /**
1182         * Gets the version identifier for the OAuth 2.0 client software.
1183         * Corresponds to the {@code software_version} client metadata field.
1184         *
1185         * @return The version identifier, {@code null} if not specified.
1186         */
1187        public SoftwareVersion getSoftwareVersion() {
1188
1189                return softwareVersion;
1190        }
1191
1192
1193        /**
1194         * Sets the version identifier for the OAuth 2.0 client software.
1195         * Corresponds to the {@code software_version} client metadata field.
1196         *
1197         * @param softwareVersion The version identifier, {@code null} if not
1198         *                        specified.
1199         */
1200        public void setSoftwareVersion(final SoftwareVersion softwareVersion) {
1201
1202                this.softwareVersion = softwareVersion;
1203        }
1204        
1205        
1206        /**
1207         * Sets the preference for TLS client certificate bound access tokens.
1208         * Corresponds to the
1209         * {@code tls_client_certificate_bound_access_tokens} client metadata
1210         * field.
1211         *
1212         * @return {@code true} indicates a preference for TLS client
1213         *         certificate bound access tokens, {@code false} if none.
1214         */
1215        public boolean getTLSClientCertificateBoundAccessTokens() {
1216                
1217                return tlsClientCertificateBoundAccessTokens;
1218        }
1219        
1220        
1221        /**
1222         * Gets the preference for TLS client certificate bound access tokens.
1223         * Corresponds to the
1224         * {@code tls_client_certificate_bound_access_tokens} client metadata
1225         * field.
1226         *
1227         * @param tlsClientCertBoundTokens {@code true} indicates a preference
1228         *                                 for TLS client certificate bound
1229         *                                 access tokens, {@code false} if
1230         *                                 none.
1231         */
1232        public void setTLSClientCertificateBoundAccessTokens(final boolean tlsClientCertBoundTokens) {
1233                
1234                tlsClientCertificateBoundAccessTokens = tlsClientCertBoundTokens;
1235        }
1236        
1237        
1238        /**
1239         * Sets the preference for TLS client certificate bound access tokens.
1240         * Corresponds to the
1241         * {@code tls_client_certificate_bound_access_tokens} client metadata
1242         * field.
1243         *
1244         * @return {@code true} indicates a preference for TLS client
1245         *         certificate bound access tokens, {@code false} if none.
1246         */
1247        @Deprecated
1248        public boolean getMutualTLSSenderConstrainedAccessTokens() {
1249                
1250                return tlsClientCertificateBoundAccessTokens;
1251        }
1252        
1253        
1254        /**
1255         * Gets the preference for TLS client certificate bound access tokens.
1256         * Corresponds to the
1257         * {@code tls_client_certificate_bound_access_tokens} client metadata
1258         * field.
1259         *
1260         * @param tlsSenderAccessTokens {@code true} indicates a preference for
1261         *                              TLS client certificate bound access
1262         *                              tokens, {@code false} if none.
1263         */
1264        @Deprecated
1265        public void setMutualTLSSenderConstrainedAccessTokens(final boolean tlsSenderAccessTokens) {
1266                
1267                tlsClientCertificateBoundAccessTokens = tlsSenderAccessTokens;
1268        }
1269        
1270        
1271        /**
1272         * Gets the expected subject distinguished name (DN) of the client
1273         * X.509 certificate in mutual TLS authentication. Corresponds to the
1274         * {@code tls_client_auth_subject_dn} client metadata field.
1275         *
1276         * @return The expected subject distinguished name (DN) of the client
1277         *         X.509 certificate, {@code null} if not specified.
1278         */
1279        public String getTLSClientAuthSubjectDN() {
1280                
1281                return tlsClientAuthSubjectDN;
1282        }
1283        
1284        
1285        /**
1286         * Sets the expected subject distinguished name (DN) of the client
1287         * X.509 certificate in mutual TLS authentication. Corresponds to the
1288         * {@code tls_client_auth_subject_dn} client metadata field.
1289         *
1290         * @param subjectDN The expected subject distinguished name (DN) of the
1291         *                  client X.509 certificate, {@code null} if not
1292         *                  specified.
1293         */
1294        public void setTLSClientAuthSubjectDN(final String subjectDN) {
1295                
1296                this.tlsClientAuthSubjectDN = subjectDN;
1297        }
1298        
1299        
1300        /**
1301         * Gets the JWS algorithm for JWT-encoded authorisation responses.
1302         * Corresponds to the {@code authorization_signed_response_alg} client
1303         * metadata field.
1304         *
1305         * @return The JWS algorithm, {@code null} if not specified.
1306         */
1307        public JWSAlgorithm getAuthorizationJWSAlg() {
1308                
1309                return authzJWSAlg;
1310        }
1311        
1312        
1313        /**
1314         * Sets the JWS algorithm for JWT-encoded authorisation responses.
1315         * Corresponds to the {@code authorization_signed_response_alg} client
1316         * metadata field.
1317         *
1318         * @param authzJWSAlg The JWS algorithm, {@code null} if not specified.
1319         *                    Must not be {@code "none"}.
1320         */
1321        public void setAuthorizationJWSAlg(final JWSAlgorithm authzJWSAlg) {
1322                
1323                if (new JWSAlgorithm("none").equals(authzJWSAlg)) {
1324                        // Prevent passing none as JWS alg
1325                        throw new IllegalArgumentException("The JWS algorithm must not be \"none\"");
1326                }
1327                
1328                this.authzJWSAlg = authzJWSAlg;
1329        }
1330        
1331        
1332        /**
1333         * Gets the JWE algorithm for JWT-encoded authorisation responses.
1334         * Corresponds to the {@code authorization_encrypted_response_alg}
1335         * client metadata field.
1336         *
1337         * @return The JWE algorithm, {@code null} if not specified.
1338         */
1339        public JWEAlgorithm getAuthorizationJWEAlg() {
1340                
1341                return authzJWEAlg;
1342        }
1343        
1344        
1345        /**
1346         * Sets the JWE algorithm for JWT-encoded authorisation responses.
1347         * Corresponds to the {@code authorization_encrypted_response_alg}
1348         * client metadata field.
1349         *
1350         * @param authzJWEAlg The JWE algorithm, {@code null} if not specified.
1351         */
1352        public void setAuthorizationJWEAlg(final JWEAlgorithm authzJWEAlg) {
1353                
1354                this.authzJWEAlg = authzJWEAlg;
1355        }
1356        
1357        
1358        /**
1359         * Sets the encryption method for JWT-encoded authorisation responses.
1360         * Corresponds to the {@code authorization_encrypted_response_enc}
1361         * client metadata field.
1362         *
1363         * @return The encryption method, {@code null} if specified.
1364         */
1365        public EncryptionMethod getAuthorizationJWEEnc() {
1366                
1367                return authzJWEEnc;
1368        }
1369        
1370        
1371        /**
1372         * Sets the encryption method for JWT-encoded authorisation responses.
1373         * Corresponds to the {@code authorization_encrypted_response_enc}
1374         * client metadata field.
1375         *
1376         * @param authzJWEEnc The encryption method, {@code null} if specified.
1377         */
1378        public void setAuthorizationJWEEnc(final EncryptionMethod authzJWEEnc) {
1379                
1380                this.authzJWEEnc = authzJWEEnc;
1381        }
1382        
1383        
1384        /**
1385         * Gets the specified custom metadata field.
1386         *
1387         * @param name The field name. Must not be {@code null}.
1388         *
1389         * @return The field value, typically serialisable to a JSON entity,
1390         *         {@code null} if none.
1391         */
1392        public Object getCustomField(final String name) {
1393
1394                return customFields.get(name);
1395        }
1396
1397
1398        /**
1399         * Gets the custom metadata fields.
1400         *
1401         * @return The custom metadata fields, as a JSON object, empty object
1402         *         if none.
1403         */
1404        public JSONObject getCustomFields() {
1405
1406                return customFields;
1407        }
1408
1409
1410        /**
1411         * Sets the specified custom metadata field.
1412         *
1413         * @param name  The field name. Must not be {@code null}.
1414         * @param value The field value. Should serialise to a JSON entity.
1415         */
1416        public void setCustomField(final String name, final Object value) {
1417
1418                customFields.put(name, value);
1419        }
1420
1421
1422        /**
1423         * Sets the custom metadata fields.
1424         *
1425         * @param customFields The custom metadata fields, as a JSON object,
1426         *                     empty object if none. Must not be {@code null}.
1427         */
1428        public void setCustomFields(final JSONObject customFields) {
1429
1430                if (customFields == null)
1431                        throw new IllegalArgumentException("The custom fields JSON object must not be null");
1432
1433                this.customFields = customFields;
1434        }
1435
1436
1437        /**
1438         * Applies the client metadata defaults where no values have been
1439         * specified.
1440         *
1441         * <ul>
1442         *     <li>The response types default to {@code ["code"]}.
1443         *     <li>The grant types default to {@code ["authorization_code"]}.
1444         *     <li>The client authentication method defaults to
1445         *         "client_secret_basic", unless the grant type is "implicit"
1446         *         only.
1447         *     <li>The encryption method for JWT-encoded authorisation
1448         *         responses defaults to {@code A128CBC-HS256} if a JWE
1449         *         algorithm is set.
1450         * </ul>
1451         */
1452        public void applyDefaults() {
1453
1454                if (responseTypes == null) {
1455                        responseTypes = new HashSet<>();
1456                        responseTypes.add(ResponseType.getDefault());
1457                }
1458
1459                if (grantTypes == null) {
1460                        grantTypes = new HashSet<>();
1461                        grantTypes.add(GrantType.AUTHORIZATION_CODE);
1462                }
1463
1464                if (authMethod == null) {
1465
1466                        if (grantTypes.contains(GrantType.IMPLICIT) && grantTypes.size() == 1) {
1467                                authMethod = ClientAuthenticationMethod.NONE;
1468                        } else {
1469                                authMethod = ClientAuthenticationMethod.getDefault();
1470                        }
1471                }
1472                
1473                if (authzJWEAlg != null && authzJWEEnc == null) {
1474                        authzJWEEnc = EncryptionMethod.A128CBC_HS256;
1475                }
1476        }
1477
1478
1479        /**
1480         * Returns the JSON object representation of this client metadata,
1481         * including any custom fields.
1482         *
1483         * @return The JSON object.
1484         */
1485        public JSONObject toJSONObject() {
1486
1487                return toJSONObject(true);
1488        }
1489
1490
1491        /**
1492         * Returns the JSON object representation of this client metadata.
1493         *
1494         * @param includeCustomFields {@code true} to include any custom
1495         *                            metadata fields, {@code false} to omit
1496         *                            them.
1497         *
1498         * @return The JSON object.
1499         */
1500        public JSONObject toJSONObject(final boolean includeCustomFields) {
1501
1502                JSONObject o;
1503
1504                if (includeCustomFields)
1505                        o = new JSONObject(customFields);
1506                else
1507                        o = new JSONObject();
1508
1509
1510                if (redirectURIs != null) {
1511
1512                        JSONArray uriList = new JSONArray();
1513
1514                        for (URI uri: redirectURIs)
1515                                uriList.add(uri.toString());
1516
1517                        o.put("redirect_uris", uriList);
1518                }
1519
1520
1521                if (scope != null)
1522                        o.put("scope", scope.toString());
1523
1524
1525                if (responseTypes != null) {
1526
1527                        JSONArray rtList = new JSONArray();
1528
1529                        for (ResponseType rt: responseTypes)
1530                                rtList.add(rt.toString());
1531
1532                        o.put("response_types", rtList);
1533                }
1534
1535
1536                if (grantTypes != null) {
1537
1538                        JSONArray grantList = new JSONArray();
1539
1540                        for (GrantType grant: grantTypes)
1541                                grantList.add(grant.toString());
1542
1543                        o.put("grant_types", grantList);
1544                }
1545
1546
1547                if (contacts != null) {
1548                        o.put("contacts", contacts);
1549                }
1550
1551
1552                if (! nameEntries.isEmpty()) {
1553
1554                        for (Map.Entry<LangTag,String> entry: nameEntries.entrySet()) {
1555
1556                                LangTag langTag = entry.getKey();
1557                                String name = entry.getValue();
1558
1559                                if (name == null)
1560                                        continue;
1561
1562                                if (langTag == null)
1563                                        o.put("client_name", entry.getValue());
1564                                else
1565                                        o.put("client_name#" + langTag, entry.getValue());
1566                        }
1567                }
1568
1569
1570                if (! logoURIEntries.isEmpty()) {
1571
1572                        for (Map.Entry<LangTag,URI> entry: logoURIEntries.entrySet()) {
1573
1574                                LangTag langTag = entry.getKey();
1575                                URI uri = entry.getValue();
1576
1577                                if (uri == null)
1578                                        continue;
1579
1580                                if (langTag == null)
1581                                        o.put("logo_uri", entry.getValue().toString());
1582                                else
1583                                        o.put("logo_uri#" + langTag, entry.getValue().toString());
1584                        }
1585                }
1586
1587
1588                if (! uriEntries.isEmpty()) {
1589
1590                        for (Map.Entry<LangTag,URI> entry: uriEntries.entrySet()) {
1591
1592                                LangTag langTag = entry.getKey();
1593                                URI uri = entry.getValue();
1594
1595                                if (uri == null)
1596                                        continue;
1597
1598                                if (langTag == null)
1599                                        o.put("client_uri", entry.getValue().toString());
1600                                else
1601                                        o.put("client_uri#" + langTag, entry.getValue().toString());
1602                        }
1603                }
1604
1605
1606                if (! policyURIEntries.isEmpty()) {
1607
1608                        for (Map.Entry<LangTag,URI> entry: policyURIEntries.entrySet()) {
1609
1610                                LangTag langTag = entry.getKey();
1611                                URI uri = entry.getValue();
1612
1613                                if (uri == null)
1614                                        continue;
1615
1616                                if (langTag == null)
1617                                        o.put("policy_uri", entry.getValue().toString());
1618                                else
1619                                        o.put("policy_uri#" + langTag, entry.getValue().toString());
1620                        }
1621                }
1622
1623
1624                if (! tosURIEntries.isEmpty()) {
1625
1626                        for (Map.Entry<LangTag,URI> entry: tosURIEntries.entrySet()) {
1627
1628                                LangTag langTag = entry.getKey();
1629                                URI uri = entry.getValue();
1630
1631                                if (uri == null)
1632                                        continue;
1633
1634                                if (langTag == null)
1635                                        o.put("tos_uri", entry.getValue().toString());
1636                                else
1637                                        o.put("tos_uri#" + langTag, entry.getValue().toString());
1638                        }
1639                }
1640
1641
1642                if (authMethod != null)
1643                        o.put("token_endpoint_auth_method", authMethod.toString());
1644
1645
1646                if (authJWSAlg != null)
1647                        o.put("token_endpoint_auth_signing_alg", authJWSAlg.getName());
1648
1649
1650                if (jwkSetURI != null)
1651                        o.put("jwks_uri", jwkSetURI.toString());
1652
1653
1654                if (jwkSet != null)
1655                        o.put("jwks", jwkSet.toJSONObject(true)); // prevent private keys from leaking
1656                
1657                
1658                if (requestObjectURIs != null) {
1659                        
1660                        JSONArray uriList = new JSONArray();
1661                        
1662                        for (URI uri: requestObjectURIs)
1663                                uriList.add(uri.toString());
1664                        
1665                        o.put("request_uris", uriList);
1666                }
1667                
1668                
1669                if (requestObjectJWSAlg != null)
1670                        o.put("request_object_signing_alg", requestObjectJWSAlg.getName());
1671                
1672                if (requestObjectJWEAlg != null)
1673                        o.put("request_object_encryption_alg", requestObjectJWEAlg.getName());
1674                
1675                if (requestObjectJWEEnc != null)
1676                        o.put("request_object_encryption_enc", requestObjectJWEEnc.getName());
1677
1678
1679                if (softwareID != null)
1680                        o.put("software_id", softwareID.getValue());
1681
1682                if (softwareVersion != null)
1683                        o.put("software_version", softwareVersion.getValue());
1684                
1685                o.put("tls_client_certificate_bound_access_tokens", tlsClientCertificateBoundAccessTokens);
1686                
1687                if (tlsClientAuthSubjectDN != null)
1688                        o.put("tls_client_auth_subject_dn", tlsClientAuthSubjectDN);
1689                
1690                if (authzJWSAlg != null) {
1691                        o.put("authorization_signed_response_alg", authzJWSAlg.getName());
1692                }
1693                
1694                if (authzJWEAlg != null) {
1695                        o.put("authorization_encrypted_response_alg", authzJWEAlg.getName());
1696                }
1697                
1698                if (authzJWEEnc != null) {
1699                        o.put("authorization_encrypted_response_enc", authzJWEEnc.getName());
1700                }
1701
1702                return o;
1703        }
1704        
1705        
1706        @Override
1707        public String toString() {
1708                return toJSONObject().toJSONString();
1709        }
1710        
1711        
1712        /**
1713         * Parses an client metadata instance from the specified JSON object.
1714         *
1715         * @param jsonObject The JSON object to parse. Must not be
1716         *                   {@code null}.
1717         *
1718         * @return The client metadata.
1719         *
1720         * @throws ParseException If the JSON object couldn't be parsed to a
1721         *                        client metadata instance.
1722         */
1723        public static ClientMetadata parse(final JSONObject jsonObject)
1724                throws ParseException {
1725
1726                // Copy JSON object, then parse
1727                return parseFromModifiableJSONObject(new JSONObject(jsonObject));
1728        }
1729
1730
1731        /**
1732         * Parses an client metadata instance from the specified JSON object.
1733         *
1734         * @param jsonObject The JSON object to parse, will be modified by
1735         *                   the parse routine. Must not be {@code null}.
1736         *
1737         * @return The client metadata.
1738         *
1739         * @throws ParseException If the JSON object couldn't be parsed to a
1740         *                        client metadata instance.
1741         */
1742        private static ClientMetadata parseFromModifiableJSONObject(final JSONObject jsonObject)
1743                throws ParseException {
1744
1745                ClientMetadata metadata = new ClientMetadata();
1746
1747                if (jsonObject.get("redirect_uris") != null) {
1748
1749                        Set<URI> redirectURIs = new LinkedHashSet<>();
1750
1751                        for (String uriString: JSONObjectUtils.getStringArray(jsonObject, "redirect_uris")) {
1752                                URI uri;
1753                                try {
1754                                        uri = new URI(uriString);
1755                                } catch (URISyntaxException e) {
1756                                        throw new ParseException("Invalid \"redirect_uris\" parameter: " + e.getMessage(), RegistrationError.INVALID_REDIRECT_URI.appendDescription(": " + e.getMessage()));
1757                                }
1758
1759                                if (uri.getFragment() != null) {
1760                                        String detail = "URI must not contain fragment";
1761                                        throw new ParseException("Invalid \"redirect_uris\" parameter: " + detail, RegistrationError.INVALID_REDIRECT_URI.appendDescription(": " + detail));
1762                                }
1763
1764                                redirectURIs.add(uri);
1765                        }
1766
1767                        metadata.setRedirectionURIs(redirectURIs);
1768                        jsonObject.remove("redirect_uris");
1769                }
1770
1771                try {
1772
1773                        if (jsonObject.get("scope") != null) {
1774                                metadata.setScope(Scope.parse(JSONObjectUtils.getString(jsonObject, "scope")));
1775                                jsonObject.remove("scope");
1776                        }
1777
1778
1779                        if (jsonObject.get("response_types") != null) {
1780
1781                                Set<ResponseType> responseTypes = new LinkedHashSet<>();
1782
1783                                for (String rt : JSONObjectUtils.getStringArray(jsonObject, "response_types")) {
1784
1785                                        responseTypes.add(ResponseType.parse(rt));
1786                                }
1787
1788                                metadata.setResponseTypes(responseTypes);
1789                                jsonObject.remove("response_types");
1790                        }
1791
1792
1793                        if (jsonObject.get("grant_types") != null) {
1794
1795                                Set<GrantType> grantTypes = new LinkedHashSet<>();
1796
1797                                for (String grant : JSONObjectUtils.getStringArray(jsonObject, "grant_types")) {
1798
1799                                        grantTypes.add(GrantType.parse(grant));
1800                                }
1801
1802                                metadata.setGrantTypes(grantTypes);
1803                                jsonObject.remove("grant_types");
1804                        }
1805
1806
1807                        if (jsonObject.get("contacts") != null) {
1808                                metadata.setEmailContacts(JSONObjectUtils.getStringList(jsonObject, "contacts"));
1809                                jsonObject.remove("contacts");
1810                        }
1811
1812
1813                        // Find lang-tagged client_name params
1814                        Map<LangTag, Object> matches = LangTagUtils.find("client_name", jsonObject);
1815
1816                        for (Map.Entry<LangTag, Object> entry : matches.entrySet()) {
1817
1818                                try {
1819                                        metadata.setName((String) entry.getValue(), entry.getKey());
1820
1821                                } catch (ClassCastException e) {
1822
1823                                        throw new ParseException("Invalid \"client_name\" (language tag) parameter");
1824                                }
1825
1826                                removeMember(jsonObject, "client_name", entry.getKey());
1827                        }
1828
1829
1830                        matches = LangTagUtils.find("logo_uri", jsonObject);
1831
1832                        for (Map.Entry<LangTag, Object> entry : matches.entrySet()) {
1833
1834                                if (entry.getValue() == null) continue;
1835                                
1836                                try {
1837                                        metadata.setLogoURI(new URI((String) entry.getValue()), entry.getKey());
1838
1839                                } catch (Exception e) {
1840
1841                                        throw new ParseException("Invalid \"logo_uri\" (language tag) parameter");
1842                                }
1843
1844                                removeMember(jsonObject, "logo_uri", entry.getKey());
1845                        }
1846
1847
1848                        matches = LangTagUtils.find("client_uri", jsonObject);
1849
1850                        for (Map.Entry<LangTag, Object> entry : matches.entrySet()) {
1851                                
1852                                if (entry.getValue() == null) continue;
1853
1854                                try {
1855                                        metadata.setURI(new URI((String) entry.getValue()), entry.getKey());
1856
1857
1858                                } catch (Exception e) {
1859
1860                                        throw new ParseException("Invalid \"client_uri\" (language tag) parameter");
1861                                }
1862
1863                                removeMember(jsonObject, "client_uri", entry.getKey());
1864                        }
1865
1866
1867                        matches = LangTagUtils.find("policy_uri", jsonObject);
1868
1869                        for (Map.Entry<LangTag, Object> entry : matches.entrySet()) {
1870                                
1871                                if (entry.getValue() == null) continue;
1872
1873                                try {
1874                                        metadata.setPolicyURI(new URI((String) entry.getValue()), entry.getKey());
1875
1876                                } catch (Exception e) {
1877
1878                                        throw new ParseException("Invalid \"policy_uri\" (language tag) parameter");
1879                                }
1880
1881                                removeMember(jsonObject, "policy_uri", entry.getKey());
1882                        }
1883
1884
1885                        matches = LangTagUtils.find("tos_uri", jsonObject);
1886
1887                        for (Map.Entry<LangTag, Object> entry : matches.entrySet()) {
1888                                
1889                                if (entry.getValue() == null) continue;
1890
1891                                try {
1892                                        metadata.setTermsOfServiceURI(new URI((String) entry.getValue()), entry.getKey());
1893
1894                                } catch (Exception e) {
1895
1896                                        throw new ParseException("Invalid \"tos_uri\" (language tag) parameter");
1897                                }
1898
1899                                removeMember(jsonObject, "tos_uri", entry.getKey());
1900                        }
1901
1902
1903                        if (jsonObject.get("token_endpoint_auth_method") != null) {
1904                                metadata.setTokenEndpointAuthMethod(ClientAuthenticationMethod.parse(
1905                                        JSONObjectUtils.getString(jsonObject, "token_endpoint_auth_method")));
1906
1907                                jsonObject.remove("token_endpoint_auth_method");
1908                        }
1909
1910
1911                        if (jsonObject.get("token_endpoint_auth_signing_alg") != null) {
1912                                metadata.setTokenEndpointAuthJWSAlg(JWSAlgorithm.parse(
1913                                        JSONObjectUtils.getString(jsonObject, "token_endpoint_auth_signing_alg")));
1914
1915                                jsonObject.remove("token_endpoint_auth_signing_alg");
1916                        }
1917
1918
1919                        if (jsonObject.get("jwks_uri") != null) {
1920                                metadata.setJWKSetURI(JSONObjectUtils.getURI(jsonObject, "jwks_uri"));
1921                                jsonObject.remove("jwks_uri");
1922                        }
1923
1924                        if (jsonObject.get("jwks") != null) {
1925
1926                                try {
1927                                        metadata.setJWKSet(JWKSet.parse(JSONObjectUtils.getJSONObject(jsonObject, "jwks")));
1928
1929                                } catch (java.text.ParseException e) {
1930                                        throw new ParseException(e.getMessage(), e);
1931                                }
1932
1933                                jsonObject.remove("jwks");
1934                        }
1935                        
1936                        if (jsonObject.get("request_uris") != null) {
1937                                
1938                                Set<URI> requestURIs = new LinkedHashSet<>();
1939                                
1940                                for (String uriString : JSONObjectUtils.getStringArray(jsonObject, "request_uris")) {
1941                                        
1942                                        try {
1943                                                requestURIs.add(new URI(uriString));
1944                                                
1945                                        } catch (URISyntaxException e) {
1946                                                
1947                                                throw new ParseException("Invalid \"request_uris\" parameter");
1948                                        }
1949                                }
1950                                
1951                                metadata.setRequestObjectURIs(requestURIs);
1952                                jsonObject.remove("request_uris");
1953                        }
1954                        
1955                        if (jsonObject.get("request_object_signing_alg") != null) {
1956                                metadata.setRequestObjectJWSAlg(JWSAlgorithm.parse(
1957                                        JSONObjectUtils.getString(jsonObject, "request_object_signing_alg")));
1958                                
1959                                jsonObject.remove("request_object_signing_alg");
1960                        }
1961                        
1962                        if (jsonObject.get("request_object_encryption_alg") != null) {
1963                                metadata.setRequestObjectJWEAlg(JWEAlgorithm.parse(
1964                                        JSONObjectUtils.getString(jsonObject, "request_object_encryption_alg")));
1965                                
1966                                jsonObject.remove("request_object_encryption_alg");
1967                        }
1968                        
1969                        if (jsonObject.get("request_object_encryption_enc") != null) {
1970                                metadata.setRequestObjectJWEEnc(EncryptionMethod.parse(
1971                                        JSONObjectUtils.getString(jsonObject, "request_object_encryption_enc")));
1972                                
1973                                jsonObject.remove("request_object_encryption_enc");
1974                        }
1975
1976                        if (jsonObject.get("software_id") != null) {
1977                                metadata.setSoftwareID(new SoftwareID(JSONObjectUtils.getString(jsonObject, "software_id")));
1978                                jsonObject.remove("software_id");
1979                        }
1980
1981                        if (jsonObject.get("software_version") != null) {
1982                                metadata.setSoftwareVersion(new SoftwareVersion(JSONObjectUtils.getString(jsonObject, "software_version")));
1983                                jsonObject.remove("software_version");
1984                        }
1985                        
1986                        if (jsonObject.get("tls_client_certificate_bound_access_tokens") != null) {
1987                                metadata.setTLSClientCertificateBoundAccessTokens(JSONObjectUtils.getBoolean(jsonObject, "tls_client_certificate_bound_access_tokens"));
1988                                jsonObject.remove("tls_client_certificate_bound_access_tokens");
1989                        }
1990                        
1991                        if (jsonObject.get("tls_client_auth_subject_dn") != null) {
1992                                metadata.setTLSClientAuthSubjectDN(JSONObjectUtils.getString(jsonObject, "tls_client_auth_subject_dn"));
1993                                jsonObject.remove("tls_client_auth_subject_dn");
1994                        }
1995                        
1996                        if (jsonObject.get("authorization_signed_response_alg") != null) {
1997                                metadata.setAuthorizationJWSAlg(JWSAlgorithm.parse(JSONObjectUtils.getString(jsonObject, "authorization_signed_response_alg")));
1998                                jsonObject.remove("authorization_signed_response_alg");
1999                        }
2000                        
2001                        if (jsonObject.get("authorization_encrypted_response_alg") != null) {
2002                                metadata.setAuthorizationJWEAlg(JWEAlgorithm.parse(JSONObjectUtils.getString(jsonObject, "authorization_encrypted_response_alg")));
2003                                jsonObject.remove("authorization_encrypted_response_alg");
2004                        }
2005                        
2006                        if (jsonObject.get("authorization_encrypted_response_enc") != null) {
2007                                metadata.setAuthorizationJWEEnc(EncryptionMethod.parse(JSONObjectUtils.getString(jsonObject, "authorization_encrypted_response_enc")));
2008                                jsonObject.remove("authorization_encrypted_response_enc");
2009                        }
2010
2011                } catch (ParseException e) {
2012                        // Insert client_client_metadata error code so that it
2013                        // can be reported back to the client if we have a
2014                        // registration event
2015                        throw new ParseException(e.getMessage(), RegistrationError.INVALID_CLIENT_METADATA.appendDescription(": " + e.getMessage()), e.getCause());
2016                }
2017
2018                // The remaining fields are custom
2019                metadata.customFields = jsonObject;
2020
2021                return metadata;
2022        }
2023
2024
2025        /**
2026         * Removes a JSON object member with the specified base name and
2027         * optional language tag.
2028         *
2029         * @param jsonObject The JSON object. Must not be {@code null}.
2030         * @param name       The base member name. Must not be {@code null}.
2031         * @param langTag    The language tag, {@code null} if none.
2032         */
2033        private static void removeMember(final JSONObject jsonObject, final String name, final LangTag langTag) {
2034
2035                if (langTag == null)
2036                        jsonObject.remove(name);
2037                else
2038                        jsonObject.remove(name + "#" + langTag);
2039        }
2040}