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