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