/* * Copyright (c) 2013 Dropbox, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.dropbox.client2.session; import java.util.Map; import org.apache.http.HttpRequest; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpUriRequest; import com.dropbox.client2.DropboxAPI; import com.dropbox.client2.RESTUtility; import com.dropbox.client2.RESTUtility.RequestMethod; import com.dropbox.client2.exception.DropboxException; import com.dropbox.client2.exception.DropboxIOException; import com.dropbox.client2.exception.DropboxParseException; import com.dropbox.client2.exception.DropboxServerException; /** * Keeps track of a logged in user contains configuration options for the * {@link DropboxAPI}. This type of {@link Session} uses the OAuth2 * "auth code flow" to authenticate users: * <ol> * <li>A redirect URL are retrieved using * {@link WebOAuth2Session#getAuthorizeURL()} or * {@link WebOAuth2Session#getAuthorizeURL(String, String)}.</li> * <li>You redirect the * user to the redirect URL where they will authenticate with Dropbox and * grant your app permission to access their account.</li> * <li>Dropbox will redirect back to your site with a auth code in the query * string if it was provided a URL to do so (otherwise, you have to ask the * user for the auth code when he/she is done).</li> * <li>The user's access token is set on this session when you call * {@link WebOAuth2Session#retrieveWebAccessToken(String, String)} with the auth code * from Dropbox and the same redirect URL you passed to * {@link WebOAuth2Session#getAuthorizeURL(String, String)}. You have a limited amount * of time to make this call or the request token will expire.</li> </ol> */ public class WebOAuth2Session extends AbstractSession { /** * Creates a new web auth session with the given app key pair. * The session will not be linked because it has no access token. */ public WebOAuth2Session(AppKeyPair appKeyPair) { super(appKeyPair); } /** * Creates a new web auth session with the given app key pair. * The session will also be linked to the account corresponding to the * given access token. */ public WebOAuth2Session(AppKeyPair appKeyPair, String oauth2AccessToken) { super(appKeyPair, oauth2AccessToken); } public String getAuthorizeURL() { return getAuthorizeURL(null, null); } /** * Starts an authentication request with Dropbox servers and computes the * URL the user needs to visit to authorize your app. * * @param redirectUrl the URL to which Dropbox will redirect the user after * he/she has authenticated on the Dropbox site. If the user * authorizes your app, the server will pass a <code>code</code> * query parameter that you should pass to * {@link WebOAuth2Session#retrieveWebAccessToken(String, String)} to * finish authentication. If the user denies access to your app, the * query will contain <code>error</code> and * <code>error_description</code> parameters. Make sure this URL * appears in the "Redirect URIs" section on the App Console page * for your app. If you pass <code>null</code> for this parameter, * the user will be presented with the Auth code. * @param csrfToken If not <code>null</code>, this will appear as the * <code>state</code> query parameter when the server redirects the * user to <code>redirectURL</code>. This should be a short string * tied to the authentication state of the user in your system. When * a user fetches <code>redirectURL</code>, you should verify the * state parameter is the same what you pass to this function. This * protects your application against cross-site request * forgery. While technically optional, this parameter should always * be passed when using redirects. * * @return the URL to redirect the user to to authorize your app */ public String getAuthorizeURL(String redirectUrl, String csrfToken) { String[] args = new String[] { "response_type", "code", "client_id", getAppKeyPair().key, "redirect_uri", redirectUrl, "state", csrfToken, }; String path = "/oauth2/authorize"; return RESTUtility.buildURL(getWebServer(), DropboxAPI.VERSION, path, args); } /** * Obtain an access token for a user. This is the second step of OAuth2 * "Auth code flow". After you send the user to the authorize URL, you can * use the authorized request code with this method to get the access token * to use for future operations. The access token is stored on the session * object. * * @param code the auth code sent to you by the Dropbox server * @param redirectUrl This should be exactly the same as was passed to * getAuthorizeURL. (Possibly null). * * @return the access token for the user * * @throws DropboxServerException if the server responds with an error * code. See the constants in {@link DropboxServerException} for * the meaning of each error code. The most common error codes you * can expect from this call are 401 (bad request token), 403 (bad * app key pair), 500, 502, and 503 (all for internal Dropbox * server issues). * @throws DropboxIOException if any network-related error occurs. * @throws DropboxParseException if a malformed or unknown response was * received from the server. * @throws DropboxException for any other unknown errors. This is also a * superclass of all other Dropbox exceptions, so you may want to * only catch this exception which signals that some kind of error * occurred. */ public String retrieveWebAccessToken(String code, String redirectUrl) throws DropboxException { if (code == null) throw new IllegalArgumentException("'code' must not be null"); String[] args = new String[] { "grant_type", "authorization_code", "code", code, "client_id", getAppKeyPair().key, "client_secret", getAppKeyPair().secret, "redirect_uri", redirectUrl, }; String path = "/oauth2/token"; String url = RESTUtility.buildURL(getAPIServer(), DropboxAPI.VERSION, path, args); HttpUriRequest req = new HttpPost(url); HttpResponse resp = RESTUtility.execute(this, req); @SuppressWarnings("unchecked") Map<String, Object> respData = (Map<String, Object>)RESTUtility.parseAsJSON(resp); String accessToken = (String)respData.get("access_token"); setOAuth2AccessToken(accessToken); return accessToken; } }