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         * &amp;state=xyz
110         * &amp;token_type=example
111         * &amp;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         * &amp;state=xyz
132         * &amp;token_type=example
133         * &amp;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&amp;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}