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 throws SerializeException { 155 156 Map<String,String> params = new HashMap<>(); 157 158 if (code != null) 159 params.put("code", code.getValue()); 160 161 if (accessToken != null) { 162 163 for (Map.Entry<String,Object> entry: accessToken.toJSONObject().entrySet()) { 164 165 params.put(entry.getKey(), entry.getValue().toString()); 166 } 167 } 168 169 if (getState() != null) 170 params.put("state", getState().getValue()); 171 172 return params; 173 } 174 175 176 /** 177 * Parses an authorisation success response. 178 * 179 * @param redirectURI The base redirection URI. Must not be 180 * {@code null}. 181 * @param params The response parameters to parse. Must not be 182 * {@code null}. 183 * 184 * @return The authorisation success response. 185 * 186 * @throws ParseException If the parameters couldn't be parsed to an 187 * authorisation success response. 188 */ 189 public static AuthorizationSuccessResponse parse(final URI redirectURI, 190 final Map<String,String> params) 191 throws ParseException { 192 193 // Parse code parameter 194 195 AuthorizationCode code = null; 196 197 if (params.get("code") != null) { 198 code = new AuthorizationCode(params.get("code")); 199 } 200 201 // Parse access_token parameters 202 203 AccessToken accessToken = null; 204 205 if (params.get("access_token") != null) { 206 207 JSONObject jsonObject = new JSONObject(); 208 jsonObject.putAll(params); 209 accessToken = AccessToken.parse(jsonObject); 210 } 211 212 // Parse optional state parameter 213 State state = State.parse(params.get("state")); 214 215 return new AuthorizationSuccessResponse(redirectURI, code, accessToken, state, null); 216 } 217 218 219 /** 220 * Parses an authorisation success response. 221 * 222 * <p>Use a relative URI if the host, port and path details are not 223 * known: 224 * 225 * <pre> 226 * URI relUrl = new URI("http://?code=Qcb0Orv1...&state=af0ifjsldkj"); 227 * </pre> 228 * 229 * <p>Example URI: 230 * 231 * <pre> 232 * https://client.example.com/cb?code=SplxlOBeZQQYbYS6WxSbIA&state=xyz 233 * </pre> 234 * 235 * @param uri The URI to parse. Can be absolute or relative, with a 236 * fragment or query string containing the authorisation 237 * response parameters. Must not be {@code null}. 238 * 239 * @return The authorisation success response. 240 * 241 * @throws ParseException If the redirection URI couldn't be parsed to 242 * an authorisation success response. 243 */ 244 public static AuthorizationSuccessResponse parse(final URI uri) 245 throws ParseException { 246 247 Map<String,String> params; 248 249 if (uri.getRawFragment() != null) { 250 251 params = URLUtils.parseParameters(uri.getRawFragment()); 252 253 } else if (uri.getRawQuery() != null) { 254 255 params = URLUtils.parseParameters(uri.getRawQuery()); 256 257 } else { 258 259 throw new ParseException("Missing URI fragment or query string"); 260 } 261 262 return parse(URIUtils.getBaseURI(uri), params); 263 } 264 265 266 /** 267 * Parses an authorisation success response from the specified initial 268 * HTTP 302 redirect response generated at the authorisation endpoint. 269 * 270 * <p>Example HTTP response: 271 * 272 * <pre> 273 * HTTP/1.1 302 Found 274 * Location: https://client.example.com/cb?code=SplxlOBeZQQYbYS6WxSbIA&state=xyz 275 * </pre> 276 * 277 * @see #parse(HTTPRequest) 278 * 279 * @param httpResponse The HTTP response to parse. Must not be 280 * {@code null}. 281 * 282 * @return The authorisation success response. 283 * 284 * @throws ParseException If the HTTP response couldn't be parsed to an 285 * authorisation success response. 286 */ 287 public static AuthorizationSuccessResponse parse(final HTTPResponse httpResponse) 288 throws ParseException { 289 290 URI location = httpResponse.getLocation(); 291 292 if (location == null) { 293 throw new ParseException("Missing redirection URL / HTTP Location header"); 294 } 295 296 return parse(location); 297 } 298 299 300 /** 301 * Parses an authorisation success response from the specified HTTP 302 * request at the client redirection (callback) URI. Applies to 303 * {@code query}, {@code fragment} and {@code form_post} response 304 * modes. 305 * 306 * <p>Example HTTP request (authorisation success): 307 * 308 * <pre> 309 * GET /cb?code=SplxlOBeZQQYbYS6WxSbIA&state=xyz HTTP/1.1 310 * Host: client.example.com 311 * </pre> 312 * 313 * @see #parse(HTTPResponse) 314 * 315 * @param httpRequest The HTTP request to parse. Must not be 316 * {@code null}. 317 * 318 * @throws ParseException If the HTTP request couldn't be parsed to an 319 * authorisation success response. 320 */ 321 public static AuthorizationSuccessResponse parse(final HTTPRequest httpRequest) 322 throws ParseException { 323 324 final URI baseURI; 325 326 try { 327 baseURI = httpRequest.getURL().toURI(); 328 329 } catch (URISyntaxException e) { 330 throw new ParseException(e.getMessage(), e); 331 } 332 333 if (httpRequest.getQuery() != null) { 334 // For query string and form_post response mode 335 return parse(baseURI, URLUtils.parseParameters(httpRequest.getQuery())); 336 } else if (httpRequest.getFragment() != null) { 337 // For fragment response mode (never available in actual HTTP request from browser) 338 return parse(baseURI, URLUtils.parseParameters(httpRequest.getFragment())); 339 } else { 340 throw new ParseException("Missing URI fragment, query string or post body"); 341 } 342 } 343}