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