001package com.nimbusds.oauth2.sdk; 002 003 004import java.net.URI; 005import java.net.URISyntaxException; 006import java.util.HashMap; 007import java.util.Map; 008 009import net.jcip.annotations.Immutable; 010 011import net.minidev.json.JSONObject; 012 013import com.nimbusds.oauth2.sdk.id.State; 014import com.nimbusds.oauth2.sdk.token.AccessToken; 015import com.nimbusds.oauth2.sdk.http.HTTPRequest; 016import com.nimbusds.oauth2.sdk.http.HTTPResponse; 017import com.nimbusds.oauth2.sdk.util.URIUtils; 018import com.nimbusds.oauth2.sdk.util.URLUtils; 019 020 021/** 022 * Authorisation success response. Used to return an authorisation code or 023 * access token at the Authorisation endpoint. 024 * 025 * <p>Example HTTP response with code (code flow): 026 * 027 * <pre> 028 * HTTP/1.1 302 Found 029 * Location: https://client.example.com/cb?code=SplxlOBeZQQYbYS6WxSbIA&state=xyz 030 * </pre> 031 * 032 * <p>Example HTTP response with access token (implicit flow): 033 * 034 * <pre> 035 * HTTP/1.1 302 Found 036 * Location: http://example.com/cb#access_token=2YotnFZFEjr1zCsicMWpAA 037 * &state=xyz&token_type=Bearer&expires_in=3600 038 * </pre> 039 * 040 * <p>Related specifications: 041 * 042 * <ul> 043 * <li>OAuth 2.0 (RFC 6749), sections 4.1.2 and 4.2.2. 044 * <li>OAuth 2.0 Multiple Response Type Encoding Practices 1.0. 045 * <li>OAuth 2.0 Form Post Response Mode 1.0. 046 * </ul> 047 */ 048@Immutable 049public class AuthorizationSuccessResponse 050 extends AuthorizationResponse 051 implements SuccessResponse { 052 053 054 /** 055 * The authorisation code, if requested. 056 */ 057 private final AuthorizationCode code; 058 059 060 /** 061 * The access token, if requested. 062 */ 063 private final AccessToken accessToken; 064 065 066 /** 067 * Creates a new authorisation success response. 068 * 069 * @param redirectURI The base redirection URI. Must not be 070 * {@code null}. 071 * @param code The authorisation code, {@code null} if not 072 * requested. 073 * @param accessToken The access token, {@code null} if not requested. 074 * @param state The state, {@code null} if not specified. 075 * @param rm The response mode, {@code null} if not specified. 076 */ 077 public AuthorizationSuccessResponse(final URI redirectURI, 078 final AuthorizationCode code, 079 final AccessToken accessToken, 080 final State state, 081 final ResponseMode rm) { 082 083 super(redirectURI, state, rm); 084 this.code = code; 085 this.accessToken = accessToken; 086 } 087 088 089 @Override 090 public boolean indicatesSuccess() { 091 092 return true; 093 } 094 095 096 /** 097 * Returns the implied response type. 098 * 099 * @return The implied response type. 100 */ 101 public ResponseType impliedResponseType() { 102 103 ResponseType rt = new ResponseType(); 104 105 if (code != null) 106 rt.add(ResponseType.Value.CODE); 107 108 if (accessToken != null) 109 rt.add(ResponseType.Value.TOKEN); 110 111 return rt; 112 } 113 114 115 @Override 116 public ResponseMode impliedResponseMode() { 117 118 if (getResponseMode() != null) { 119 return getResponseMode(); 120 } else { 121 if (accessToken != null) { 122 return ResponseMode.FRAGMENT; 123 } else { 124 return ResponseMode.QUERY; 125 } 126 } 127 } 128 129 130 /** 131 * Gets the authorisation code. 132 * 133 * @return The authorisation code, {@code null} if not requested. 134 */ 135 public AuthorizationCode getAuthorizationCode() { 136 137 return code; 138 } 139 140 141 /** 142 * Gets the access token. 143 * 144 * @return The access token, {@code null} if not requested. 145 */ 146 public AccessToken getAccessToken() { 147 148 return accessToken; 149 } 150 151 152 @Override 153 public Map<String,String> toParameters() { 154 155 Map<String,String> params = new HashMap<>(); 156 157 if (code != null) 158 params.put("code", code.getValue()); 159 160 if (accessToken != null) { 161 162 for (Map.Entry<String,Object> entry: accessToken.toJSONObject().entrySet()) { 163 164 params.put(entry.getKey(), entry.getValue().toString()); 165 } 166 } 167 168 if (getState() != null) 169 params.put("state", getState().getValue()); 170 171 return params; 172 } 173 174 175 /** 176 * Parses an authorisation success response. 177 * 178 * @param redirectURI The base redirection URI. Must not be 179 * {@code null}. 180 * @param params The response parameters to parse. Must not be 181 * {@code null}. 182 * 183 * @return The authorisation success response. 184 * 185 * @throws ParseException If the parameters couldn't be parsed to an 186 * authorisation success response. 187 */ 188 public static AuthorizationSuccessResponse parse(final URI redirectURI, 189 final Map<String,String> params) 190 throws ParseException { 191 192 // Parse code parameter 193 194 AuthorizationCode code = null; 195 196 if (params.get("code") != null) { 197 code = new AuthorizationCode(params.get("code")); 198 } 199 200 // Parse access_token parameters 201 202 AccessToken accessToken = null; 203 204 if (params.get("access_token") != null) { 205 206 JSONObject jsonObject = new JSONObject(); 207 jsonObject.putAll(params); 208 accessToken = AccessToken.parse(jsonObject); 209 } 210 211 // Parse optional state parameter 212 State state = State.parse(params.get("state")); 213 214 return new AuthorizationSuccessResponse(redirectURI, code, accessToken, state, null); 215 } 216 217 218 /** 219 * Parses an authorisation success response. 220 * 221 * <p>Use a relative URI if the host, port and path details are not 222 * known: 223 * 224 * <pre> 225 * URI relUrl = new URI("http://?code=Qcb0Orv1...&state=af0ifjsldkj"); 226 * </pre> 227 * 228 * <p>Example URI: 229 * 230 * <pre> 231 * https://client.example.com/cb?code=SplxlOBeZQQYbYS6WxSbIA&state=xyz 232 * </pre> 233 * 234 * @param uri The URI to parse. Can be absolute or relative, with a 235 * fragment or query string containing the authorisation 236 * response parameters. Must not be {@code null}. 237 * 238 * @return The authorisation success response. 239 * 240 * @throws ParseException If the redirection URI couldn't be parsed to 241 * an authorisation success response. 242 */ 243 public static AuthorizationSuccessResponse parse(final URI uri) 244 throws ParseException { 245 246 Map<String,String> params; 247 248 if (uri.getRawFragment() != null) { 249 250 params = URLUtils.parseParameters(uri.getRawFragment()); 251 252 } else if (uri.getRawQuery() != null) { 253 254 params = URLUtils.parseParameters(uri.getRawQuery()); 255 256 } else { 257 258 throw new ParseException("Missing URI fragment or query string"); 259 } 260 261 return parse(URIUtils.getBaseURI(uri), params); 262 } 263 264 265 /** 266 * Parses an authorisation success response from the specified initial 267 * HTTP 302 redirect response generated at the authorisation endpoint. 268 * 269 * <p>Example HTTP response: 270 * 271 * <pre> 272 * HTTP/1.1 302 Found 273 * Location: https://client.example.com/cb?code=SplxlOBeZQQYbYS6WxSbIA&state=xyz 274 * </pre> 275 * 276 * @see #parse(HTTPRequest) 277 * 278 * @param httpResponse The HTTP response to parse. Must not be 279 * {@code null}. 280 * 281 * @return The authorisation success response. 282 * 283 * @throws ParseException If the HTTP response couldn't be parsed to an 284 * authorisation success response. 285 */ 286 public static AuthorizationSuccessResponse parse(final HTTPResponse httpResponse) 287 throws ParseException { 288 289 URI location = httpResponse.getLocation(); 290 291 if (location == null) { 292 throw new ParseException("Missing redirection URL / HTTP Location header"); 293 } 294 295 return parse(location); 296 } 297 298 299 /** 300 * Parses an authorisation success response from the specified HTTP 301 * request at the client redirection (callback) URI. Applies to 302 * {@code query}, {@code fragment} and {@code form_post} response 303 * modes. 304 * 305 * <p>Example HTTP request (authorisation success): 306 * 307 * <pre> 308 * GET /cb?code=SplxlOBeZQQYbYS6WxSbIA&state=xyz HTTP/1.1 309 * Host: client.example.com 310 * </pre> 311 * 312 * @see #parse(HTTPResponse) 313 * 314 * @param httpRequest The HTTP request to parse. Must not be 315 * {@code null}. 316 * 317 * @throws ParseException If the HTTP request couldn't be parsed to an 318 * authorisation success response. 319 */ 320 public static AuthorizationSuccessResponse parse(final HTTPRequest httpRequest) 321 throws ParseException { 322 323 final URI baseURI; 324 325 try { 326 baseURI = httpRequest.getURL().toURI(); 327 328 } catch (URISyntaxException e) { 329 throw new ParseException(e.getMessage(), e); 330 } 331 332 if (httpRequest.getQuery() != null) { 333 // For query string and form_post response mode 334 return parse(baseURI, URLUtils.parseParameters(httpRequest.getQuery())); 335 } else if (httpRequest.getFragment() != null) { 336 // For fragment response mode (never available in actual HTTP request from browser) 337 return parse(baseURI, URLUtils.parseParameters(httpRequest.getFragment())); 338 } else { 339 throw new ParseException("Missing URI fragment, query string or post body"); 340 } 341 } 342}