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