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