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}