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 redirect 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 redirect URI. Must not be {@code null}.
044         * @param state       The state, {@code null} if not requested.
045         */
046        protected AuthorizationResponse(final URL redirectURI, final State state) {
047
048                if (redirectURI == null)
049                        throw new IllegalArgumentException("The redirect URI must not be null");
050                
051                this.redirectURI = redirectURI;
052
053                this.state = state;
054        }
055
056
057        /**
058         * Gets the base redirect URI.
059         *
060         * @return The base redirect URI (without the appended error response 
061         *         parameters).
062         */
063        public URL getRedirectURI() {
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 (redirect 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 URL 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                
147                response.setLocation(toURI());
148                
149                return response;
150        }
151
152
153        /**
154         * Parses an authorisation response.
155         *
156         * @param redirectURI The base redirect URI. Must not be {@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 = null;
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 redirect URL / HTTP Location header");
242                
243                return parse(location);
244        }
245}