001package com.nimbusds.oauth2.sdk; 002 003 004import java.net.URI; 005import java.util.Map; 006 007import com.nimbusds.oauth2.sdk.util.URIUtils; 008import org.apache.commons.lang3.StringUtils; 009 010import com.nimbusds.oauth2.sdk.id.State; 011import com.nimbusds.oauth2.sdk.http.HTTPResponse; 012import com.nimbusds.oauth2.sdk.util.URLUtils; 013 014 015/** 016 * The base abstract class for authorisation success and error responses. 017 * 018 * <p>Related specifications: 019 * 020 * <ul> 021 * <li>OAuth 2.0 (RFC 6749), section 3.1. 022 * </ul> 023 */ 024public abstract class AuthorizationResponse implements Response { 025 026 027 /** 028 * The base redirection URI. 029 */ 030 private final URI redirectURI; 031 032 033 /** 034 * The optional state parameter to be echoed back to the client. 035 */ 036 private final State state; 037 038 039 /** 040 * Creates a new authorisation response. 041 * 042 * @param redirectURI The base redirection URI. Must not be 043 * {@code null}. 044 * @param state The state, {@code null} if not requested. 045 */ 046 protected AuthorizationResponse(final URI redirectURI, final State state) { 047 048 if (redirectURI == null) 049 throw new IllegalArgumentException("The redirection URI must not be null"); 050 051 this.redirectURI = redirectURI; 052 053 this.state = state; 054 } 055 056 057 /** 058 * Gets the base redirection URI. 059 * 060 * @return The base redirection URI (without the appended error 061 * response parameters). 062 */ 063 public URI getRedirectionURI() { 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 (redirection 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 URI 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 response.setLocation(toURI()); 147 return response; 148 } 149 150 151 /** 152 * Parses an authorisation response. 153 * 154 * @param redirectURI The base redirection URI. Must not be 155 * {@code null}. 156 * @param params The response parameters to parse. Must not be 157 * {@code null}. 158 * 159 * @return The authorisation success or error response. 160 * 161 * @throws ParseException If the parameters couldn't be parsed to an 162 * authorisation success or error response. 163 */ 164 public static AuthorizationResponse parse(final URI redirectURI, final Map<String,String> params) 165 throws ParseException { 166 167 if (StringUtils.isNotBlank(params.get("error"))) 168 return AuthorizationErrorResponse.parse(redirectURI, params); 169 170 else 171 return AuthorizationSuccessResponse.parse(redirectURI, params); 172 } 173 174 175 /** 176 * Parses an authorisation response. 177 * 178 * <p>Use a relative URI if the host, port and path details are not 179 * known: 180 * 181 * <pre> 182 * URI relUrl = new URI("http://?code=Qcb0Orv1...&state=af0ifjsldkj"); 183 * AuthorizationResponse = AuthorizationResponse.parse(relURL); 184 * </pre> 185 * 186 * @param uri The URI to parse. May be absolute or relative, with a 187 * fragment or query string containing the authorisation 188 * response parameters. Must not be {@code null}. 189 * 190 * @return The authorisation success or error response. 191 * 192 * @throws ParseException If no authorisation response parameters were 193 * found in the URL. 194 */ 195 public static AuthorizationResponse parse(final URI uri) 196 throws ParseException { 197 198 Map<String,String> params; 199 200 if (uri.getRawFragment() != null) 201 params = URLUtils.parseParameters(uri.getRawFragment()); 202 203 else if (uri.getQuery() != null) 204 params = URLUtils.parseParameters(uri.getQuery()); 205 206 else 207 throw new ParseException("Missing URI fragment or query string"); 208 209 210 return parse(URIUtils.getBaseURI(uri), params); 211 } 212 213 214 /** 215 * Parses an authorisation response. 216 * 217 * <p>Example HTTP response (authorisation success): 218 * 219 * <pre> 220 * HTTP/1.1 302 Found 221 * Location: https://client.example.com/cb?code=SplxlOBeZQQYbYS6WxSbIA&state=xyz 222 * </pre> 223 * 224 * @param httpResponse The HTTP response to parse. Must not be 225 * {@code null}. 226 * 227 * @throws ParseException If the HTTP response couldn't be parsed to an 228 * authorisation response. 229 */ 230 public static AuthorizationResponse parse(final HTTPResponse httpResponse) 231 throws ParseException { 232 233 if (httpResponse.getStatusCode() != HTTPResponse.SC_FOUND) 234 throw new ParseException("Unexpected HTTP status code, must be 302 (Found): " + 235 httpResponse.getStatusCode()); 236 237 URI location = httpResponse.getLocation(); 238 239 if (location == null) 240 throw new ParseException("Missing redirection URI / HTTP Location header"); 241 242 return parse(location); 243 } 244}