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.
People
Section titled “People”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.
| Token | Lasts | Notes |
|---|---|---|
| Access token | 15 minutes | Sent with every call |
| Refresh token | 30 days | Replaced on every use. A reused one ends the session |
Service principals
Section titled “Service principals”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:
curl -s https://api.m12p.com/iam/$TENANT/token \ -u "$CLIENT_ID:$CLIENT_SECRET" \ -d grant_type=client_credentials -d scope=platformThe token lasts 15 minutes and has no refresh token. Ask again when it expires. The CLI does that for you:
export PLATFORM_REST=https://api.m12p.comexport PLATFORM_TENANT=tnt_...export PLATFORM_CLIENT_ID=client_...export PLATFORM_CLIENT_SECRET=... # from your secret storeplatform service deploy service_... --waitOn 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.
Roles and scopes
Section titled “Roles and scopes”A principal starts with no access. Open it and use Access to give it a role at one scope:
| Scope | Covers |
|---|---|
| The whole tenant | Every app and service in it |
| An app | That app, its child apps, and their services |
| One service | Only that service |
| Role | Allows |
|---|---|
| Viewer | Reading |
| Editor | Creating, changing, deploying, and deleting in the scope |
| Admin | Everything 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.
Compared with other clouds
Section titled “Compared with other clouds”| M12P | AWS | Azure | Google Cloud | |
|---|---|---|---|---|
| Person | User in the tenant directory | IAM Identity Center user | Entra ID user | Google account |
| Automation | Service principal | IAM role | Service principal or managed identity | Service account |
| Long-lived secret | Client secret | Access key | Client secret or certificate | Service account key |
| Federation from GitHub | Federated credential on the principal | OIDC identity provider and a role trust policy | Federated identity credential | Workload identity federation |
| Subject match | Exact | Conditions, wildcards allowed | Exact | Attribute conditions |
| Where a grant applies | Tenant, app, or one service | Resource policies | Management group, subscription, resource group, resource | Organization, 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.