001package com.nimbusds.oauth2.sdk; 002 003 004import java.util.Collections; 005import java.util.HashMap; 006import java.util.Map; 007import java.util.Set; 008 009import net.jcip.annotations.Immutable; 010 011import net.minidev.json.JSONObject; 012 013import com.nimbusds.oauth2.sdk.token.AccessToken; 014import com.nimbusds.oauth2.sdk.token.RefreshToken; 015import com.nimbusds.oauth2.sdk.token.TokenPair; 016import com.nimbusds.oauth2.sdk.http.CommonContentTypes; 017import com.nimbusds.oauth2.sdk.http.HTTPResponse; 018 019 020/** 021 * Access token response from the Token endpoint. 022 * 023 * <p>Example HTTP response: 024 * 025 * <pre> 026 * HTTP/1.1 200 OK 027 * Content-Type: application/json;charset=UTF-8 028 * Cache-Control: no-store 029 * Pragma: no-cache 030 * 031 * { 032 * "access_token" : "2YotnFZFEjr1zCsicMWpAA", 033 * "token_type" : "example", 034 * "expires_in" : 3600, 035 * "refresh_token" : "tGzv3JOkF0XG5Qx2TlKWIA", 036 * "example_parameter" : "example_value" 037 * } 038 * </pre> 039 * 040 * <p>Related specifications: 041 * 042 * <ul> 043 * <li>OAuth 2.0 (RFC 6749), sections 4.1.4, 4.3.3, 4.4.3 and 5.1. 044 * </ul> 045 */ 046@Immutable 047public class AccessTokenResponse 048 extends TokenResponse 049 implements SuccessResponse { 050 051 052 /** 053 * The access token. 054 */ 055 private final AccessToken accessToken; 056 057 058 /** 059 * Optional refresh token. 060 */ 061 private final RefreshToken refreshToken; 062 063 064 /** 065 * Optional custom parameters. 066 */ 067 private final Map<String,Object> customParams; 068 069 070 /** 071 * Creates a new access token response. 072 * 073 * @param accessToken The access token. Must not be {@code null}. 074 * @param refreshToken Optional refresh token, {@code null} if none. 075 */ 076 public AccessTokenResponse(final AccessToken accessToken, 077 final RefreshToken refreshToken) { 078 079 this(accessToken, refreshToken, null); 080 } 081 082 083 /** 084 * Creates a new access token response. 085 * 086 * @param accessToken The access token. Must not be {@code null}. 087 * @param refreshToken Optional refresh token, {@code null} if none. 088 * @param customParams Optional custom parameters, {@code null} if 089 * none. 090 */ 091 public AccessTokenResponse(final AccessToken accessToken, 092 final RefreshToken refreshToken, 093 final Map<String,Object> customParams) { 094 095 if (accessToken == null) 096 throw new IllegalArgumentException("The access token must not be null"); 097 098 this.accessToken = accessToken; 099 100 this.refreshToken = refreshToken; 101 102 this.customParams = customParams; 103 } 104 105 106 /** 107 * Creates a new access token response. 108 * 109 * @param tokenPair The access and refresh token pair. Must not be 110 * {@code null}. 111 */ 112 public AccessTokenResponse(final TokenPair tokenPair) { 113 114 this(tokenPair, null); 115 } 116 117 118 /** 119 * Creates a new access token response. 120 * 121 * @param tokenPair The access and refresh token pair. Must not be 122 * {@code null}. 123 * @param customParams Optional custom parameters, {@code null} if 124 * none. 125 */ 126 public AccessTokenResponse(final TokenPair tokenPair, 127 final Map<String,Object> customParams) { 128 129 this(tokenPair.getAccessToken(), tokenPair.getRefreshToken(), customParams); 130 } 131 132 133 @Override 134 public boolean indicatesSuccess() { 135 136 return true; 137 } 138 139 140 /** 141 * Gets the access token. 142 * 143 * @return The access token. 144 */ 145 public AccessToken getAccessToken() { 146 147 return accessToken; 148 } 149 150 151 /** 152 * Gets the optional refresh token. 153 * 154 * @return The refresh token, {@code null} if none. 155 */ 156 public RefreshToken getRefreshToken() { 157 158 return refreshToken; 159 } 160 161 162 /** 163 * Gets the access and refresh token pair. 164 * 165 * @return The access and refresh token pair. Must not be {@code null}. 166 */ 167 public TokenPair getTokenPair() { 168 169 return new TokenPair(accessToken, refreshToken); 170 } 171 172 173 /** 174 * Gets the custom parameters. 175 * 176 * @return The custom parameters, as a unmodifiable map, empty map if 177 * none. 178 */ 179 public Map<String,Object> getCustomParams() { 180 181 if (customParams == null) 182 return Collections.emptyMap(); 183 184 return Collections.unmodifiableMap(customParams); 185 } 186 187 188 /** 189 * Returns the JSON object representing this access token response. 190 * 191 * <p>Example JSON object: 192 * 193 * <pre> 194 * { 195 * "access_token" : "SlAV32hkKG", 196 * "token_type" : "Bearer", 197 * "refresh_token": "8xLOxBtZp8", 198 * "expires_in" : 3600 199 * } 200 * </pre> 201 * 202 * @return The JSON object. 203 * 204 * @throws SerializeException If this access token response couldn't be 205 * serialised to a JSON object. 206 */ 207 public JSONObject toJSONObject() 208 throws SerializeException { 209 210 JSONObject o = accessToken.toJSONObject(); 211 212 if (refreshToken != null) 213 o.putAll(refreshToken.toJSONObject()); 214 215 if (customParams != null) 216 o.putAll(customParams); 217 218 return o; 219 } 220 221 222 @Override 223 public HTTPResponse toHTTPResponse() 224 throws SerializeException { 225 226 HTTPResponse httpResponse = new HTTPResponse(HTTPResponse.SC_OK); 227 228 httpResponse.setContentType(CommonContentTypes.APPLICATION_JSON); 229 httpResponse.setCacheControl("no-store"); 230 httpResponse.setPragma("no-cache"); 231 232 httpResponse.setContent(toJSONObject().toString()); 233 234 return httpResponse; 235 } 236 237 238 /** 239 * Parses an access token response from the specified JSON object. 240 * 241 * @param jsonObject The JSON object to parse. Must not be {@code null}. 242 * 243 * @return The access token response. 244 * 245 * @throws ParseException If the JSON object couldn't be parsed to an 246 * access token response. 247 */ 248 public static AccessTokenResponse parse(final JSONObject jsonObject) 249 throws ParseException { 250 251 AccessToken accessToken = AccessToken.parse(jsonObject); 252 253 RefreshToken refreshToken = RefreshToken.parse(jsonObject); 254 255 // Get the std param names for the access + refresh token 256 Set<String> paramNames = accessToken.getParamNames(); 257 258 if (refreshToken != null) 259 paramNames.addAll(refreshToken.getParamNames()); 260 261 // Determine the custom param names 262 Set<String> customParamNames = jsonObject.keySet(); 263 customParamNames.removeAll(paramNames); 264 265 Map<String,Object> customParams = null; 266 267 if (customParamNames.size() > 0) { 268 269 customParams = new HashMap<>(); 270 271 for (String name: customParamNames) { 272 customParams.put(name, jsonObject.get(name)); 273 } 274 } 275 276 return new AccessTokenResponse(accessToken, refreshToken, customParams); 277 } 278 279 280 /** 281 * Parses an access token response from the specified HTTP response. 282 * 283 * @param httpResponse The HTTP response. Must not be {@code null}. 284 * 285 * @return The access token response. 286 * 287 * @throws ParseException If the HTTP response couldn't be parsed to an 288 * access token response. 289 */ 290 public static AccessTokenResponse parse(final HTTPResponse httpResponse) 291 throws ParseException { 292 293 httpResponse.ensureStatusCode(HTTPResponse.SC_OK); 294 295 JSONObject jsonObject = httpResponse.getContentAsJSONObject(); 296 297 return parse(jsonObject); 298 } 299}