Skip to content

Search is only available in production builds. Try building and previewing the site to test it out locally.

Overview

Every call to the platform API carries an access token. A token belongs either to a person or to a service principal. Both are checked against the same roles, and both show up in the audit log.

A person signs in through the browser, at the sign-in page of their tenant: password, authenticator, passkey, or a social account, whatever the tenant allows. The console and the CLI both work this way. The CLI opens your browser and receives the session back. It never sees your password.

TokenLastsNotes
Access token15 minutesSent with every call
Refresh token30 daysReplaced on every use. A reused one ends the session

A service principal is an identity for automation: a deploy pipeline, a script, a scheduled job. It is an app registration of the kind “Service principal (API access)”. It cannot sign a person in, and a sign-in app’s secret can never reach the platform API.

Create one in the console under IAM → App registrations → New application. Pick the kind Service principal (API access) and give it a name. The name appears in the audit log as app:<client id>.

A principal can prove who it is in two ways:

  • Federation, no secret. It trusts a token another system issues to its workload, for example the token every GitHub Actions run can ask for. See GitHub Actions. Prefer this. There is nothing to leak or rotate.
  • Client secret. Shown once, when the principal is created. Keep it in a secret store. Use it where there is no token to federate.

Either way, the principal asks the tenant’s token endpoint for a platform token, with grant_type=client_credentials and scope=platform:

Terminal window
curl -s https://api.m12p.com/iam/$TENANT/token \
-u "$CLIENT_ID:$CLIENT_SECRET" \
-d grant_type=client_credentials -d scope=platform

The token lasts 15 minutes and has no refresh token. Ask again when it expires. The CLI does that for you:

Terminal window
export PLATFORM_REST=https://api.m12p.com
export PLATFORM_TENANT=tnt_...
export PLATFORM_CLIENT_ID=client_...
export PLATFORM_CLIENT_SECRET=... # from your secret store
platform service deploy service_... --wait

On your own machine, platform login --client-id <id> --tenant <tenant> --client-secret-stdin stores the secret in ~/.platform/config.json, which only you can read. platform logout removes it.

A principal starts with no access. Open it and use Access to give it a role at one scope:

ScopeCovers
The whole tenantEvery app and service in it
An appThat app, its child apps, and their services
One serviceOnly that service
RoleAllows
ViewerReading
EditorCreating, changing, deploying, and deleting in the scope
AdminEverything in the tenant, including its directory

Give the smallest scope that works. A deploy pipeline needs Editor on its own service and nothing else. A principal can never be a platform administrator.

Access is checked on every call. Removing a role or deleting the principal stops it at the next request, even with a token that has not expired.

M12PAWSAzureGoogle Cloud
PersonUser in the tenant directoryIAM Identity Center userEntra ID userGoogle account
AutomationService principalIAM roleService principal or managed identityService account
Long-lived secretClient secretAccess keyClient secret or certificateService account key
Federation from GitHubFederated credential on the principalOIDC identity provider and a role trust policyFederated identity credentialWorkload identity federation
Subject matchExactConditions, wildcards allowedExactAttribute conditions
Where a grant appliesTenant, app, or one serviceResource policiesManagement group, subscription, resource group, resourceOrganization, folder, project, resource

The M12P model is closest to Azure. A service principal with a federated credential is the same shape as an Entra app registration with a federated identity credential, and the token request is the same form.