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