Usage
Usage¶
Authentication¶
There are three ways to authenticate with the Toshi API, listed in priority order. ToshiClientBase auto-detects the method based on what is configured:
- M2M — used when
NZSHM22_TOSHI_M2M_SECRET_ARNandNZSHM22_TOSHI_COGNITO_DOMAINare both set. - Interactive scientist — used when
~/.toshi/credentialsexists and the Cognito domain + scientist client ID are configured (via~/.toshi/auth_config.jsonor the matchingNZSHM22_TOSHI_COGNITO_*env vars). - Legacy API key — not auto-detected; you must pass
headers={"x-api-key": ...}explicitly.
If both M2M env vars and ~/.toshi/credentials are present on the same machine, M2M wins. ToshiClientBase logs a warning when auto-detection overrides an explicit auth_token or headers argument.
1. M2M (machine-to-machine) — for automation and batch jobs¶
M2M credentials live in AWS Secrets Manager and are fetched at process start using the runtime's IAM role. The secret value must be JSON:
{
"client_id": "your_client_id",
"client_secret": "your_client_secret"
}
Grant your runtime IAM role secretsmanager:GetSecretValue on the secret ARN, then set these env vars and the client configures itself automatically. The AWS region is derived automatically from the secret ARN, so AWS_DEFAULT_REGION does not need to be set for the M2M path.
export NZSHM22_TOSHI_API_URL=https://example-api-url.com/graphql
export NZSHM22_TOSHI_COGNITO_DOMAIN=https://toshi-auth.example.auth.ap-southeast-2.amazoncognito.com
export NZSHM22_TOSHI_M2M_SECRET_ARN=arn:aws:secretsmanager:ap-southeast-2:123456789012:secret:toshi-m2m-AbCdEf
from nshm_toshi_client import ToshiFile
api = ToshiFile(
"https://example-api-url.com/graphql",
"https://example-s3-url.com",
)
file = api.get_file("{example_id}")
The Secrets Manager fetch happens once when ToshiTokenManager is constructed (either explicitly, or implicitly by ToshiClientBase when auto-detecting); Cognito access tokens are refreshed transparently from there. Long-running jobs (24h+) never need to manage token lifetime.
You can also pass a ToshiTokenManager explicitly:
from nshm_toshi_client.auth import ToshiTokenManager
from nshm_toshi_client import ToshiFile
mgr = ToshiTokenManager(
cognito_domain="https://toshi-auth.example.auth.ap-southeast-2.amazoncognito.com",
secret_arn="arn:aws:secretsmanager:ap-southeast-2:123456789012:secret:toshi-m2m-AbCdEf",
# region= is optional; derived from the ARN when not given
)
api = ToshiFile(
"https://example-api-url.com/graphql",
"https://example-s3-url.com",
token_manager=mgr,
)
2. Interactive credentials — for scientists¶
Install the CLI:
pip install nshm-toshi-client[cli]
Then give the CLI the Cognito pool details. The simplest way is a JSON config file — copy docs/auth_config.example.json to ~/.toshi/auth_config.json and fill in the values for your Toshi deployment (ask the Toshi admin if you don't have them):
mkdir -p ~/.toshi
cp auth_config.example.json ~/.toshi/auth_config.json
# then edit ~/.toshi/auth_config.json
Log in (you'll be prompted for email + password):
toshi-auth login
This saves tokens to ~/.toshi/credentials. Both toshi-auth and ToshiClientBase read the same ~/.toshi/auth_config.json, so no extra env vars are needed for Cognito auth. You still need the API URL:
export NZSHM22_TOSHI_API_URL=https://example-api-url.com/graphql
The client detects ~/.toshi/credentials and refreshes tokens automatically:
from nshm_toshi_client import ToshiFile
api = ToshiFile(
"https://example-api-url.com/graphql",
"https://example-s3-url.com",
)
file = api.get_file("{example_id}")
Alternative: instead of
~/.toshi/auth_config.json, you can set theNZSHM22_TOSHI_COGNITO_*env vars (domain, scientist client ID, region, user pool ID). Env vars take precedence over the file on a per-key basis, so you can mix the two (e.g. file for shared defaults, env to overrideNZSHM22_TOSHI_COGNITO_DOMAINin a dev pool).
3. API key (legacy)¶
export NZSHM22_TOSHI_API_URL=https://example-api-url.com/graphql
export NZSHM22_TOSHI_API_KEY=your-api-key
export NZSHM22_TOSHI_S3_URL=https://example-s3-url.com
from nshm_toshi_client import ToshiFile, API_URL, API_KEY, S3_URL, get_auth_kwargs
headers = {"x-api-key": API_KEY}
api = ToshiFile(API_URL, S3_URL, None, **get_auth_kwargs())
file = api.get_file("{example_id}")
The helper function get_auth_kwargs() returns the correct constructor keyword arguments for whichever auth mode is active, so callers can initialise the client without branching. When a legacy API key resolved (via env var or Secrets Manager), get_auth_kwargs() returns {'headers': {'x-api-key': API_KEY}}. Otherwise it returns {}, letting ToshiClientBase fall through to Cognito auto-detection (M2M or interactive credentials, as described above).
nshm_toshi_client.API_KEY provides a universal way for applications to define the API key. For local machine usage it is obtained from the NZSHM22_TOSHI_API_KEY environment variable. When NZSHM22_TOSHI_API_KEY is not set and the process is running inside an AWS Batch job (AWS_BATCH_JOB_ID is set), the legacy API key can be sourced automatically from AWS Secrets Manager. The secret is chosen from the API URL — URLs containing TEST use NZSHM22_TOSHI_API_SECRET_TEST; URLs containing PROD use NZSHM22_TOSHI_API_SECRET_PROD. Grant the task's IAM role secretsmanager:GetSecretValue on the relevant secret ARN; no extra env vars are needed. If NZSHM22_TOSHI_M2M_SECRET_ARN and NZSHM22_TOSHI_COGNITO_DOMAIN are both set, the Secrets Manager fetch is skipped entirely and the client uses Cognito M2M auth instead — set those env vars in the Batch task definition to migrate to Cognito auth without touching code.
Scopes¶
Toshi API authorisation is gated by OAuth scopes defined as a Resource Server in the Cognito user pool. The naming convention is {resource_server}/{scope} — e.g. toshi/read, toshi/write. Scopes themselves are deployment configuration; this client only requests them.
You can see what scopes the token you currently hold actually has:
toshi-auth whoami
Where scopes come from¶
| Auth method | How scopes are chosen |
|---|---|
M2M (client_credentials grant) |
Client explicitly requests toshi/read toshi/write. This is hardcoded at nshm_toshi_client/auth.py:72. The automation app client in Cognito must be configured to permit those scopes, or the token request fails with invalid_scope. |
Scientist (USER_PASSWORD_AUTH) |
Client does not request scopes. Cognito returns whatever the scientist app client is configured to grant by default (set per deployment under App clients → Allowed custom scopes). |
| Legacy API key | Not a Cognito token — scopes don't apply; access is gated by the API key itself on the server side. |
Testing scope behaviour¶
When verifying scope policy against a deployment:
- A token with only
toshi/readshould be rejected by the API on any write mutation. - A token with no
toshi/*scope should be rejected on every call. - An M2M token request will fail at Cognito (not the API) if the automation app client isn't permitted the hardcoded scopes — look for
invalid_scopein the Cognito response. - Scientist scope changes don't require a code release — but a user already holding a token must
toshi-auth logout && toshi-auth loginto pick up the new scopes.
To find the authoritative scope definition: AWS Console → Cognito → your user pool → App integration → Resource servers (defines available scopes) and App clients → Allowed custom scopes (grants scopes per client).
AWS credentials (Identity Pool federation)¶
toshi-auth login writes JWT tokens to ~/.toshi/credentials. The same login produces two distinct credential types depending on how you consume them:
| Path | Credential type | Stored where | Consumer |
|---|---|---|---|
ToshiClientBase (auto-detected) |
JWT Bearer token (access_token) |
~/.toshi/credentials |
Authorization: Bearer header on GraphQL calls to the Toshi API |
get_aws_session() (programmatic) |
AWS STS credentials (in-memory boto3.Session) |
not persisted | direct AWS service calls in Python (S3, Batch, …) |
toshi-auth aws-creds (CLI) |
AWS STS credentials | ~/.aws/credentials ([toshi] profile) |
aws CLI / boto3 default credential chain |
ToshiClientBase never reads ~/.aws/credentials — it uses only the JWT path above.
In-process Python: get_aws_session()¶
When you need to call AWS services from Python directly, use nshm_toshi_client.aws.get_aws_session(). It exchanges the id_token from ~/.toshi/credentials for STS credentials via Cognito Identity Pool federation and returns a ready-to-use boto3.Session:
from nshm_toshi_client.aws import get_aws_session, CognitoAuthError
try:
session = get_aws_session()
s3 = session.client('s3')
print(s3.list_buckets())
except CognitoAuthError as exc:
# Not logged in, token refresh failed, config incomplete, etc.
raise SystemExit(f"Run: toshi-auth login ({exc})") from exc
Typed exceptions (NoCredentialsError, RefreshFailedError, ConfigIncompleteError, IdentityPoolError) all inherit from CognitoAuthError, so you can catch the base or react to specific failures.
Out-of-process tools: toshi-auth aws-creds¶
For the aws CLI, scripts that rely on AWS_PROFILE, or any tool that uses the boto3 default credential chain, write the STS credentials to ~/.aws/credentials once and let those tools pick them up:
toshi-auth aws-creds --profile toshi
# → writes [toshi] section to ~/.aws/credentials
aws --profile toshi s3 ls
AWS_PROFILE=toshi python my_script.py
When to use which¶
Use get_aws_session() when your Python process needs AWS access — no file round-trip, typed exceptions, and token refresh handled automatically. Use toshi-auth aws-creds when you need credentials available to the aws CLI or other tools outside your Python process.
toshi-auth CLI¶
Install with pip install nshm-toshi-client[cli].
The CLI reads config from ~/.toshi/auth_config.json, or TOSHI_COGNITO_CONFIG env var pointing to a config file, or falls back to NZSHM22_TOSHI_COGNITO_* env vars.
| Command | Description |
|---|---|
toshi-auth login |
Username/password login, saves tokens to ~/.toshi/credentials |
toshi-auth logout |
Delete saved credentials at ~/.toshi/credentials |
toshi-auth token [--raw] |
Print current Bearer token, auto-refreshing if expired |
toshi-auth whoami |
Decode and display JWT claims (user, scopes, expiry) |
toshi-auth aws-creds [--profile] |
Exchange Cognito token for AWS STS credentials, written to ~/.aws/credentials |
Methods¶
ToshiFile.get_file¶
from nshm_toshi_client import ToshiFile
api = ToshiFile(API_URL, S3_URL)
file = api.get_file("{example_id}")
Example response:
{'__typename': 'InversionSolutionNrml',
'id': 'SW52ZXJzaW9uU29sdXRpb25Ocm1sOjEwMDM0Mw==',
'file_name': 'NZSHM22_InversionSolution-QXV0b21hdGlvblRhc2s6MTAwMTA4_nrml.zip',
'file_size': 3331426,
'meta': {'mykey': 'myvalue', 'mykey2': 'myothervalue'}
}
ToshiFile.get_file_download_url¶
file = api.get_file_download_url("{example_id}", True)
Example response:
{'__typename': 'InversionSolutionNrml',
'id': 'SW52ZXJzaW9uU29sdXRpb25Ocm1sOjEwMDM0Mw==',
'file_name': 'NZSHM22_InversionSolution-QXV0b21hdGlvblRhc2s6MTAwMTA4_nrml.zip',
'file_size': 3331426,
'meta': {'mykey': 'myvalue', 'mykey2': 'myothervalue'},
'file_url': 'https://s3.amazonaws.com/toshi-files/NZSHM22_InversionSolution-QXV0b21hdGlvblRhc2s6MTAwMTA4_nrml.zip'
}
ToshiFile.download_file¶
api.download_file("{example_id}", "/tmp/downloads")
If successful, prints:
saving to /tmp/downloads/{example_id}
If failure, prints:
Download failed: status code {status_code} (eg. 404)