Use OAuthAuthenticator (maltego.model.oauth.OAuthAuthenticator) when a transform needs the user to authenticate against a third-party API in the Maltego client. The client handles the browser-based login flow, while the SDK registers the authenticator, enables OAuthMiddleware, and exposes the access token to the transform through settings[OAUTH.access_token_input].
**Client availability:** OAuth-authenticated transforms are currently supported in Maltego Graph Desktop only. Maltego Graph Browser does not currently run the OAuth flow for a custom transform server.Configuring an authenticator
Create one OAuthAuthenticator per provider:
from maltego.model.oauth import OAuthAuthenticator
OAUTH = OAuthAuthenticator(
name="acme-api",
display_name="Log in to Acme",
description="Authenticate against the Acme API",
access_token_input="acme.token",
access_token_pem_file_prefix="./oauth/acme",
oauth_version="2.0",
app_key="ACME_CLIENT_ID",
app_secret="ACME_CLIENT_SECRET",
authorization_url="https://example.com/oauth/authorize",
access_token_endpoint="https://example.com/oauth/token",
request_type_for_access_token="POST",
icon="",
)The main fields are:
access_token_input: the setting name your transform will read later.access_token_pem_file_prefix: prefix for the PEM files used to protect the token material on disk.oauth_version: use"2.0"for standard OAuth 2.0 providers and"1.0a"when the provider also requiresrequest_token_endpoint.app_key/app_secret: the client credentials for the provider.
Key material and development setup
The SDK expects the following files for the configured access_token_pem_file_prefix:
{prefix}_private.pem{prefix}_public.pem
During setup(), the server copies MaltegoServerSettings.allow_regenerating_oauth_keys onto every registered authenticator and then calls generate_keys_if_needed(). For local development, enable automatic generation:
from maltego.server import MaltegoServerSettings
settings = MaltegoServerSettings(
server_name="Acme Transforms",
ns="acme",
author="dev@acme.com",
allow_regenerating_oauth_keys=True,
)For deployed servers, generate or provision the PEM files ahead of time and keep them out of source control.
Registering an OAuth-protected transform
Attach the authenticator through register_transform(..., authenticator=...):
from typing import Any, Dict
from maltego.entities import Phrase
from maltego.server import register_transform
@register_transform(
display_name="OAuth Test [Maltego Examples]",
authenticator=OAUTH,
)
async def transform_oauth(
input_entity: Phrase,
settings: Dict[str, Any],
) -> Phrase:
token = settings[OAUTH.access_token_input]
if token is None:
raise ValueError("settings[OAUTH.access_token_input] needs to be not None")
return Phrase(token)Passing authenticator=OAUTH does two things automatically:
- The server registers the authenticator for discovery.
- On the first authenticator, the server adds
OAuthMiddlewareand injects a hiddenTransformSettingwithauth=Trueandis_oauth=True.
What the user sees in Maltego
When a user runs the transform in the Maltego client:
- Maltego prompts the user to authenticate.
- The client completes the browser flow with the configured provider.
- The encrypted token is sent back to the server using the setting named by
access_token_input. - The SDK normalizes that value so your transform reads it as
settings[OAUTH.access_token_input].
Making authenticated API calls
Once the token is available, use it like any other transform setting:
from typing import Any, Dict
from maltego.entities import Phrase
from maltego.model.context import MaltegoContext
from maltego.server import IntegrationClient, register_transform
client = IntegrationClient()
@register_transform(
display_name="Acme profile",
authenticator=OAUTH,
)
async def acme_profile(
input_entity: Phrase,
settings: Dict[str, Any],
context: MaltegoContext,
) -> Phrase:
token = settings[OAUTH.access_token_input]
if token is None:
raise ValueError("OAuth token is required")
response = await client.get(
"https://api.example.com/me",
context=context,
headers={"Authorization": f"Bearer {token}"},
)
payload = response.json()
return Phrase(payload["username"])Where this fits in a transform server project
Keep the OAuthAuthenticator definition with the rest of your server-wide configuration, and place the protected transforms in modules under transforms/. The important runtime contract is the one shown above: after the client completes the login flow, the transform reads the injected token from settings[OAUTH.access_token_input].
OAuth vs server authentication
This page is about authenticating from your transform to a third-party API. If you need to protect your own transform server with JWT/OIDC, see the Authentication article.