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