001package com.nimbusds.openid.connect.sdk.claims;
002
003
004import java.net.URI;
005import java.util.Collections;
006import java.util.Date;
007import java.util.HashMap;
008import java.util.LinkedHashSet;
009import java.util.Map;
010import java.util.Set;
011
012import javax.mail.internet.InternetAddress;
013
014import net.minidev.json.JSONObject;
015
016import com.nimbusds.langtag.LangTag;
017
018import com.nimbusds.jwt.JWTClaimsSet;
019
020import com.nimbusds.oauth2.sdk.ParseException;
021import com.nimbusds.oauth2.sdk.id.Subject;
022import com.nimbusds.oauth2.sdk.util.JSONObjectUtils;
023
024
025/**
026 * UserInfo claims set, serialisable to a JSON object.
027 *
028 * <p>Example UserInfo claims set:
029 *
030 * <pre>
031 * {
032 *   "sub"                : "248289761001",
033 *   "name"               : "Jane Doe",
034 *   "given_name"         : "Jane",
035 *   "family_name"        : "Doe",
036 *   "preferred_username" : "j.doe",
037 *   "email"              : "[email protected]",
038 *   "picture"            : "http://example.com/janedoe/me.jpg"
039 * }
040 * </pre>
041 *
042 * <p>Related specifications:
043 *
044 * <ul>
045 *     <li>OpenID Connect Core 1.0, section 5.1.
046 * </ul>
047 */
048public class UserInfo extends ClaimsSet {
049
050
051        /**
052         * The subject claim name.
053         */
054        public static final String SUB_CLAIM_NAME = "sub";
055
056
057        /**
058         * The name claim name.
059         */
060        public static final String NAME_CLAIM_NAME = "name";
061
062
063        /**
064         * The given name claim name.
065         */
066        public static final String GIVEN_NAME_CLAIM_NAME = "given_name";
067
068
069        /**
070         * The family name claim name.
071         */
072        public static final String FAMILY_NAME_CLAIM_NAME = "family_name";
073
074
075        /**
076         * The middle name claim name.
077         */
078        public static final String MIDDLE_NAME_CLAIM_NAME = "middle_name";
079
080
081        /**
082         * The nickname claim name.
083         */
084        public static final String NICKNAME_CLAIM_NAME = "nickname";
085
086
087        /**
088         * The preferred username claim name.
089         */
090        public static final String PREFERRED_USERNAME_CLAIM_NAME = "preferred_username";
091
092
093        /**
094         * The profile claim name.
095         */
096        public static final String PROFILE_CLAIM_NAME = "profile";
097
098
099        /**
100         * The picture claim name.
101         */
102        public static final String PICTURE_CLAIM_NAME = "picture";
103
104
105        /**
106         * The website claim name.
107         */
108        public static final String WEBSITE_CLAIM_NAME = "website";
109
110
111        /**
112         * The email claim name.
113         */
114        public static final String EMAIL_CLAIM_NAME = "email";
115
116
117        /**
118         * The email verified claim name.
119         */
120        public static final String EMAIL_VERIFIED_CLAIM_NAME = "email_verified";
121
122
123        /**
124         * The gender claim name.
125         */
126        public static final String GENDER_CLAIM_NAME = "gender";
127
128
129        /**
130         * The birth date claim name.
131         */
132        public static final String BIRTHDATE_CLAIM_NAME = "birthdate";
133
134
135        /**
136         * The zoneinfo claim name.
137         */
138        public static final String ZONEINFO_CLAIM_NAME = "zoneinfo";
139
140
141        /**
142         * The locale claim name.
143         */
144        public static final String LOCALE_CLAIM_NAME = "locale";
145
146
147        /**
148         * The phone number claim name.
149         */
150        public static final String PHONE_NUMBER_CLAIM_NAME = "phone_number";
151
152
153        /**
154         * The phone number verified claim name.
155         */
156        public static final String PHONE_NUMBER_VERIFIED_CLAIM_NAME = "phone_number_verified";
157
158
159        /**
160         * The address claim name.
161         */
162        public static final String ADDRESS_CLAIM_NAME = "address";
163
164
165        /**
166         * The updated at claim name.
167         */
168        public static final String UPDATED_AT_CLAIM_NAME = "updated_at";
169
170
171        /**
172         * The names of the standard top-level UserInfo claims.
173         */
174        private static final Set<String> stdClaimNames = new LinkedHashSet<>();
175        
176        
177        static {
178                stdClaimNames.add(SUB_CLAIM_NAME);
179                stdClaimNames.add(NAME_CLAIM_NAME);
180                stdClaimNames.add(GIVEN_NAME_CLAIM_NAME);
181                stdClaimNames.add(FAMILY_NAME_CLAIM_NAME);
182                stdClaimNames.add(MIDDLE_NAME_CLAIM_NAME);
183                stdClaimNames.add(NICKNAME_CLAIM_NAME);
184                stdClaimNames.add(PREFERRED_USERNAME_CLAIM_NAME);
185                stdClaimNames.add(PROFILE_CLAIM_NAME);
186                stdClaimNames.add(PICTURE_CLAIM_NAME);
187                stdClaimNames.add(WEBSITE_CLAIM_NAME);
188                stdClaimNames.add(EMAIL_CLAIM_NAME);
189                stdClaimNames.add(EMAIL_VERIFIED_CLAIM_NAME);
190                stdClaimNames.add(GENDER_CLAIM_NAME);
191                stdClaimNames.add(BIRTHDATE_CLAIM_NAME);
192                stdClaimNames.add(ZONEINFO_CLAIM_NAME);
193                stdClaimNames.add(LOCALE_CLAIM_NAME);
194                stdClaimNames.add(PHONE_NUMBER_CLAIM_NAME);
195                stdClaimNames.add(PHONE_NUMBER_VERIFIED_CLAIM_NAME);
196                stdClaimNames.add(ADDRESS_CLAIM_NAME);
197                stdClaimNames.add(UPDATED_AT_CLAIM_NAME);
198        }
199        
200        
201        /**
202         * Gets the names of the standard top-level UserInfo claims.
203         *
204         * @return The names of the standard top-level UserInfo claims 
205         *         (read-only set).
206         */
207        public static Set<String> getStandardClaimNames() {
208        
209                return Collections.unmodifiableSet(stdClaimNames);
210        }
211        
212        
213        /**
214         * Creates a new minimal UserInfo claims set.
215         *
216         * @param sub The subject. Must not be {@code null}.
217         */
218        public UserInfo(final Subject sub) {
219        
220                setClaim(SUB_CLAIM_NAME, sub.getValue());
221        }
222
223
224        /**
225         * Creates a new UserInfo claims set from the specified JSON object.
226         *
227         * @param jsonObject The JSON object. Must not be {@code null}.
228         *
229         * @throws IllegalArgumentException If the JSON object doesn't contain
230         *                                  a subject {@code sub} string claim.
231         */
232        public UserInfo(final JSONObject jsonObject) {
233
234                super(jsonObject);
235
236                if (getStringClaim(SUB_CLAIM_NAME) == null)
237                        throw new IllegalArgumentException("Missing or invalid \"sub\" claim");
238        }
239
240
241        /**
242         * Creates a new UserInfo claims set from the specified JSON Web Token
243         * (JWT) claims set.
244         *
245         * @param jwtClaimsSet The JWT claims set. Must not be {@code null}.
246         *
247         * @throws IllegalArgumentException If the JWT claims set doesn't
248         *                                  contain a subject {@code sub}
249         *                                  string claim.
250         */
251        public UserInfo(final JWTClaimsSet jwtClaimsSet) {
252
253                this(jwtClaimsSet.toJSONObject());
254        }
255
256
257        /**
258         * Puts all claims from the specified other UserInfo claims set.
259         *
260         * @param other The other UserInfo. Must have the same
261         *              {@link #getSubject subject}. Must not be {@code null}.
262         *
263         * @throws IllegalArgumentException If the other UserInfo claims set
264         *                                  doesn't have an identical subject.
265         */
266        public void putAll(final UserInfo other) {
267
268                Subject otherSubject = other.getSubject();
269
270                if (otherSubject == null)
271                        throw new IllegalArgumentException("The subject of the other UserInfo is missing");
272
273                if (! otherSubject.equals(getSubject()))
274                        throw new IllegalArgumentException("The subject of the other UserInfo must be identical");
275
276                putAll((ClaimsSet)other);
277        }
278        
279        
280        /**
281         * Gets the UserInfo subject. Corresponds to the {@code sub} claim.
282         *
283         * @return The subject.
284         */
285        public Subject getSubject() {
286        
287                return new Subject(getStringClaim(SUB_CLAIM_NAME));
288        }
289
290        
291        /**
292         * Gets the full name. Corresponds to the {@code name} claim, with no
293         * language tag.
294         *
295         * @return The full name, {@code null} if not specified.
296         */
297        public String getName() {
298        
299                return getStringClaim(NAME_CLAIM_NAME);
300        }
301        
302        
303        /**
304         * Gets the full name. Corresponds to the {@code name} claim, with an
305         * optional language tag.
306         *
307         * @param langTag The language tag of the entry, {@code null} to get 
308         *                the non-tagged entry.
309         *
310         * @return The full name, {@code null} if not specified.
311         */
312        public String getName(final LangTag langTag) {
313        
314                return getStringClaim(NAME_CLAIM_NAME, langTag);
315        }
316        
317        
318        /**
319         * Gets the full name entries. Correspond to the {@code name} claim.
320         *
321         * @return The full name entries, empty map if none.
322         */
323        public Map<LangTag,String> getNameEntries() {
324        
325                return getLangTaggedClaim(NAME_CLAIM_NAME, String.class);
326        }
327
328
329        /**
330         * Sets the full name. Corresponds to the {@code name} claim, with no
331         * language tag.
332         *
333         * @param name The full name. If {@code null} the claim will be 
334         *             removed.
335         */
336        public void setName(final String name) {
337        
338                setClaim(NAME_CLAIM_NAME, name);
339        }
340        
341        
342        /**
343         * Sets the full name. Corresponds to the {@code name} claim, with an
344         * optional language tag.
345         *
346         * @param name    The full name. If {@code null} the claim will be 
347         *                removed.
348         * @param langTag The language tag, {@code null} if not specified.
349         */
350        public void setName(final String name, final LangTag langTag) {
351        
352                setClaim(NAME_CLAIM_NAME, name, langTag);
353        }       
354        
355        
356        /**
357         * Gets the given or first name. Corresponds to the {@code given_name} 
358         * claim, with no language tag.
359         *
360         * @return The given or first name, {@code null} if not specified.
361         */
362        public String getGivenName() {
363        
364                return getStringClaim(GIVEN_NAME_CLAIM_NAME);
365        }
366        
367        
368        /**
369         * Gets the given or first name. Corresponds to the {@code given_name} 
370         * claim, with an optional language tag.
371         *
372         * @param langTag The language tag of the entry, {@code null} to get 
373         *                the non-tagged entry.
374         *
375         * @return The given or first name, {@code null} if not specified.
376         */
377        public String getGivenName(final LangTag langTag) {
378        
379                return getStringClaim(GIVEN_NAME_CLAIM_NAME, langTag);
380        }
381        
382        
383        /**
384         * Gets the given or first name entries. Correspond to the 
385         * {@code given_name} claim.
386         *
387         * @return The given or first name entries, empty map if none.
388         */
389        public Map<LangTag,String> getGivenNameEntries() {
390        
391                return getLangTaggedClaim(GIVEN_NAME_CLAIM_NAME, String.class);
392        }
393
394
395        /**
396         * Sets the given or first name. Corresponds to the {@code given_name} 
397         * claim, with no language tag.
398         *
399         * @param givenName The given or first name. If {@code null} the claim
400         *                  will be removed.
401         */
402        public void setGivenName(final String givenName) {
403        
404                setClaim(GIVEN_NAME_CLAIM_NAME, givenName);
405        }
406        
407        
408        /**
409         * Sets the given or first name. Corresponds to the {@code given_name}
410         * claim, with an optional language tag.
411         *
412         * @param givenName The given or first full name. If {@code null} the 
413         *                  claim will be removed.
414         * @param langTag   The language tag, {@code null} if not specified.
415         */
416        public void setGivenName(final String givenName, final LangTag langTag) {
417        
418                setClaim(GIVEN_NAME_CLAIM_NAME, givenName, langTag);
419        }
420
421        
422        /**
423         * Gets the surname or last name. Corresponds to the 
424         * {@code family_name} claim, with no language tag.
425         *
426         * @return The surname or last name, {@code null} if not specified.
427         */
428        public String getFamilyName() {
429        
430                return getStringClaim(FAMILY_NAME_CLAIM_NAME);
431        }
432        
433        
434        /**
435         * Gets the surname or last name. Corresponds to the 
436         * {@code family_name} claim, with an optional language tag.
437         *
438         * @param langTag The language tag of the entry, {@code null} to get 
439         *                the non-tagged entry.
440         *
441         * @return The surname or last name, {@code null} if not specified.
442         */
443        public String getFamilyName(final LangTag langTag) {
444        
445                return getStringClaim(FAMILY_NAME_CLAIM_NAME, langTag);
446        }
447        
448        
449        /**
450         * Gets the surname or last name entries. Correspond to the 
451         * @code family_name} claim.
452         *
453         * @return The surname or last name entries, empty map if none.
454         */
455        public Map<LangTag,String> getFamilyNameEntries() {
456        
457                return getLangTaggedClaim(FAMILY_NAME_CLAIM_NAME, String.class);
458        }
459
460
461        /**
462         * Sets the surname or last name. Corresponds to the 
463         * {@code family_name} claim, with no language tag.
464         *
465         * @param familyName The surname or last name. If {@code null} the 
466         *                   claim will be removed.
467         */
468        public void setFamilyName(final String familyName) {
469        
470                setClaim(FAMILY_NAME_CLAIM_NAME, familyName);
471        }
472        
473        
474        /**
475         * Sets the surname or last name. Corresponds to the 
476         * {@code family_name} claim, with an optional language tag.
477         *
478         * @param familyName The surname or last name. If {@code null} the 
479         *                   claim will be removed.
480         * @param langTag    The language tag, {@code null} if not specified.
481         */
482        public void setFamilyName(final String familyName, final LangTag langTag) {
483        
484                setClaim(FAMILY_NAME_CLAIM_NAME, familyName, langTag);
485        }
486
487        
488        /**
489         * Gets the middle name. Corresponds to the {@code middle_name} claim, 
490         * with no language tag.
491         *
492         * @return The middle name, {@code null} if not specified.
493         */
494        public String getMiddleName() {
495        
496                return getStringClaim(MIDDLE_NAME_CLAIM_NAME);
497        }
498        
499        
500        /**
501         * Gets the middle name. Corresponds to the {@code middle_name} claim,
502         * with an optional language tag.
503         *
504         * @param langTag The language tag of the entry, {@code null} to get 
505         *                the non-tagged entry.
506         *
507         * @return The middle name, {@code null} if not specified.
508         */
509        public String getMiddleName(final LangTag langTag) {
510        
511                return getStringClaim(MIDDLE_NAME_CLAIM_NAME, langTag);
512        }
513        
514        
515        /**
516         * Gets the middle name entries. Correspond to the {@code middle_name}
517         * claim.
518         *
519         * @return The middle name entries, empty map if none.
520         */
521        public Map<LangTag,String> getMiddleNameEntries() {
522        
523                return getLangTaggedClaim(MIDDLE_NAME_CLAIM_NAME, String.class);
524        }
525
526
527        /**
528         * Sets the middle name. Corresponds to the {@code middle_name} claim,
529         * with no language tag.
530         *
531         * @param middleName The middle name. If {@code null} the claim will be
532         *                   removed.
533         */
534        public void setMiddleName(final String middleName) {
535        
536                setClaim(MIDDLE_NAME_CLAIM_NAME, middleName);
537        }
538        
539        
540        /**
541         * Sets the middle name. Corresponds to the {@code middle_name} claim, 
542         * with an optional language tag.
543         *
544         * @param middleName The middle name. If {@code null} the claim will be
545         *                   removed.
546         * @param langTag    The language tag, {@code null} if not specified.
547         */
548        public void setMiddleName(final String middleName, final LangTag langTag) {
549        
550                setClaim(MIDDLE_NAME_CLAIM_NAME, middleName, langTag);
551        }
552        
553        
554        /**
555         * Gets the casual name. Corresponds to the {@code nickname} claim, 
556         * with no language tag.
557         *
558         * @return The casual name, {@code null} if not specified.
559         */
560        public String getNickname() {
561        
562                return getStringClaim(NICKNAME_CLAIM_NAME);
563        }
564        
565        
566        /**
567         * Gets the casual name. Corresponds to the {@code nickname} claim, 
568         * with an optional language tag.
569         *
570         * @param langTag The language tag of the entry, {@code null} to get 
571         *                the non-tagged entry.
572         *
573         * @return The casual name, {@code null} if not specified.
574         */
575        public String getNickname(final LangTag langTag) {
576        
577                return getStringClaim(NICKNAME_CLAIM_NAME, langTag);
578        }
579        
580        
581        /**
582         * Gets the casual name entries. Correspond to the {@code nickname} 
583         * claim.
584         *
585         * @return The casual name entries, empty map if none.
586         */
587        public Map<LangTag,String> getNicknameEntries() {
588        
589                return getLangTaggedClaim(NICKNAME_CLAIM_NAME, String.class);
590        }
591
592
593        /**
594         * Sets the casual name. Corresponds to the {@code nickname} claim, 
595         * with no language tag.
596         *
597         * @param nickname The casual name. If {@code null} the claim will be
598         *                 removed.
599         */
600        public void setNickname(final String nickname) {
601        
602                setClaim(NICKNAME_CLAIM_NAME, nickname);
603        }
604        
605        
606        /**
607         * Sets the casual name. Corresponds to the {@code nickname} claim, 
608         * with an optional language tag.
609         *
610         * @param nickname The casual name. If {@code null} the claim will be
611         *                 removed.
612         * @param langTag  The language tag, {@code null} if not specified.
613         */
614        public void setNickname(final String nickname, final LangTag langTag) {
615        
616                setClaim(NICKNAME_CLAIM_NAME, nickname, langTag);
617        }
618        
619        
620        /**
621         * Gets the preferred username. Corresponds to the 
622         * {@code preferred_username} claim.
623         *
624         * @return The preferred username, {@code null} if not specified.
625         */
626        public String getPreferredUsername() {
627        
628                return getStringClaim(PREFERRED_USERNAME_CLAIM_NAME);
629        }
630        
631        
632        /**
633         * Sets the preferred username. Corresponds to the 
634         * {@code preferred_username} claim.
635         *
636         * @param preferredUsername The preferred username. If {@code null} the
637         *                          claim will be removed.
638         */
639        public void setPreferredUsername(final String preferredUsername) {
640        
641                setClaim(PREFERRED_USERNAME_CLAIM_NAME, preferredUsername);
642        }
643        
644        
645        /**
646         * Gets the profile page. Corresponds to the {@code profile} claim.
647         *
648         * @return The profile page URI, {@code null} if not specified.
649         */
650        public URI getProfile() {
651        
652                return getURIClaim(PROFILE_CLAIM_NAME);
653        }
654        
655        
656        /**
657         * Sets the profile page. Corresponds to the {@code profile} claim.
658         *
659         * @param profile The profile page URI. If {@code null} the claim will
660         *                be removed.
661         */
662        public void setProfile(final URI profile) {
663        
664                setURIClaim(PROFILE_CLAIM_NAME, profile);
665        }
666        
667        
668        /**
669         * Gets the picture. Corresponds to the {@code picture} claim.
670         *
671         * @return The picture URI, {@code null} if not specified.
672         */
673        public URI getPicture() {
674        
675                return getURIClaim(PICTURE_CLAIM_NAME);
676        }
677        
678        
679        /**
680         * Sets the picture. Corresponds to the {@code picture} claim.
681         *
682         * @param picture The picture URI. If {@code null} the claim will be
683         *                removed.
684         */
685        public void setPicture(final URI picture) {
686        
687                setURIClaim(PICTURE_CLAIM_NAME, picture);
688        }
689        
690        
691        /**
692         * Gets the web page or blog. Corresponds to the {@code website} claim.
693         *
694         * @return The web page or blog URI, {@code null} if not specified.
695         */
696        public URI getWebsite() {
697        
698                return getURIClaim(WEBSITE_CLAIM_NAME);
699        }
700        
701        
702        /**
703         * Sets the web page or blog. Corresponds to the {@code website} claim.
704         *
705         * @param website The web page or blog URI. If {@code null} the claim
706         *                will be removed.
707         */
708        public void setWebsite(final URI website) {
709        
710                setURIClaim(WEBSITE_CLAIM_NAME, website);
711        }
712        
713        
714        /**
715         * Gets the preferred email address. Corresponds to the {@code email} 
716         * claim.
717         *
718         * @return The preferred email address, {@code null} if not specified.
719         */
720        public InternetAddress getEmail() {
721        
722                return getEmailClaim(EMAIL_CLAIM_NAME);
723        }
724        
725        
726        /**
727         * Sets the preferred email address. Corresponds to the {@code email}
728         * claim.
729         *
730         * @param email The preferred email address. If {@code null} the claim
731         *              will be removed.
732         */
733        public void setEmail(final InternetAddress email) {
734        
735                setEmailClaim(EMAIL_CLAIM_NAME, email);
736        }
737        
738        
739        /**
740         * Gets the email verification status. Corresponds to the 
741         * {@code email_verified} claim.
742         *
743         * @return The email verification status, {@code null} if not 
744         *         specified.
745         */
746        public Boolean getEmailVerified() {
747        
748                return getBooleanClaim(EMAIL_VERIFIED_CLAIM_NAME);
749        }
750        
751        
752        /**
753         * Sets the email verification status. Corresponds to the
754         * {@code email_verified} claim.
755         *
756         * @param emailVerified The email verification status. If {@code null} 
757         *                      the claim will be removed.
758         */
759        public void setEmailVerified(final Boolean emailVerified) {
760        
761                setClaim(EMAIL_VERIFIED_CLAIM_NAME, emailVerified);
762        }
763        
764        
765        /**
766         * Gets the gender. Corresponds to the {@code gender} claim.
767         *
768         * @return The gender, {@code null} if not specified.
769         */
770        public Gender getGender() {
771        
772                String value = getStringClaim(GENDER_CLAIM_NAME);
773                
774                if (value == null)
775                        return null;
776
777                return new Gender(value);
778        }
779        
780        
781        /**
782         * Sets the gender. Corresponds to the {@code gender} claim.
783         *
784         * @param gender The gender. If {@code null} the claim will be removed.
785         */
786        public void setGender(final Gender gender) {
787        
788                if (gender != null)
789                        setClaim(GENDER_CLAIM_NAME, gender.getValue());
790                else
791                        setClaim(GENDER_CLAIM_NAME, null);
792        }
793        
794        
795        /**
796         * Gets the date of birth. Corresponds to the {@code birthdate} claim.
797         *
798         * @return The date of birth, {@code null} if not specified.
799         */
800        public String getBirthdate() {
801        
802                return getStringClaim(BIRTHDATE_CLAIM_NAME);
803        }
804        
805        
806        /**
807         * Sets the date of birth. Corresponds to the {@code birthdate} claim.
808         *
809         * @param birthdate The date of birth. If {@code null} the claim will
810         *                  be removed.
811         */
812        public void setBirthdate(final String birthdate) {
813        
814                setClaim(BIRTHDATE_CLAIM_NAME, birthdate);
815        }
816        
817        
818        /**
819         * Gets the zoneinfo. Corresponds to the {@code zoneinfo} claim.
820         *
821         * @return The zoneinfo, {@code null} if not specified.
822         */
823        public String getZoneinfo() {
824        
825                return getStringClaim(ZONEINFO_CLAIM_NAME);
826        }
827        
828        
829        /**
830         * Sets the zoneinfo. Corresponds to the {@code zoneinfo} claim.
831         *
832         * @param zoneinfo The zoneinfo. If {@code null} the claim will be 
833         *                 removed.
834         */
835        public void setZoneinfo(final String zoneinfo) {
836        
837                setClaim(ZONEINFO_CLAIM_NAME, zoneinfo);
838        }
839        
840        
841        /**
842         * Gets the locale. Corresponds to the {@code locale} claim.
843         *
844         * @return The locale, {@code null} if not specified.
845         */
846        public String getLocale() {
847        
848                return getStringClaim(LOCALE_CLAIM_NAME);
849        }
850        
851        
852        /**
853         * Sets the locale. Corresponds to the {@code locale} claim.
854         *
855         * @param locale The locale. If {@code null} the claim will be 
856         *               removed.
857         */
858        public void setLocale(final String locale) {
859        
860                setClaim(LOCALE_CLAIM_NAME, locale);
861        }
862        
863        
864        /**
865         * Gets the preferred telephone number. Corresponds to the 
866         * {@code phone_number} claim.
867         *
868         * @return The preferred telephone number, {@code null} if not 
869         *         specified.
870         */
871        public String getPhoneNumber() {
872        
873                return getStringClaim(PHONE_NUMBER_CLAIM_NAME);
874        }
875        
876        
877        /**
878         * Sets the preferred telephone number. Corresponds to the 
879         * {@code phone_number} claim.
880         *
881         * @param phoneNumber The preferred telephone number. If {@code null} 
882         *                    the claim will be removed.
883         */
884        public void setPhoneNumber(final String phoneNumber) {
885        
886                setClaim(PHONE_NUMBER_CLAIM_NAME, phoneNumber);
887        }
888        
889        
890        /**
891         * Gets the phone number verification status. Corresponds to the 
892         * {@code phone_number_verified} claim.
893         *
894         * @return The phone number verification status, {@code null} if not 
895         *         specified.
896         */
897        public Boolean getPhoneNumberVerified() {
898        
899                return getBooleanClaim(PHONE_NUMBER_VERIFIED_CLAIM_NAME);
900        }
901        
902        
903        /**
904         * Sets the email verification status. Corresponds to the
905         * {@code phone_number_verified} claim.
906         *
907         * @param phoneNumberVerified The phone number verification status. If 
908         *                            {@code null} the claim will be removed.
909         */
910        public void setPhoneNumberVerified(final Boolean phoneNumberVerified) {
911        
912                setClaim(PHONE_NUMBER_VERIFIED_CLAIM_NAME, phoneNumberVerified);
913        }
914
915
916        /**
917         * Gets the preferred address. Corresponds to the {@code address} 
918         * claim, with no language tag.
919         *
920         * @return The preferred address, {@code null} if not specified.
921         */
922        public Address getAddress() {
923        
924                return getAddress(null);
925        }
926        
927        
928        /**
929         * Gets the preferred address. Corresponds to the {@code address} 
930         * claim, with an optional language tag.
931         *
932         * @param langTag The language tag of the entry, {@code null} to get 
933         *                the non-tagged entry.
934         *
935         * @return The preferred address, {@code null} if not specified.
936         */
937        public Address getAddress(final LangTag langTag) {
938        
939                String name;
940
941                if (langTag!= null)
942                        name = ADDRESS_CLAIM_NAME + "#" + langTag;
943                else
944                        name = ADDRESS_CLAIM_NAME;
945
946                JSONObject jsonObject = getClaim(name, JSONObject.class);
947
948                if (jsonObject == null)
949                        return null;
950
951                return new Address(jsonObject);
952        }
953        
954        
955        /**
956         * Gets the preferred address entries. Correspond to the 
957         * {@code address} claim.
958         *
959         * @return The preferred address entries, empty map if none.
960         */
961        public Map<LangTag,Address> getAddressEntries() {
962        
963                Map<LangTag,JSONObject> entriesIn = getLangTaggedClaim(ADDRESS_CLAIM_NAME, JSONObject.class);
964
965                Map<LangTag,Address> entriesOut = new HashMap<>();
966
967                for (Map.Entry<LangTag,JSONObject> en: entriesIn.entrySet())
968                        entriesOut.put(en.getKey(), new Address(en.getValue()));
969
970                return entriesOut;
971        }
972
973
974        /**
975         * Sets the preferred address. Corresponds to the {@code address} 
976         * claim, with no language tag.
977         *
978         * @param address The preferred address. If {@code null} the claim will
979         *                be removed.
980         */
981        public void setAddress(final Address address) {
982        
983                if (address != null)
984                        setClaim(ADDRESS_CLAIM_NAME, address.toJSONObject());
985                else
986                        setClaim(ADDRESS_CLAIM_NAME, null);
987        }
988        
989        
990        /**
991         * Sets the preferred address. Corresponds to the {@code address}
992         * claim, with an optional language tag.
993         *
994         * @param address  The preferred address. If {@code null} the claim 
995         *                 will be removed.
996         * @param langTag The language tag, {@code null} if not specified.
997         */
998        public void setAddress(final Address address, final LangTag langTag) {
999
1000                String key = langTag == null ? ADDRESS_CLAIM_NAME : ADDRESS_CLAIM_NAME + "#" + langTag;
1001
1002                if (address != null)
1003                        setClaim(key, address.toJSONObject());
1004                else
1005                        setClaim(key, null);
1006        }
1007        
1008        
1009        /**
1010         * Gets the time the end-user information was last updated. Corresponds 
1011         * to the {@code updated_at} claim.
1012         *
1013         * @return The time the end-user information was last updated, 
1014         *         {@code null} if not specified.
1015         */
1016        public Date getUpdatedTime() {
1017        
1018                return getDateClaim(UPDATED_AT_CLAIM_NAME);
1019        }
1020        
1021        
1022        /**
1023         * Sets the time the end-user information was last updated. Corresponds
1024         * to the {@code updated_at} claim.
1025         *
1026         * @param updatedTime The time the end-user information was last 
1027         *                    updated. If {@code null} the claim will be 
1028         *                    removed.
1029         */
1030        public void setUpdatedTime(final Date updatedTime) {
1031        
1032                setDateClaim(UPDATED_AT_CLAIM_NAME, updatedTime);
1033        }
1034
1035
1036        /**
1037         * Parses a UserInfo claims set from the specified JSON object string.
1038         *
1039         * @param json The JSON object string to parse. Must not be
1040         *             {@code null}.
1041         *
1042         * @return The UserInfo claims set.
1043         *
1044         * @throws ParseException If parsing failed.
1045         */
1046        public static UserInfo parse(final String json)
1047                throws ParseException {
1048
1049                JSONObject jsonObject = JSONObjectUtils.parseJSONObject(json);
1050
1051                try {
1052                        return new UserInfo(jsonObject);
1053
1054                } catch (IllegalArgumentException e) {
1055
1056                        throw new ParseException(e.getMessage(), e);
1057                }
1058        }
1059}