001    package com.nimbusds.oauth2.sdk;
002    
003    
004    import java.net.URL;
005    import java.util.Map;
006    
007    import org.apache.commons.lang3.StringUtils;
008    
009    import com.nimbusds.oauth2.sdk.id.State;
010    import com.nimbusds.oauth2.sdk.http.HTTPResponse;
011    import 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     */
025    public 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    }