001package com.nimbusds.openid.connect.sdk;
002
003
004import java.util.Collection;
005import java.util.Collections;
006import java.util.Iterator;
007import java.util.HashMap;
008import java.util.HashSet;
009import java.util.LinkedList;
010import java.util.List;
011import java.util.Map;
012import java.util.Set;
013
014import net.jcip.annotations.Immutable;
015
016import org.apache.commons.lang3.tuple.ImmutablePair;
017
018import net.minidev.json.JSONObject;
019
020import com.nimbusds.langtag.LangTag;
021import com.nimbusds.langtag.LangTagException;
022
023import com.nimbusds.oauth2.sdk.Scope;
024
025import com.nimbusds.openid.connect.sdk.claims.ClaimRequirement;
026
027
028/**
029 * Specifies the individual claims to return from the UserInfo endpoint and / 
030 * or in the ID Token.
031 *
032 * <p>Related specifications: 
033 * 
034 * <ul>
035 *     <li>OpenID Connect Messages, section 2.6.
036 * </ul>
037 *
038 * @author Vladimir Dzhuvinov
039 */
040public class ClaimsRequest {
041
042
043        /**
044         * Individual claim request. This class is immutable.
045         *
046         * <p>Related specifications: 
047         * 
048         * <ul>
049         *     <li>OpenID Connect Messages, section 2.6.1.
050         * </ul>
051         */
052        @Immutable
053        public static class Entry {
054
055
056                /**
057                 * The claim name.
058                 */
059                private final String claimName;
060
061
062                /**
063                 * The claim requirement.
064                 */
065                private final ClaimRequirement requirement;
066
067
068                /**
069                 * Optional language tag.
070                 */
071                private final LangTag langTag;
072
073
074                /**
075                 * Optional claim value.
076                 */
077                private final String value;
078
079
080                /**
081                 * Optional claim values.
082                 */
083                private final List<String> values;
084
085
086                /**
087                 * Creates a new individual claim request. The claim 
088                 * requirement is set to voluntary (the default) and no 
089                 * expected value(s) are specified.
090                 *
091                 * @param claimName   The claim name. Must not be {@code null}.
092                 * @param langTag     Optional language tag for the claim.
093                 */
094                public Entry(final String claimName, final LangTag langTag) {
095
096                        this(claimName, ClaimRequirement.VOLUNTARY, langTag, (String)null, null);
097                }
098                
099                
100                /**
101                 * Creates a new individual claim request.
102                 *
103                 * @param claimName   The claim name. Must not be {@code null}.
104                 * @param requirement The claim requirement. Must not be 
105                 *                    {@code null}.
106                 */
107                public Entry(final String claimName, final ClaimRequirement requirement) {
108
109                        this(claimName, requirement, null, null, null);
110                }
111
112
113                /**
114                 * Creates a new individual claim request.
115                 *
116                 * @param claimName   The claim name. Must not be {@code null}.
117                 * @param requirement The claim requirement. Must not be 
118                 *                    {@code null}.
119                 * @param langTag     Optional language tag for the claim.
120                 * @param value       Optional expected value for the claim.
121                 */
122                public Entry(final String claimName, final ClaimRequirement requirement, 
123                             final LangTag langTag, final String value) {
124
125                        this(claimName, requirement, langTag, value, null);
126                }
127
128
129                /**
130                 * Creates a new individual claim request.
131                 *
132                 * @param claimName   The claim name. Must not be {@code null}.
133                 * @param requirement The claim requirement. Must not be 
134                 *                    {@code null}.
135                 * @param langTag     Optional language tag for the claim.
136                 * @param values      Optional expected values for the claim.
137                 */
138                public Entry(final String claimName, final ClaimRequirement requirement, 
139                             final LangTag langTag, final List<String> values) {
140
141                        this(claimName, requirement, langTag, null, values);
142                }
143
144
145                /**
146                 * Creates a new individual claim request. This constructor is
147                 * to be used privately. Ensures that {@code value} and 
148                 * {@code values} are not simultaneously specified.
149                 *
150                 * @param claimName   The claim name. Must not be {@code null}.
151                 * @param requirement The claim requirement. Must not be 
152                 *                    {@code null}.
153                 * @param langTag     Optional language tag for the claim.
154                 * @param value       Optional expected value for the claim. If
155                 *                    set, then the {@code values} parameter
156                 *                    must not be set.
157                 * @param values      Optional expected values for the claim. 
158                 *                    If set, then the {@code value} parameter
159                 *                    must not be set.
160                 */
161                private Entry(final String claimName, final ClaimRequirement requirement, final LangTag langTag, 
162                              final String value, final List<String> values) {
163
164                        if (claimName == null)
165                                throw new IllegalArgumentException("The claim name must not be null");
166
167                        this.claimName = claimName;
168
169
170                        if (requirement == null)
171                                throw new IllegalArgumentException("The claim requirement must not be null");
172
173                        this.requirement = requirement;
174
175
176                        this.langTag = langTag;
177
178
179                        if (value != null && values == null) {
180
181                                this.value = value;
182                                this.values = null;
183
184                        } else if (value == null && values != null) {
185
186                                this.value = null;
187                                this.values = values;
188
189                        } else if (value == null && values == null) {
190
191                                this.value = null;
192                                this.values = null;
193
194                        } else {
195
196                                throw new IllegalArgumentException("Either value or values must be specified, but not both");
197                        }
198                }
199
200
201                /**
202                 * Gets the claim name.
203                 *
204                 * @return The claim name.
205                 */
206                public String getClaimName() {
207
208                        return claimName;
209                }
210                
211                
212                /**
213                 * Gets the claim name, optionally with the language tag
214                 * appended.
215                 * 
216                 * <p>Example with language tag:
217                 * 
218                 * <pre>
219                 * name#de-DE
220                 * </pre>
221                 * 
222                 * @param withLangTag If {@code true} the language tag will be
223                 *                    appended to the name (if any), else not.
224                 * 
225                 * @return The claim name, with optionally appended language
226                 *         tag.
227                 */
228                public String getClaimName(final boolean withLangTag) {
229                        
230                        if (withLangTag && langTag != null)
231                                return claimName + "#" + langTag.toString();
232                        else
233                                return claimName;
234                }
235
236
237                /**
238                 * Gets the claim requirement.
239                 *
240                 * @return The claim requirement.
241                 */
242                public ClaimRequirement getClaimRequirement() {
243
244                        return requirement;
245                }
246
247
248                /**
249                 * Gets the optional language tag for the claim.
250                 *
251                 * @return The language tag, {@code null} if not specified.
252                 */
253                public LangTag getLangTag() {
254
255                        return langTag;
256                }
257
258
259                /**
260                 * Gets the optional value for the claim.
261                 *
262                 * @return The value, {@code null} if not specified.
263                 */
264                public String getValue() {
265
266                        return value;
267                }
268
269
270                /**
271                 * Gets the optional values for the claim.
272                 *
273                 * @return The values, {@code null} if not specified.
274                 */
275                public List<String> getValues() {
276
277                        return values;
278                }
279
280
281                /**
282                 * Returns the JSON object representation of the specified 
283                 * collection of individual claim requests.
284                 *
285                 * <p>Example:
286                 *
287                 * <pre>
288                 * {
289                 *   "given_name": {"essential": true},
290                 *   "nickname": null,
291                 *   "email": {"essential": true},
292                 *   "email_verified": {"essential": true},
293                 *   "picture": null,
294                 *   "http://example.info/claims/groups": null
295                 * }  
296                 * </pre>
297                 *
298                 * @param entries The entries to serialise. Must not be 
299                 *                {@code null}.
300                 *
301                 * @return The corresponding JSON object, empty if no claims 
302                 *         were found.
303                 */
304                public static JSONObject toJSONObject(final Collection<Entry> entries) {
305
306                        JSONObject o = new JSONObject();
307
308                        for (Entry entry: entries) {
309
310                                // Compose the optional value
311                                JSONObject entrySpec = null;
312
313                                if (entry.getValue() != null) {
314
315                                        entrySpec = new JSONObject();
316                                        entrySpec.put("value", entry.getValue());
317                                }
318
319                                if (entry.getValues() != null) {
320
321                                        // Either "value" or "values", or none
322                                        // may be defined
323                                        entrySpec = new JSONObject();
324                                        entrySpec.put("values", entry.getValues());
325                                }
326
327                                if (entry.getClaimRequirement().equals(ClaimRequirement.ESSENTIAL)) {
328
329                                        if (entrySpec == null)
330                                                entrySpec = new JSONObject();
331
332                                        entrySpec.put("essential", true);
333                                }
334
335                                o.put(entry.getClaimName(true), entrySpec);
336                        }
337
338                        return o;
339                }
340
341
342                /**
343                 * Parses a collection of individual claim requests from the
344                 * specified JSON object. Request entries that are not 
345                 * understood are silently ignored.
346                 */
347                public static Collection<Entry> parseEntries(final JSONObject jsonObject) {
348
349                        Collection<Entry> entries = new LinkedList<Entry>();
350
351                        if (jsonObject.isEmpty())
352                                return entries;
353
354                        for (Map.Entry<String,Object> member: jsonObject.entrySet()) {
355
356                                // Process the key
357                                String claimNameWithOptLangTag = member.getKey();
358
359                                String claimName;
360                                LangTag langTag = null;
361
362                                if (claimNameWithOptLangTag.contains("#")) {
363
364                                        String[] parts = claimNameWithOptLangTag.split("#", 2);
365
366                                        claimName = parts[0];
367
368                                        try {
369                                                langTag = LangTag.parse(parts[1]);
370
371                                        } catch (LangTagException e) {
372
373                                                // Ignore and continue
374                                                continue;
375                                        }
376
377                                } else {
378                                        claimName = claimNameWithOptLangTag;
379                                }
380
381                                // Parse the optional value
382                                if (member.getValue() == null) {
383
384                                        // Voluntary claim with no value(s)
385                                        entries.add(new Entry(claimName, langTag));
386                                        continue;
387                                }
388
389                                try {
390                                        JSONObject entrySpec = (JSONObject)member.getValue();
391
392                                        ClaimRequirement requirement = ClaimRequirement.VOLUNTARY;
393
394                                        if (entrySpec.containsKey("essential")) {
395
396                                                boolean isEssential = (Boolean)entrySpec.get("essential");
397
398                                                if (isEssential)
399                                                        requirement = ClaimRequirement.ESSENTIAL;
400                                        }
401
402
403                                        if (entrySpec.containsKey("value")) {
404
405                                                String expectedValue = (String)entrySpec.get("value");
406
407                                                entries.add(new Entry(claimName, requirement, langTag, expectedValue));
408
409                                        } else if (entrySpec.containsKey("values")) {
410
411
412                                                List<String> expectedValues = new LinkedList<String>();
413
414                                                for (Object v: (List)entrySpec.get("values")) {
415
416                                                        expectedValues.add((String)v);
417                                                }
418
419                                                entries.add(new Entry(claimName, requirement, langTag, expectedValues));
420                                        }
421
422                                } catch (Exception e) {
423
424                                        // Ignore and continue
425                                        continue;
426                                }
427                        }
428
429                        return entries;
430                }
431        }
432
433
434        /**
435         * The requested ID token claims, keyed by claim name and language tag.
436         */
437        private final Map<ImmutablePair<String,LangTag>,Entry> idTokenClaims =
438                new HashMap<ImmutablePair<String,LangTag>,Entry>();
439
440
441        /**
442         * The requested UserInfo claims, keyed by claim name and language tag.
443         */
444        private final Map<ImmutablePair<String,LangTag>,Entry> userInfoClaims =
445                new HashMap<ImmutablePair<String,LangTag>,Entry>(); 
446        
447
448        /**
449         * Creates a new empty claims request.
450         */
451        public ClaimsRequest() {
452
453                // Nothing to initialise
454        }
455        
456        
457        /**
458         * Adds the entries from the specified other claims request.
459         * 
460         * @param other The other claims request. If {@code null} no claims
461         *              request entries will be added to this claims request.
462         */
463        public void add(final ClaimsRequest other) {
464                
465                if (other == null)
466                        return;
467                
468                idTokenClaims.putAll(other.idTokenClaims);
469                userInfoClaims.putAll(other.userInfoClaims);
470        }
471
472
473        /**
474         * Adds the specified ID token claim to the request. It is marked as
475         * voluntary and no language tag and value(s) are associated with it.
476         *
477         * @param claimName The claim name. Must not be {@code null}.
478         */
479        public void addIDTokenClaim(final String claimName) {
480
481                addIDTokenClaim(claimName, ClaimRequirement.VOLUNTARY);
482        }
483
484
485        /**
486         * Adds the specified ID token claim to the request. No language tag 
487         * and value(s) are associated with it.
488         *
489         * @param claimName   The claim name. Must not be {@code null}.
490         * @param requirement The claim requirement. Must not be {@code null}.
491         */
492        public void addIDTokenClaim(final String claimName, final ClaimRequirement requirement) {
493
494                addIDTokenClaim(claimName, requirement, null);
495        }
496
497
498        /**
499         * Adds the specified ID token claim to the request. No value(s) are 
500         * associated with it.
501         *
502         * @param claimName   The claim name. Must not be {@code null}.
503         * @param requirement The claim requirement. Must not be {@code null}.
504         * @param langTag     The associated language tag, {@code null} if not
505         *                    specified.
506         */
507        public void addIDTokenClaim(final String claimName, final ClaimRequirement requirement, 
508                                    final LangTag langTag) {
509
510
511                addIDTokenClaim(claimName, requirement, langTag, (String)null);
512        }
513
514
515        /**
516         * Adds the specified ID token claim to the request.
517         *
518         * @param claimName   The claim name. Must not be {@code null}.
519         * @param requirement The claim requirement. Must not be {@code null}.
520         * @param langTag     The associated language tag, {@code null} if not
521         *                    specified.
522         * @param value       The expected claim value, {@code null} if not
523         *                    specified.
524         */
525        public void addIDTokenClaim(final String claimName, final ClaimRequirement requirement, 
526                                    final LangTag langTag, final String value) {
527
528                addIDTokenClaim(new Entry(claimName, requirement, langTag, value));
529        }
530
531
532        /**
533         * Adds the specified ID token claim to the request.
534         *
535         * @param claimName   The claim name. Must not be {@code null}.
536         * @param requirement The claim requirement. Must not be {@code null}.
537         * @param langTag     The associated language tag, {@code null} if not
538         *                    specified.
539         * @param values      The expected claim values, {@code null} if not
540         *                    specified.
541         */
542        public void addIDTokenClaim(final String claimName, final ClaimRequirement requirement, 
543                                    final LangTag langTag, final List<String> values) {
544
545                addIDTokenClaim(new Entry(claimName, requirement, langTag, values));
546        }
547
548
549        /**
550         * Adds the specified ID token claim to the request.
551         *
552         * @param entry The individual ID token claim request. Must not be
553         *              {@code null}.
554         */
555        public void addIDTokenClaim(final Entry entry) {
556
557                ImmutablePair<String,LangTag> key = 
558                        new ImmutablePair<String,LangTag>(entry.getClaimName(), entry.getLangTag());
559
560                idTokenClaims.put(key, entry);
561        }
562
563
564        /**
565         * Gets the requested ID token claims.
566         *
567         * @return The ID token claims, as an unmodifiable collection, empty 
568         *         set if none.
569         */
570        public Collection<Entry> getIDTokenClaims() {
571
572                return Collections.unmodifiableCollection(idTokenClaims.values());
573        }
574        
575        
576        /**
577         * Gets the names of the requested ID token claim names.
578         * 
579         * @param withLangTag If {@code true} the language tags, if any, will 
580         *                    be appended to the names, else not.
581         * 
582         * 
583         * @return The ID token claim names, as an unmodifiable set, empty set
584         *         if none.
585         */
586        public Set<String> getIDTokenClaimNames(final boolean withLangTag) {
587                
588                Set<String> names = new HashSet<String>();
589                
590                for (Entry en: idTokenClaims.values())
591                        names.add(en.getClaimName(withLangTag));
592                
593                return Collections.unmodifiableSet(names);
594        }
595
596
597        /**
598         * Removes the specified ID token claim from the request.
599         *
600         * @param claimName The claim name. Must not be {@code null}.
601         * @param langTag   The associated language tag, {@code null} if none.
602         *
603         * @return The removed ID token claim, {@code null} if not found.
604         */
605        public Entry removeIDTokenClaim(final String claimName, final LangTag langTag) {
606
607                ImmutablePair<String,LangTag> key = 
608                        new ImmutablePair<String,LangTag>(claimName, langTag);
609
610                return idTokenClaims.remove(key);
611        }
612
613
614        /**
615         * Removes the specified ID token claims from the request, in all
616         * existing language tag variations.
617         *
618         * @param claimName The claim name. Must not be {@code null}.
619         *
620         * @return The removed ID token claims, as an unmodifiable collection,
621         *         empty set if none were found.
622         */
623        public Collection<Entry> removeIDTokenClaims(final String claimName) {
624
625                Collection<Entry> removedClaims = new LinkedList<Entry>();
626
627                Iterator<Map.Entry<ImmutablePair<String,LangTag>,Entry>> it = idTokenClaims.entrySet().iterator();
628
629                while (it.hasNext()) {
630
631                        Map.Entry<ImmutablePair<String,LangTag>,Entry> reqEntry = it.next();
632
633                        if (reqEntry.getKey().getLeft().equals(claimName)) {
634
635                                removedClaims.add(reqEntry.getValue());
636
637                                it.remove();
638                        }
639                }
640
641                return Collections.unmodifiableCollection(removedClaims);
642        }
643
644
645        /**
646         * Adds the specified UserInfo claim to the request. It is marked as
647         * voluntary and no language tag and value(s) are associated with it.
648         *
649         * @param claimName The claim name. Must not be {@code null}.
650         */
651        public void addUserInfoClaim(final String claimName) {
652
653                addUserInfoClaim(claimName, ClaimRequirement.VOLUNTARY);
654        }
655
656
657        /**
658         * Adds the specified UserInfo claim to the request. No language tag 
659         * and value(s) are associated with it.
660         *
661         * @param claimName   The claim name. Must not be {@code null}.
662         * @param requirement The claim requirement. Must not be {@code null}.
663         */
664        public void addUserInfoClaim(final String claimName, final ClaimRequirement requirement) {
665
666                addUserInfoClaim(claimName, requirement, null);
667        }
668
669
670        /**
671         * Adds the specified UserInfo claim to the request. No value(s) are 
672         * associated with it.
673         *
674         * @param claimName   The claim name. Must not be {@code null}.
675         * @param requirement The claim requirement. Must not be {@code null}.
676         * @param langTag     The associated language tag, {@code null} if not
677         *                    specified.
678         */
679        public void addUserInfoClaim(final String claimName, final ClaimRequirement requirement, 
680                                     final LangTag langTag) {
681
682
683                addUserInfoClaim(claimName, requirement, langTag, (String)null);
684        }
685
686
687        /**
688         * Adds the specified UserInfo claim to the request.
689         *
690         * @param claimName   The claim name. Must not be {@code null}.
691         * @param requirement The claim requirement. Must not be {@code null}.
692         * @param langTag     The associated language tag, {@code null} if not
693         *                    specified.
694         * @param value       The expected claim value, {@code null} if not
695         *                    specified.
696         */
697        public void addUserInfoClaim(final String claimName, final ClaimRequirement requirement, 
698                                     final LangTag langTag, final String value) {
699
700                addUserInfoClaim(new Entry(claimName, requirement, langTag, value));
701        }
702
703
704        /**
705         * Adds the specified UserInfo claim to the request.
706         *
707         * @param claimName   The claim name. Must not be {@code null}.
708         * @param requirement The claim requirement. Must not be {@code null}.
709         * @param langTag     The associated language tag, {@code null} if not
710         *                    specified.
711         * @param values      The expected claim values, {@code null} if not
712         *                    specified.
713         */
714        public void addUserInfoClaim(final String claimName, final ClaimRequirement requirement, 
715                                     final LangTag langTag, final List<String> values) {
716
717                addUserInfoClaim(new Entry(claimName, requirement, langTag, values));
718        }
719
720
721        /**
722         * Adds the specified UserInfo claim to the request.
723         *
724         * @param entry The individual UserInfo claim request. Must not be
725         *              {@code null}.
726         */
727        public void addUserInfoClaim(final Entry entry) {
728
729                ImmutablePair<String,LangTag> key = 
730                        new ImmutablePair<String,LangTag>(entry.getClaimName(), entry.getLangTag());
731
732                userInfoClaims.put(key, entry);
733        }
734
735
736        /**
737         * Gets the requested UserInfo claims.
738         *
739         * @return The UserInfo claims, as an unmodifiable collection, empty 
740         *         set if none.
741         */
742        public Collection<Entry> getUserInfoClaims() {
743
744                return Collections.unmodifiableCollection(userInfoClaims.values());
745        }
746        
747        
748        /**
749         * Gets the names of the requested UserInfo claim names.
750         * 
751         * @param withLangTag If {@code true} the language tags, if any, will 
752         *                    be appended to the names, else not.
753         * 
754         * 
755         * @return The UserInfo claim names, as an unmodifiable set, empty set
756         *         if none.
757         */
758        public Set<String> getUserInfoClaimNames(final boolean withLangTag) {
759                
760                Set<String> names = new HashSet<String>();
761                
762                for (Entry en: userInfoClaims.values())
763                        names.add(en.getClaimName(withLangTag));
764                
765                return Collections.unmodifiableSet(names);
766        }
767
768
769        /**
770         * Removes the specified UserInfo claim from the request.
771         *
772         * @param claimName The claim name. Must not be {@code null}.
773         * @param langTag   The associated language tag, {@code null} if none.
774         *
775         * @return The removed UserInfo claim, {@code null} if not found.
776         */
777        public Entry removeUserInfoClaim(final String claimName, final LangTag langTag) {
778
779                ImmutablePair<String,LangTag> key = 
780                        new ImmutablePair<String,LangTag>(claimName, langTag);
781
782                return userInfoClaims.remove(key);
783        }
784
785
786        /**
787         * Removes the specified UserInfo claims from the request, in all
788         * existing language tag variations.
789         *
790         * @param claimName The claim name. Must not be {@code null}.
791         *
792         * @return The removed UserInfo claims, as an unmodifiable collection,
793         *         empty set if none were found.
794         */
795        public Collection<Entry> removeUserInfoClaims(final String claimName) {
796
797                Collection<Entry> removedClaims = new LinkedList<Entry>();
798
799                Iterator<Map.Entry<ImmutablePair<String,LangTag>,Entry>> it = userInfoClaims.entrySet().iterator();
800
801                while (it.hasNext()) {
802
803                        Map.Entry<ImmutablePair<String,LangTag>,Entry> reqEntry = it.next();
804
805                        if (reqEntry.getKey().getLeft().equals(claimName)) {
806
807                                removedClaims.add(reqEntry.getValue());
808
809                                it.remove();
810                        }
811                }
812
813                return Collections.unmodifiableCollection(removedClaims);
814        }
815
816
817        /**
818         * Returns the JSON object representation of this claims request.
819         *
820         * <p>Example:
821         *
822         * <pre>
823         * {
824         *   "userinfo":
825         *    {
826         *     "given_name": {"essential": true},
827         *     "nickname": null,
828         *     "email": {"essential": true},
829         *     "email_verified": {"essential": true},
830         *     "picture": null,
831         *     "http://example.info/claims/groups": null
832         *    },
833         *   "id_token":
834         *    {
835         *     "auth_time": {"essential": true},
836         *     "acr": {"values": ["urn:mace:incommon:iap:silver"] }
837         *    }
838         * }
839         * </pre>
840         *
841         * @return The corresponding JSON object, empty if no ID token and 
842         *         UserInfo claims are specified.
843         */
844        public JSONObject toJSONObject() {
845
846                JSONObject o = new JSONObject();
847
848                Collection<Entry> idTokenEntries = getIDTokenClaims();
849
850                if (! idTokenEntries.isEmpty()) {
851
852                        o.put("id_token", Entry.toJSONObject(idTokenEntries));
853                }
854
855                Collection<Entry> userInfoEntries = getUserInfoClaims();
856
857                if (! userInfoEntries.isEmpty()) {
858
859                        o.put("user_info", Entry.toJSONObject(userInfoEntries));
860                }
861
862                return o;
863        }
864
865
866        @Override
867        public String toString() {
868
869                return toJSONObject().toString();
870        }
871        
872        
873        /**
874         * Gets the claims request for the specified scope. The scope values
875         * that are {@link OIDCScopeValue standard OpenID Connect scope values}
876         * are resolved to their respective individual claims requests, any
877         * other scope values are ignored.
878         * 
879         * @param scope The scope. Must not be {@code null}.
880         * 
881         * @return The claims request.
882         */
883        public static ClaimsRequest forScope(final Scope scope) {
884                
885                ClaimsRequest claimsRequest = new ClaimsRequest();
886                
887                for (Scope.Value value: scope) {
888                        
889                        Set<ClaimsRequest.Entry> entries;
890                        
891                        if (value.equals(OIDCScopeValue.PROFILE)) {
892                                
893                                entries = OIDCScopeValue.PROFILE.toClaimsRequestEntries();
894                                
895                        } else if (value.equals(OIDCScopeValue.EMAIL)) {
896                                
897                                entries = OIDCScopeValue.EMAIL.toClaimsRequestEntries();
898                                
899                        } else if (value.equals(OIDCScopeValue.PHONE)) {
900                                
901                                entries = OIDCScopeValue.PHONE.toClaimsRequestEntries();
902                                
903                        } else if (value.equals(OIDCScopeValue.ADDRESS)) {
904                                
905                                entries = OIDCScopeValue.ADDRESS.toClaimsRequestEntries();
906                                
907                        } else {
908                                
909                                continue; // skip
910                        }
911                        
912                        for (ClaimsRequest.Entry en: entries)
913                                claimsRequest.addUserInfoClaim(en);
914                }
915                
916                return claimsRequest;
917        }
918
919
920        /**
921         * Parses a claims request from the specified JSON object 
922         * representation. Unexpected members in the JSON object are silently
923         * ignored.
924         *
925         * @param jsonObject The JSON object to parse. Must not be 
926         *                   {@code null}.
927         *
928         * @return The claims request.
929         */
930        public static ClaimsRequest parse(final JSONObject jsonObject) {
931
932                ClaimsRequest claimsRequest = new ClaimsRequest();
933
934                try {
935                        if (jsonObject.containsKey("id_token")) {
936
937                                JSONObject idTokenObject = (JSONObject)jsonObject.get("id_token");
938
939                                Collection<Entry> idTokenClaims = Entry.parseEntries(idTokenObject);
940
941                                for (Entry entry: idTokenClaims)
942                                        claimsRequest.addIDTokenClaim(entry);
943                        }
944
945
946                        if (jsonObject.containsKey("user_info")) {
947
948                                JSONObject userInfoObject = (JSONObject)jsonObject.get("user_info");
949
950                                Collection<Entry> userInfoClaims = Entry.parseEntries(userInfoObject);
951
952                                for (Entry entry: userInfoClaims)
953                                        claimsRequest.addUserInfoClaim(entry);
954                        }
955
956                } catch (Exception e) {
957
958                        // Ignore
959                }
960
961                return claimsRequest;
962        }
963
964}