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