Non-interactive authentication#
Some callers need to authenticate while nobody is at the keyboard: a script that synchronizes files every night, or one of our own backend services acting on behalf of a person. Without dedicated support, such a caller has to store the person's password and the seed of their second factor. That credential carries the person's full privileges, never expires, cannot be audited, and can only be revoked by changing the password, which breaks every other use of it.
Skalio ID offers three credentials for these callers instead:
| Personal API token | Delegation grant | Service credential | |
|---|---|---|---|
| For | automation on infrastructure we don't control | our own services, acting for a person | our own services, acting as themselves |
| Is | an opaque secret | a consent record, no secret | an opaque secret |
| Held by | the customer's script | nobody: it is referenced, not held | the configuration of the service |
| Created by | the person, with a second factor | the person's client, silently | an operator, once per environment |
| Visible to | the person and their organization admin | nobody | operators |
| Ends by | expiry or revocation | revocation only | deletion by an operator |
Description#
Exchange, never use directly#
No backend service accepts these credentials directly. A caller exchanges them at Skalio ID for a freshly minted ID token, and then uses that ID token exactly like one obtained from an interactive login: to request access tokens from backend services.
An ID token encodes name, email address, privileges and organization membership of the person, all of which may change. Because every exchange mints a new, short-lived ID token, such changes take effect within a day. Backend services verify these ID tokens like any other, and need no additional logic.
Issued ID tokens#
ID tokens minted from a personal API token or a delegation grant differ from interactively obtained ones:
| Interactive login | Personal API token | Delegation grant | |
|---|---|---|---|
| Lifetime | 30 days | 24 hours | 24 hours |
aud |
all services | audiences of the PAT | audiences of the grant |
amr |
pwd, plus mfa if a second factor was used |
http://skalio.com/pat, plus mfa if a second factor was used to create the PAT |
http://skalio.com/grant, never mfa |
| Additional claims | none | none | http://skalio.com/purpose, http://skalio.com/target_ref |
| Revoked by | logout | revoking the PAT | revoking the grant |
Access tokens that Skalio ID issues for such an ID token carry the same aud, amr, http://skalio.com/purpose and http://skalio.com/target_ref claims.
An ID token minted from a personal API token or a delegation grant never turns into an interactive session. Requests to upgrade the token, leave an organization, accept an invitation, confirm an email address or verify an authenticator are rejected. So are requests to manage personal API tokens or create delegation grants: otherwise, a leaked personal API token could be used to create a new one, which would survive the revocation of the first.
Audience scope#
Every ID token carries an explicit aud claim, listing the backend services it may be used with:
http://skalio.com/id: Skalio IDhttp://skalio.com/skp: TeamBeam Transferhttp://skalio.com/drive: TeamBeam Drive
A backend service accepts a token only if the claim lists its own audience. An interactive login lists all services. A personal API token or a delegation grant lists the audiences chosen at its creation, or all services if none were chosen.
| Minted from | aud |
Skalio ID | TeamBeam Transfer | TeamBeam Drive |
|---|---|---|---|---|
| interactive login | id, skp, drive |
yes | yes | yes |
| personal API token or grant, scoped to Drive | drive |
no | no | yes |
| personal API token or grant, without restriction | id, skp, drive |
yes | yes | yes |
The audience scope is the most important protection against a leaked credential: it can only act as the person against the audiences it names. Scoping a credential narrowly is therefore recommended.
Revocation#
Every ID token minted from a personal API token or a delegation grant records its origin. Revoking the credential blacklists every ID token minted from it, so access ends immediately rather than when the tokens expire.
Each credential also records when it was last used. Since the record of individual ID tokens is removed shortly after they expire, this is the long-term signal whether a credential is still in use.
Personal API tokens#
A personal API token (PAT) authenticates automation that runs on infrastructure Skalio does not control: customer scripts, or tools such as a sync client.
Format#
A PAT has the format tbapi_<uid>_<secret>:
tbapi_is a fixed prefix. It makes the token recognizable, so leaked tokens can be found in logs, repositories and support tickets, for example by secret-scanning tools.uididentifies the PAT. It is not secret.secretis a random string of 50 characters (more than 256 bits of entropy). Skalio ID stores only its SHA-256 hash.
Creating a personal API token#
Every person can create personal API tokens. There is no role requirement and no quota: one PAT per script, each with a meaningful name, is the intended use.
- Name: a label of the person's choice, to recognize the PAT later.
- Audiences: the backend services that the minted ID tokens may be used with. If omitted, all services.
- Validity: up to 365 days, which is also the default. An organization can cap the maximum validity lower (
apiTokenMaxValidityDays). A change of the cap applies to new PATs only. - Second factor: if the person uses multi-factor authentication, they must provide a current one-time code of their authenticator app. Recovery codes are not accepted. A PAT bypasses the second factor for as long as a script holds it, so creating one costs one challenge.
The response contains the secret token. It is shown only once and cannot be retrieved again. The person is notified by email whenever a PAT is created.
A PAT records whether a second factor was provided at its creation. Only then do the minted ID tokens claim mfa. Enabling multi-factor authentication later does not upgrade an existing PAT.
Using a personal API token#
The script exchanges the PAT for an ID token, presenting it in the Authorization header:
curl -X POST \
-H "Authorization: tbapi_fzrr728zxxz82sp2gy9w_3dy6aqalqvs9nx1r46hrlnq12p5b1di7d1uwdrupp28su68wgo" \
https://free.teambeam.de/api/id/v3/auth/exchange
With the ID token, the script requests access tokens from backend services, as described in authentication.
Exchanges are rate-limited per PAT: after a burst of 3, one exchange per 15 seconds. A script must therefore cache the ID token:
- Exchange the PAT for an ID token once, and keep the ID token.
- Use the ID token to obtain access tokens, and renew access tokens as they expire.
- Exchange the PAT again only when the ID token has expired or has been rejected.
All failures of an exchange are reported identically, whether the PAT is unknown, expired, revoked, or the person has been removed. The endpoint does not reveal which PATs exist.
Lifecycle#
- Listing: the person can list their PATs, with name, audiences, and the timestamps of creation, expiration and last use. The secret is never shown again. Expired and revoked PATs are not listed.
- Expiry: the person is warned by email 21, 7 and 3 days before a PAT expires, and notified when it has expired. Consuming services are not notified. Expiry does not blacklist ID tokens already minted; they run out within 24 hours.
- Renewal: a PAT cannot be renewed or rotated. Create a new PAT, deploy it, then revoke the old one.
- Revocation: the person can revoke a PAT at any time. It is unusable at once, every ID token minted from it is blacklisted, and the person is notified by email.
- Automatic revocation: all PATs of a person are removed when the person is removed or leaves their organization. Changing the password, resetting the second factor, or changing roles does not revoke a PAT. Privileges are encoded in the short-lived ID token and are evaluated at every exchange.
An organization admin can list and revoke the PATs of members. An admin cannot create a PAT for a member: that would allow them to act as that person.
Storing a personal API token#
A script has to present its PAT, so it must be able to read it in clear text. Skalio ID imposes no requirements on how it is stored. Treat a PAT like a password:
- Keep it in a secret store, an environment variable or a file only the script can read. Never commit it into a repository.
- Scope it to the audiences the script actually needs.
- Revoke it immediately if it may have leaked.
Migrating from password and second factor#
Scripts that log in with password and TOTP seed keep working. To migrate them:
- Create a PAT with a descriptive name and only the required audiences.
- Replace the login by the exchange of the PAT. The rest of the script remains unchanged.
- Cache the ID token, as described above.
- Remove the password and the TOTP seed from the script.
Delegation grants#
A delegation grant lets one of our own backend services act on behalf of a person, for example when TeamBeam Transfer copies incoming mailbox transfers into a TeamBeam Drive Space. The grant is a consent record, not a credential:
Person P has authorized service S to mint ID tokens scoped to audiences A, for purpose X, optionally limited to target Y.
Properties#
- No secret: the grant is referenced by its
uid. Redeeming it requires the service credential of the service it is bound to, so theuidis useless to anybody else and may safely pass through the client. - Created by the person only: a grant is created by a request authenticated with an access token of an interactive login, typically by the client while the person enables a feature. A service credential can never create a grant: a service must not be able to produce consent on its own. Neither can a token minted from a personal API token or a grant.
- No second factor: creating a grant hands out no secret, so it requires no second factor. ID tokens minted from a grant never claim
mfa. Whether that suffices for a feature that requires multi-factor authentication, such as secure Spaces, is up to the consuming service. - One per purpose: a person holds at most one grant per consumer and purpose. Creating it again updates the existing grant, keeping its
uid. - Purpose: the purpose is one of a fixed list (
GrantPurpose), defined by Skalio ID. Every ID token minted from a grant names it in thehttp://skalio.com/purposeclaim. - Target: a grant can be limited to a target, such as the uid of a Drive Space. Skalio ID does not interpret it, but carries it into the
http://skalio.com/target_refclaim. Enforcing it is up to the consuming service. - No expiry: a grant ends only when it is revoked. The organization's cap on validity does not apply.
- Invisible: neither the person nor their organization admin sees grants. The feature that created the grant is its control surface: switching the feature off revokes the grant.
Bindings#
Two checks protect a grant from misuse:
- Consumer binding: a grant names the service that may use it. Skalio ID compares the presented service credential with it, and rejects every other service. Handing the
uidto a different service achieves nothing. - Subject binding: before a service attaches a grant to anything, it must verify that the
subjectof the grant is the person it is acting for.
Verify the subject
A consuming service that skips the subject check can be tricked into attaching the grant of another person to the caller's own data, and then acting under that other person's identity.
Using a delegation grant#
The consuming service stores the uid of the grant together with the feature setting. Whenever it needs to act on behalf of the person, it mints an ID token from the grant, authenticating with its service credential, and uses it to request access tokens from the target service.
Revoking a delegation grant#
- When the person disables the feature, the consuming service must revoke the grant. Every ID token minted from it is blacklisted.
- All grants of a person are removed when the person is removed or leaves their organization.
- Operators can list and revoke grants with the
grant listandgrant revokeshell commands. This is intended for support and incidents, for example when the feature was removed without the consuming service revoking the grant. Skalio ID cannot detect this on its own.
Service credentials#
A service credential authenticates one of our own backend services towards Skalio ID. It identifies a service, never a person. On its own, it authorizes nothing: it can neither create a grant nor mint an ID token. It is required to verify, redeem and revoke delegation grants.
A service credential has the format tbsvc_<uid>_<secret>, analogous to a personal API token.
Registration#
An operator registers service credentials using the Skalio ID shell. There is no API for it.
service credential createregisters a new credential. The secret is shown once.service credential listlists the credentials, including when each was last used.service credential deleteremoves a credential. It takes effect immediately.
A separate credential is registered for each service and environment, so a leaked credential of a development environment is worthless in production.
The credential belongs in the configuration of the service, not in its database. Grant uids are stored in the database: keeping the credential elsewhere means that a dump of the database alone does not allow minting ID tokens.
Rotation#
A service uses one credential at a time, but Skalio ID accepts several credentials for the same service. This allows rotating the credential without downtime:
- Register a new credential with
service credential create. - Update the configuration of every instance of the service and reload them, one after another. Both credentials are valid during this time.
- Use
service credential listto verify that the old credential is no longer used: its last-used timestamp has stopped advancing. - Remove the old credential with
service credential delete.
If a credential has been compromised, delete it immediately, and accept that the service fails until the new credential has been rolled out.
Limitations#
The following is currently not supported:
- Restricting the privileges of a personal API token or a grant below those of the person.
- Scopes finer than the audience, such as individual endpoints or objects. The target of a grant is advisory.
- Accounts for machines that act as a person.
- Authorizing third-party applications, such as the OAuth client credentials flow.