OAuth 2 flow helpers — plus the endpoints of the Dropbox auth namespace.
Dropbox no longer issues long-lived access tokens: they expire after about
four hours, so any application that runs longer than that must use the
refresh token flow. The functions here are stateless — they build the
authorization URL and talk to Dropbox's /oauth2/token endpoint. Keeping
a token fresh over time is Magpie.Auth.TokenServer's job.
The whole flow, end to end:
# 1. Send the user to Dropbox
{verifier, challenge} = Magpie.Auth.pkce_pair()
url =
Magpie.Auth.authorize_url(app_key,
redirect_uri: "https://myapp.com/dropbox/callback",
state: csrf_token,
scope: ["account_info.read", "files.content.write"],
code_challenge: challenge
)
# 2. Dropbox redirects back with ?code=...
{:ok, token} =
Magpie.Auth.exchange_code(app_key, code,
code_verifier: verifier,
redirect_uri: "https://myapp.com/dropbox/callback"
)
# 3. Store token.refresh_token, then build clients from it
client = Magpie.Client.new(refresh_token: token.refresh_token, app_key: app_key, pkce: true)See the OAuth guide for the full walkthrough, including how to get a refresh token without writing any code.
Endpoints
The OAuth endpoints can be overridden (rarely needed) via:
config :magpie,
oauth_authorize_url: "https://www.dropbox.com/oauth2/authorize",
oauth_token_url: "https://api.dropboxapi.com/oauth2/token"
Summary
Functions
Builds the URL where the user authorizes your app.
Exchanges the authorization code Dropbox sent to your redirect URI for a token set.
Derives the S256 PKCE challenge of a verifier.
Generates a PKCE {code_verifier, code_challenge} pair.
Trades a refresh token for a fresh access token.
Disables the access token used to authenticate the call.
Types
Functions
Builds the URL where the user authorizes your app.
token_access_type defaults to "offline", which is what makes Dropbox
return a refresh token alongside the access token.
Options
:redirect_uri— where Dropbox sends the user back to. Must match one of the redirect URIs registered in the App Console:state— opaque value echoed back in the redirect, used to protect against CSRF (and to carry your own context):scope— list of scopes (or an already space-separated string). When omitted, the app's configured scopes are granted:code_challenge— PKCE challenge frompkce_pair/0; addscode_challenge_method=S256:token_access_type—"offline"(default),"online"or"legacy":extra_params— keyword list or map of additional Dropbox authorization params (force_reapprove,locale,require_role,disable_signup, ...), appended after the standard ones. Values are rendered withto_string/1, so booleans and atoms work. Params this function already sets cannot be overridden — a collision raisesArgumentError
Examples
iex> Magpie.Auth.authorize_url("APP_KEY")
"https://www.dropbox.com/oauth2/authorize?client_id=APP_KEY&response_type=code&token_access_type=offline"
iex> Magpie.Auth.authorize_url("APP_KEY",
...> redirect_uri: "https://myapp.com/cb",
...> scope: ["files.content.read", "files.content.write"]
...> )
"https://www.dropbox.com/oauth2/authorize?client_id=APP_KEY&response_type=code&token_access_type=offline&redirect_uri=https%3A%2F%2Fmyapp.com%2Fcb&scope=files.content.read%20files.content.write"
iex> Magpie.Auth.authorize_url("APP_KEY",
...> extra_params: [force_reapprove: true, locale: "pt_BR"]
...> )
"https://www.dropbox.com/oauth2/authorize?client_id=APP_KEY&response_type=code&token_access_type=offline&force_reapprove=true&locale=pt_BR"
@spec exchange_code(String.t(), String.t(), [token_opt()]) :: {:ok, Magpie.Auth.Token.t()} | {:error, Magpie.Error.t()}
Exchanges the authorization code Dropbox sent to your redirect URI for a token set.
Pass :app_secret for confidential apps, or :code_verifier for PKCE
apps. :redirect_uri is required whenever it was present in the
authorization request, and must be identical.
This is the only response that carries a refresh_token — store it.
{:ok, %Magpie.Auth.Token{refresh_token: refresh_token}} =
Magpie.Auth.exchange_code(app_key, code,
app_secret: app_secret,
redirect_uri: "https://myapp.com/dropbox/callback"
)Returns {:error, %Magpie.Error{}} when Dropbox rejects the exchange —
summary then holds the OAuth error code (e.g. "invalid_grant" for an
expired or already-used code).
Derives the S256 PKCE challenge of a verifier.
Test vector from RFC 7636, appendix B:
iex> Magpie.Auth.pkce_challenge("dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk")
"E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM"
Generates a PKCE {code_verifier, code_challenge} pair.
Public apps (mobile, desktop, CLIs — anything that cannot keep a secret)
use PKCE instead of the app secret: send the challenge to
authorize_url/2 and the verifier to exchange_code/3.
iex> {verifier, challenge} = Magpie.Auth.pkce_pair()
iex> byte_size(verifier)
86
iex> challenge == Magpie.Auth.pkce_challenge(verifier)
true
@spec refresh(String.t(), String.t(), [token_opt()]) :: {:ok, Magpie.Auth.Token.t()} | {:error, Magpie.Error.t()}
Trades a refresh token for a fresh access token.
Pass :app_secret for confidential apps; PKCE apps authenticate with the
app key alone. Dropbox does not rotate refresh tokens, so the response
carries no refresh_token — keep using the one you already have.
{:ok, %Magpie.Auth.Token{access_token: access_token, expires_at: expires_at}} =
Magpie.Auth.refresh(app_key, refresh_token, app_secret: app_secret)Returns {:error, %Magpie.Error{}} when the refresh token was revoked or
is invalid (summary: "invalid_grant").
Disables the access token used to authenticate the call.
More info at: https://www.dropbox.com/developers/documentation/http/documentation#auth-token-revoke