001package com.nimbusds.openid.connect.sdk;
002
003
004import java.util.Arrays;
005import java.util.Collections;
006import java.util.HashSet;
007import java.util.LinkedHashSet;
008import java.util.Set;
009
010import net.minidev.json.JSONObject;
011
012import com.nimbusds.oauth2.sdk.Scope;
013
014import com.nimbusds.openid.connect.sdk.claims.ClaimRequirement;
015
016
017/**
018 * Standard OpenID Connect scope value.
019 * 
020 * <p>Related specifications:
021 *
022 * <ul>
023 *     <li>OpenID Connect Core 1.0, section 5.2.
024 * </ul>
025 */
026public class OIDCScopeValue extends Scope.Value {
027
028
029        /**
030         * Informs the authorisation server that the client is making an OpenID 
031         * Connect request (REQUIRED). This scope value requests access to the
032         * {@code sub} claim. 
033         */
034        public static final OIDCScopeValue OPENID =
035                new OIDCScopeValue("openid", Scope.Value.Requirement.REQUIRED, new String[]{"sub"});
036        
037        
038        /**
039         * Requests that access to the end-user's default profile claims at the 
040         * UserInfo endpoint be granted by the issued access token. These 
041         * claims are: {@code name}, {@code family_name}, {@code given_name}, 
042         * {@code middle_name}, {@code nickname}, {@code preferred_username}, 
043         * {@code profile}, {@code picture}, {@code website}, {@code gender}, 
044         * {@code birthdate}, {@code zoneinfo}, {@code locale}, and 
045         * {@code updated_at}. 
046         */
047        public static final OIDCScopeValue PROFILE =
048                new OIDCScopeValue("profile", new String[]{"name",
049                                                           "family_name",
050                                                           "given_name",
051                                                           "middle_name",
052                                                           "nickname",
053                                                           "preferred_username",
054                                                           "profile",
055                                                           "picture",
056                                                           "website",
057                                                           "gender",
058                                                           "birthdate",
059                                                           "zoneinfo",
060                                                           "locale",
061                                                           "updated_at"});
062        
063        
064        /**
065         * Requests that access to the {@code email} and {@code email_verified}
066         * claims at the UserInfo endpoint be granted by the issued access 
067         * token.
068         */
069        public static final OIDCScopeValue EMAIL =
070                new OIDCScopeValue("email", new String[]{"email", "email_verified"});
071        
072        
073        /**
074         * Requests that access to {@code address} claim at the UserInfo 
075         * endpoint be granted by the issued access token. 
076         */
077        public static final OIDCScopeValue ADDRESS =
078                new OIDCScopeValue("address", new String[]{"address"});
079        
080        
081        /**
082         * Requests that access to the {@code phone_number} and
083         * {@code phone_number_verified} claims at the UserInfo endpoint be 
084         * granted by the issued access token. 
085         */
086        public static final OIDCScopeValue PHONE =
087                new OIDCScopeValue("phone", new String[]{"phone_number",
088                                                         "phone_number_verified"});
089
090
091        /**
092         * Requests that an OAuth 2.0 refresh token be issued that can be used
093         * to obtain an access token that grants access the end-user's UserInfo
094         * endpoint even when the user is not present (not logged in).
095         */
096        public static final OIDCScopeValue OFFLINE_ACCESS =
097                new OIDCScopeValue("offline_access", null);
098
099
100        /**
101         * Returns the standard OpenID Connect scope values declared in this
102         * class.
103         *
104         * @return The standard OpenID Connect scope values.
105         */
106        public static OIDCScopeValue[] values() {
107
108                return new OIDCScopeValue[]{ OPENID, PROFILE, EMAIL, ADDRESS, PHONE, OFFLINE_ACCESS };
109        }
110
111
112        /**
113         * The names of the associated claims, {@code null} if not applicable.
114         */
115        private final Set<String> claims;
116
117
118        /**
119         * Creates a new OpenID Connect scope value.
120         *
121         * @param value       The scope value. Must not be {@code null}.
122         * @param requirement The requirement. Must not be {@code null}.
123         * @param claims      The names of the associated claims, {@code null} 
124         *                    if not applicable.
125         */
126        private OIDCScopeValue(final String value, 
127                               final Scope.Value.Requirement requirement,
128                               final String[] claims) {
129        
130                super(value, requirement);
131                
132                if (claims != null)
133                        this.claims = Collections.unmodifiableSet(new LinkedHashSet<>(Arrays.asList(claims)));
134                else
135                        this.claims = null;
136        }
137
138
139        /**
140         * Creates a new OpenID Connect scope value. The requirement is set to
141         * {@link OIDCScopeValue.Requirement#OPTIONAL optional}.
142         *
143         * @param value  The scope value. Must not be {@code null}.
144         * @param claims The names of the associated claims. Must not be
145         *               {@code null}.
146         */
147        private OIDCScopeValue(final String value, 
148                               final String[] claims) {
149        
150                this(value, Scope.Value.Requirement.OPTIONAL, claims);
151        }
152
153
154        /**
155         * Returns the names of the associated claims.
156         *
157         * @return The names of the associated claims, {@code null} if not
158         *         applicable.
159         */
160        public Set<String> getClaimNames() {
161
162                return claims;
163        }
164        
165        
166        /**
167         * Gets the claims request JSON object for this OpenID Connect scope 
168         * value.
169         * 
170         * <p>See OpenID Connect Core 1.0, section 5.1.
171         * 
172         * <p>Example JSON object for "openid" scope value:
173         * 
174         * <pre>
175         * {
176         *   "sub" : { "essential" : true }
177         * }
178         * </pre>
179         * 
180         * <p>Example JSON object for "email" scope value:
181         * 
182         * <pre>
183         * {
184         *   "email"          : null,
185         *   "email_verified" : null
186         * }
187         * </pre>
188         *
189         * @return The claims request JSON object, {@code null} if not
190         *         applicable.
191         */
192        public JSONObject toClaimsRequestJSONObject() {
193
194                JSONObject req = new JSONObject();
195
196                if (claims == null)
197                        return null;
198                
199                for (String claim: claims) {
200                
201                        if (getRequirement() == Scope.Value.Requirement.REQUIRED) {
202                        
203                                // Essential (applies to OPENID - sub only)
204                                JSONObject details = new JSONObject();
205                                details.put("essential", true);
206                                req.put(claim, details);
207                                
208                        } else {
209                                // Voluntary
210                                req.put(claim, null);
211                        }
212                }
213                
214                return req;
215        }
216        
217        
218        /**
219         * Gets the claims request entries for this OpenID Connect scope value.
220         * 
221         * <p>See OpenID Connect Core 1.0, section 5.1.
222         * 
223         * @return The claims request entries, {@code null} if not applicable 
224         *         (for scope values {@link #OPENID} and 
225         *         {@link #OFFLINE_ACCESS}).
226         */
227        public Set<ClaimsRequest.Entry> toClaimsRequestEntries() {
228                
229                Set<ClaimsRequest.Entry> entries = new HashSet<>();
230                
231                if (this == OPENID || this == OFFLINE_ACCESS)
232                        return Collections.unmodifiableSet(entries);
233                
234                for (String claimName: getClaimNames())
235                        entries.add(new ClaimsRequest.Entry(claimName, ClaimRequirement.VOLUNTARY));
236                
237                return Collections.unmodifiableSet(entries);
238        }
239}