001package com.nimbusds.oauth2.sdk; 002 003 004import java.net.MalformedURLException; 005import java.net.URI; 006import java.net.URISyntaxException; 007import java.net.URL; 008import java.util.Map; 009 010import com.nimbusds.oauth2.sdk.util.URIUtils; 011import org.apache.commons.lang3.StringUtils; 012 013import com.nimbusds.oauth2.sdk.id.State; 014import com.nimbusds.oauth2.sdk.http.HTTPResponse; 015import com.nimbusds.oauth2.sdk.util.URLUtils; 016 017 018/** 019 * The base abstract class for authorisation success and error responses. 020 * 021 * <p>Related specifications: 022 * 023 * <ul> 024 * <li>OAuth 2.0 (RFC 6749), section 3.1. 025 * </ul> 026 */ 027public abstract class AuthorizationResponse implements Response { 028 029 030 /** 031 * The base redirection URI. 032 */ 033 private final URI redirectURI; 034 035 036 /** 037 * The optional state parameter to be echoed back to the client. 038 */ 039 private final State state; 040 041 042 /** 043 * Creates a new authorisation response. 044 * 045 * @param redirectURI The base redirection URI. Must not be 046 * {@code null}. 047 * @param state The state, {@code null} if not requested. 048 */ 049 protected AuthorizationResponse(final URI redirectURI, final State state) { 050 051 if (redirectURI == null) 052 throw new IllegalArgumentException("The redirection URI must not be null"); 053 054 this.redirectURI = redirectURI; 055 056 this.state = state; 057 } 058 059 060 /** 061 * Gets the base redirection URI. 062 * 063 * @return The base redirection URI (without the appended error 064 * response parameters). 065 */ 066 public URI getRedirectionURI() { 067 068 return redirectURI; 069 } 070 071 072 /** 073 * Gets the optional state. 074 * 075 * @return The state, {@code null} if not requested. 076 */ 077 public State getState() { 078 079 return state; 080 } 081 082 083 /** 084 * Returns the parameters of this authorisation response. 085 * 086 * <p>Example parameters (authorisation success): 087 * 088 * <pre> 089 * access_token = 2YotnFZFEjr1zCsicMWpAA 090 * state = xyz 091 * token_type = example 092 * expires_in = 3600 093 * </pre> 094 * 095 * @return The parameters as a map. 096 * 097 * @throws SerializeException If this response couldn't be serialised 098 * to a parameters map. 099 */ 100 public abstract Map<String,String> toParameters() 101 throws SerializeException; 102 103 104 /** 105 * Returns the URI representation (redirection URI + fragment / query 106 * string) of this authorisation response. 107 * 108 * <p>Example URI: 109 * 110 * <pre> 111 * http://example.com/cb#access_token=2YotnFZFEjr1zCsicMWpAA 112 * &state=xyz 113 * &token_type=example 114 * &expires_in=3600 115 * </pre> 116 * 117 * @return The URI representation of this authorisation response. 118 * 119 * @throws SerializeException If this response couldn't be serialised 120 * to a URI. 121 */ 122 public abstract URI toURI() 123 throws SerializeException; 124 125 126 /** 127 * Returns the HTTP response for this authorisation response. 128 * 129 * <p>Example HTTP response (authorisation success): 130 * 131 * <pre> 132 * HTTP/1.1 302 Found 133 * Location: http://example.com/cb#access_token=2YotnFZFEjr1zCsicMWpAA 134 * &state=xyz 135 * &token_type=example 136 * &expires_in=3600 137 * </pre> 138 * 139 * @return The HTTP response matching this authorisation response. 140 * 141 * @throws SerializeException If the response couldn't be serialised to 142 * an HTTP response. 143 */ 144 @Override 145 public HTTPResponse toHTTPResponse() 146 throws SerializeException { 147 148 HTTPResponse response = new HTTPResponse(HTTPResponse.SC_FOUND); 149 150 try { 151 response.setLocation(toURI().toURL()); 152 153 } catch (MalformedURLException e) { 154 155 throw new SerializeException(e.getMessage(), e); 156 } 157 158 return response; 159 } 160 161 162 /** 163 * Parses an authorisation response. 164 * 165 * @param redirectURI The base redirection URI. Must not be 166 * {@code null}. 167 * @param params The response parameters to parse. Must not be 168 * {@code null}. 169 * 170 * @return The authorisation success or error response. 171 * 172 * @throws ParseException If the parameters couldn't be parsed to an 173 * authorisation success or error response. 174 */ 175 public static AuthorizationResponse parse(final URI redirectURI, final Map<String,String> params) 176 throws ParseException { 177 178 if (StringUtils.isNotBlank(params.get("error"))) 179 return AuthorizationErrorResponse.parse(redirectURI, params); 180 181 else 182 return AuthorizationSuccessResponse.parse(redirectURI, params); 183 } 184 185 186 /** 187 * Parses an authorisation response. 188 * 189 * <p>Use a relative URI if the host, port and path details are not 190 * known: 191 * 192 * <pre> 193 * URI relUrl = new URI("http://?code=Qcb0Orv1...&state=af0ifjsldkj"); 194 * AuthorizationResponse = AuthorizationResponse.parse(relURL); 195 * </pre> 196 * 197 * @param uri The URI to parse. May be absolute or relative, with a 198 * fragment or query string containing the authorisation 199 * response parameters. Must not be {@code null}. 200 * 201 * @return The authorisation success or error response. 202 * 203 * @throws ParseException If no authorisation response parameters were 204 * found in the URL. 205 */ 206 public static AuthorizationResponse parse(final URI uri) 207 throws ParseException { 208 209 Map<String,String> params; 210 211 if (uri.getRawFragment() != null) 212 params = URLUtils.parseParameters(uri.getRawFragment()); 213 214 else if (uri.getQuery() != null) 215 params = URLUtils.parseParameters(uri.getQuery()); 216 217 else 218 throw new ParseException("Missing URI fragment or query string"); 219 220 221 return parse(URIUtils.getBaseURI(uri), params); 222 } 223 224 225 /** 226 * Parses an authorisation response. 227 * 228 * <p>Example HTTP response (authorisation success): 229 * 230 * <pre> 231 * HTTP/1.1 302 Found 232 * Location: https://client.example.com/cb?code=SplxlOBeZQQYbYS6WxSbIA&state=xyz 233 * </pre> 234 * 235 * @param httpResponse The HTTP response to parse. Must not be 236 * {@code null}. 237 * 238 * @throws ParseException If the HTTP response couldn't be parsed to an 239 * authorisation response. 240 */ 241 public static AuthorizationResponse parse(final HTTPResponse httpResponse) 242 throws ParseException { 243 244 if (httpResponse.getStatusCode() != HTTPResponse.SC_FOUND) 245 throw new ParseException("Unexpected HTTP status code, must be 302 (Found): " + 246 httpResponse.getStatusCode()); 247 248 URL location = httpResponse.getLocation(); 249 250 if (location == null) 251 throw new ParseException("Missing redirection URI / HTTP Location header"); 252 253 try { 254 return parse(location.toURI()); 255 256 } catch (URISyntaxException e) { 257 258 throw new ParseException(e.getMessage(), e); 259 } 260 } 261}