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