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 */
023public abstract class AuthorizationResponse implements Response {
024
025
026        /**
027         * The base redirection URI.
028         */
029        private final URL redirectURI;
030
031
032        /**
033         * The optional state parameter to be echoed back to the client.
034         */
035        private final State state;
036
037
038        /**
039         * Creates a new authorisation response.
040         *
041         * @param redirectURI The base redirection URI. Must not be
042         *                    {@code null}.
043         * @param state       The state, {@code null} if not requested.
044         */
045        protected AuthorizationResponse(final URL redirectURI, final State state) {
046
047                if (redirectURI == null)
048                        throw new IllegalArgumentException("The redirection URI must not be null");
049                
050                this.redirectURI = redirectURI;
051
052                this.state = state;
053        }
054
055
056        /**
057         * Gets the base redirection URI.
058         *
059         * @return The base redirection URI (without the appended error
060         *         response parameters).
061         */
062        public URL getRedirectionURI() {
063        
064                return redirectURI;
065        }
066
067
068        /**
069         * Gets the optional state.
070         *
071         * @return The state, {@code null} if not requested.
072         */
073        public State getState() {
074        
075                return state;
076        }
077
078
079        /**
080         * Returns the parameters of this authorisation response.
081         *
082         * <p>Example parameters (authorisation success):
083         *
084         * <pre>
085         * access_token = 2YotnFZFEjr1zCsicMWpAA
086         * state = xyz
087         * token_type = example
088         * expires_in = 3600
089         * </pre>
090         *
091         * @return The parameters as a map.
092         *
093         * @throws SerializeException If this response couldn't be serialised 
094         *                            to a parameters map.
095         */
096        public abstract Map<String,String> toParameters()
097                throws SerializeException;
098
099
100        /**
101         * Returns the URI representation (redirection URI + fragment / query
102         * string) of this authorisation response.
103         *
104         * <p>Example URI:
105         *
106         * <pre>
107         * http://example.com/cb#access_token=2YotnFZFEjr1zCsicMWpAA
108         * &amp;state=xyz
109         * &amp;token_type=example
110         * &amp;expires_in=3600
111         * </pre>
112         *
113         * @return The URI representation of this authorisation response.
114         *
115         * @throws SerializeException If this response couldn't be serialised 
116         *                            to a URI.
117         */
118        public abstract URL toURI()
119                throws SerializeException;
120
121
122        /**
123         * Returns the HTTP response for this authorisation response.
124         *
125         * <p>Example HTTP response (authorisation success):
126         *
127         * <pre>
128         * HTTP/1.1 302 Found
129         * Location: http://example.com/cb#access_token=2YotnFZFEjr1zCsicMWpAA
130         * &amp;state=xyz
131         * &amp;token_type=example
132         * &amp;expires_in=3600
133         * </pre>
134         *
135         * @return The HTTP response matching this authorisation response.
136         *
137         * @throws SerializeException If the response couldn't be serialised to
138         *                            an HTTP response.
139         */
140        @Override
141        public HTTPResponse toHTTPResponse()
142                throws SerializeException {
143        
144                HTTPResponse response = new HTTPResponse(HTTPResponse.SC_FOUND);
145                
146                response.setLocation(toURI());
147                
148                return response;
149        }
150
151
152        /**
153         * Parses an authorisation response.
154         *
155         * @param redirectURI The base redirection URI. Must not be
156         *                    {@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;
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&amp;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 redirection URL / HTTP Location header");
242                
243                return parse(location);
244        }
245}