Authentication
There are three main ways to authenticate with Anaplan.
- Basic Authentication
- Certificate Authentication
- OAuth2
Anaplan SDK supports all of them, though Basic Authentication is strictly not recommended for production use.
Certificate
Authentication is currently the most suitable for production use, since the Anaplan OAuth 2.0 implementation does not
support the client_credentials
grant type. This means you will have to manually manage the Refresh Token.
Basic Authentication
Basic Authentication is the simplest way to authenticate with Anaplan. It is unsuitable for Production. Anaplan password policies force password changes every 30, 60 or 90 days, depending on tenant settings, making this approach annoying to maintain and error-prone.
import anaplan_sdk
anaplan = anaplan_sdk.Client(
workspace_id="AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
model_id="11111111111111111111111111111111",
user_email="admin@company.com",
password="my_super_secret_password",
)
import anaplan_sdk
anaplan = anaplan_sdk.AsyncClient(
workspace_id="AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
model_id="11111111111111111111111111111111",
user_email="admin@company.com",
password="my_super_secret_password",
)
Certificate Authentication
Certificate Authentication is the most suitable for production use. It uses an X.509 S/MIME Certificate (aka. Client Certificate or HTTPS-Certificate) and Private Key. The Process of acquiring such a certificate is well documented. Anaplan does not support self-signed certificates, so you will need to procure a certificate from a trusted Certificate Authority (CA).
Requires Extra
If you want to use certificate authentication, you need to install the cert
extra:
pip install anaplan-sdk[cert]
uv add anaplan-sdk[cert]
poetry add anaplan-sdk[cert]
This will install cryptography to securely construct the authentication request.
import anaplan_sdk
anaplan = anaplan_sdk.Client(
workspace_id="AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
model_id="11111111111111111111111111111111",
certificate="~/certs/anaplan.pem",
private_key="~/keys/anaplan.pem",
private_key_password="my_super_secret_password", # Optional
)
import anaplan_sdk
anaplan = anaplan_sdk.AsyncClient(
workspace_id="AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
model_id="11111111111111111111111111111111",
certificate="~/certs/anaplan.pem",
private_key="~/keys/anaplan.pem",
private_key_password="my_super_secret_password", # Optional
)
OAuth2
Anaplan has introduced Oauth2 support, but it does not support the client_credentials
grant type. This means you will
have to at least once manually authenticate in the interactive authorization_code
flow. The Anaplan SDK does support
this flow, but it does not automatically manage the refresh_token
. You can however securely store the refresh_token
in your app and use it to repeatedly and authenticate with Anaplan without any interaction.
Requires Extra
If you want to use Oauth2 authentication, you need to install the oauth
extra:
pip install anaplan-sdk[oauth]
uv add anaplan-sdk[oauth]
poetry add anaplan-sdk[oauth]
This will install OAuthLib to securely construct the authentication request.
import anaplan_sdk
anaplan = anaplan_sdk.Client(
workspace_id="AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
model_id="11111111111111111111111111111111",
redirect_uri="https://vinzenzklass.github.io/anaplan-sdk",
client_id="my_anaplan_oauth_client_id",
client_secret="my_anaplan_oauth_client_secret",
)
import anaplan_sdk
anaplan = anaplan_sdk.AsyncClient(
workspace_id="AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
model_id="11111111111111111111111111111111",
redirect_uri="https://vinzenzklass.github.io/anaplan-sdk",
client_id="my_anaplan_oauth_client_id",
client_secret="my_anaplan_oauth_client_secret",
)
With these parameters, the SDK will prompt you to open the login URI in your browser. After you have logged in, you will need to copy the entire redirect URI from your browser and paste it into the terminal.
Why do I need to copy the redirect URI?
Unfortunately, registering localhost redirect URIs is not supported by Anaplan. This means we cannot intercept the
redirect URI and extract the authorization_code
automatically. This is a limitation of Anaplan's OAuth2 implementation. See this Community Note.
Authorization Code
When using OAuth authentication, the default behavior prompts you to manually open a URL, authorize the application, and paste the redirect URL back into your terminal. However, you can customize this flow by providing the on_auth_code
callback.
The on_auth_code
callback lets you hook into the Auth Flow to handle the authorization URL programmatically and return the authorization response. on_auth_code
must be a callable that takes the authorization URL as a single argument of type str
and returns the redirect URL as a str
.
Asynchronous Callbacks
Both on_auth_code
and on_token_refresh
can be either synchronous or asynchronous. When using asynchronous
callbacks in complex applications with multiple event loops, be aware that callbacks may execute in a separate
event loop context from where they were defined, which can make debugging challenging.
def on_auth_code(redirect_uri: str) -> str:
return input(f"Go fetch! {redirect_uri}\nPaste here: ")
anaplan = anaplan_sdk.Client(
workspace_id="AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
model_id="11111111111111111111111111111111",
redirect_uri="https://vinzenzklass.github.io/anaplan-sdk",
client_id="my_anaplan_oauth_client_id",
client_secret="my_anaplan_oauth_client_secret",
on_auth_code=on_auth_code,
)
async def on_auth_code(redirect_uri: str) -> str: # Can be sync or async
return input(f"Go fetch! {redirect_uri}\nPaste here: ")
anaplan = anaplan_sdk.AsyncClient(
workspace_id="AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
model_id="11111111111111111111111111111111",
redirect_uri="https://vinzenzklass.github.io/anaplan-sdk",
client_id="my_anaplan_oauth_client_id",
client_secret="my_anaplan_oauth_client_secret",
on_auth_code=on_auth_code,
)
Refresh Tokens
You can extend the above example to also pass a refresh_token
to authenticate without any user interaction, and
optionally pass a callable to the on_token_refresh
parameter. This allows you to hook into the token refresh flow and
store the current refresh_token
securely in your app. on_token_refresh
musst be a callable that takes the token as
a single argument of type dict[str, str]
and returns None
.
Example
The below uses the pykeepass library to locally store the token. This is not a recommendation and a purely illustrative example. How you handle the token is entirely up to you.
import json
from pykeepass import PyKeePass
kp = PyKeePass("db.kdbx", password="keepass")
group = kp.add_group(kp.root_group, "Anaplan")
def on_token_refresh(token: dict[str, str]) -> None:
kp.add_entry(
group, title="Anaplan Token", username=None, password=json.dumps(token)
)
kp.save()
anaplan = anaplan_sdk.Client(
workspace_id="AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
model_id="11111111111111111111111111111111",
redirect_uri="https://vinzenzklass.github.io/anaplan-sdk",
client_id="my_anaplan_oauth_client_id",
client_secret="my_anaplan_oauth_client_secret",
refresh_token="my_current_refresh_token",
on_token_refresh=on_token_refresh,
)
import json
from pykeepass import PyKeePass
kp = PyKeePass("db.kdbx", password="keepass")
group = kp.add_group(kp.root_group, "Anaplan")
def on_token_refresh(token: dict[str, str]) -> None: # Can also be async
kp.add_entry(
group, title="Anaplan Token", username=None, password=json.dumps(token)
)
kp.save()
anaplan = anaplan_sdk.AsyncClient(
workspace_id="AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
model_id="11111111111111111111111111111111",
redirect_uri="https://vinzenzklass.github.io/anaplan-sdk",
client_id="my_anaplan_oauth_client_id",
client_secret="my_anaplan_oauth_client_secret",
refresh_token="my_current_refresh_token",
on_token_refresh=on_token_refresh,
)