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