All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.dropbox.core.DbxPKCEWebAuth Maven / Gradle / Ivy

There is a newer version: 7.0.0
Show newest version
package com.dropbox.core;

import com.dropbox.core.http.HttpRequestor;
import com.dropbox.core.util.LangUtil;
import com.dropbox.core.v2.DbxRawClientV2;

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.HashMap;
import java.util.Map;

import static com.dropbox.core.util.StringUtil.urlSafeBase64Encode;

/**
 *
 *
 * This class does the OAuth2 "authorization code" flow with Proof Key for Code Exchange(PKCE).
 *
 * PKCE allows "authorization code" flow without "client_secret". It enables "native
 * application", which is ensafe to hardcode client_secret in code, to use "authorization
 * code". If you application has a server, please use regular {@link DbxWebAuth} instead.
 *
 * PKCE is more secure than "token" flow. If authorization code is compromised during
 * transmission, it can't be used to exchange for access token without random generated
 * code_verifier, which is stored inside SDK.
 *
 * DbxPKCEWebAuth and {@link DbxWebAuth} has the same interface and slightly different behavior:
 * 
    *
  1. The constructor should take {@link DbxAppInfo} without app secret.
  2. *
  3. Two step of OAuth2: {@link #authorize(DbxWebAuth.Request)} and * {@link #finishFromRedirect(String, DbxSessionStore, Map)}, should be called on the same * object.
  4. *
* * @see https://tools.ietf.org/html/rfc7636 and * new dropbox oauth guide */ public class DbxPKCEWebAuth { private final DbxRequestConfig requestConfig; private final DbxAppInfo appInfo; private final DbxWebAuth dbxWebAuth; private final DbxPKCEManager dbxPKCEManager; private boolean started; private boolean consumed; /** * Creates a new instance that will perform the OAuth2 PKCE authorization flow using the given * OAuth request configuration. * * @param requestConfig HTTP request configuration, never {@code null}. * @param appInfo Your application's Dropbox API information (the app key), never {@code null}. * * @throws IllegalStateException if appInfo contains app secret. */ public DbxPKCEWebAuth(DbxRequestConfig requestConfig, DbxAppInfo appInfo) { if (appInfo.hasSecret()) { throw new IllegalStateException("PKCE cdoe flow doesn't require app secret, if you " + "decide to embed it in your app, please use regular DbxWebAuth instead."); } this.requestConfig = requestConfig; this.appInfo = appInfo; this.dbxWebAuth = new DbxWebAuth(requestConfig, appInfo); this.dbxPKCEManager = new DbxPKCEManager(); this.started = false; this.consumed = false; } /** * Starts authorization and returns an "authorization URL" on the Dropbox website that let * the user grant your app access to their Dropbox account. * *

If a redirect URI was specified ({@link DbxWebAuth.Request.Builder#withRedirectUri}). The * redirect URI should bring user back to your app on end device. Call {@link * #finishFromRedirect} using the same {@link DbxPKCEWebAuth} instance with the query * parameters received from the redirect. * *

If no redirect URI was specified ({@link DbxWebAuth.Request.Builder#withNoRedirect}), * then users who grant access will be shown an "authorization code". The user must copy/paste the * authorization code back into your app, at which point you can call {@link * #finishFromCode(String)} with the same {@link DbxPKCEWebAuth} instance from to get an access * token. * * @param request OAuth 2.0 web-based authorization flow request configuration * * @return Authorization URL of website user can use to authorize your app. * */ public String authorize(DbxWebAuth.Request request) { if (consumed) { throw new IllegalStateException("This DbxPKCEWebAuth instance has been consumed " + "already. To start a new PKCE OAuth flow, please create a new instance."); } this.started = true; Map pkceParams = new HashMap(); pkceParams.put("code_challenge", dbxPKCEManager.getCodeChallenge()); pkceParams.put("code_challenge_method", DbxPKCEManager.CODE_CHALLENGE_METHODS); return dbxWebAuth.authorizeImpl(request, pkceParams); } /** * Call this after the user has visited the authorizaton URL and copy/pasted the authorization * code that Dropbox gave them, with the SAME {@link DbxPKCEWebAuth} instance that generated * the authorization URL. * * @throws DbxException if the instance is not the same one used to generate authorization * URL, or if an error occurs communicating with Dropbox. * @see DbxWebAuth#finishFromCode(String) */ public DbxAuthFinish finishFromCode(String code) throws DbxException { return finish(code, null, null); } /** * Call this after the user has visited the authorizaton URL and Dropbox has redirected them * back to your native app, with the SAME {@link DbxPKCEWebAuth} instance that generated * the authorization URL. * * @throws BadRequestException If the redirect request is missing required query parameters, * contains duplicate parameters, or includes mutually exclusive parameters (e.g. {@code * "error"} and {@code "code"}). * @throws DbxWebAuth.BadStateException If the CSRF token retrieved from {@code sessionStore} * is {@code null} or malformed. * @throws DbxWebAuth.CsrfException If the CSRF token passed in {@code params} does not match * the CSRF token from {@code sessionStore}. This implies the redirect request may be forged. * @throws DbxWebAuth.NotApprovedException If the user chose to deny the authorization request. * @throws DbxWebAuth.ProviderException If an OAuth2 error response besides {@code * "access_denied"} is set. * @throws DbxException if the instance is not the same one used to generate authorization * URL, or if an error occurs communicating with Dropbox. */ public DbxAuthFinish finishFromRedirect(String redirectUri, DbxSessionStore sessionStore, Map params) throws DbxException, DbxWebAuth.BadRequestException, DbxWebAuth.BadStateException, DbxWebAuth.CsrfException, DbxWebAuth.NotApprovedException, DbxWebAuth.ProviderException { String strippedState = DbxWebAuth.validateRedirectUri(redirectUri, sessionStore, params); String code = DbxWebAuth.getParam(params, "code"); return finish(code, redirectUri, strippedState); } DbxAuthFinish finish(String code, String redirectUri, final String state) throws DbxException { if (code == null) throw new NullPointerException("code"); if (!this.started) { throw new IllegalStateException("Must initialize the PKCE flow by calling authorize " + "first."); } if (this.consumed) { throw new IllegalStateException("This DbxPKCEWebAuth instance has been consumed " + "already. To start a new PKCE OAuth flow, please create a new instance."); } DbxAuthFinish authFinish = dbxPKCEManager.makeTokenRequest( requestConfig, code, appInfo.getKey(), redirectUri, appInfo.getHost() ); this.consumed = true; return authFinish.withUrlState(state); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy