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

Image

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 ID
  • http://skalio.com/skp: TeamBeam Transfer
  • http://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.

Image

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.
  • uid identifies the PAT. It is not secret.
  • secret is 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:

  1. Exchange the PAT for an ID token once, and keep the ID token.
  2. Use the ID token to obtain access tokens, and renew access tokens as they expire.
  3. 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:

  1. Create a PAT with a descriptive name and only the required audiences.
  2. Replace the login by the exchange of the PAT. The rest of the script remains unchanged.
  3. Cache the ID token, as described above.
  4. 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.

Image

Properties#

  • No secret: the grant is referenced by its uid. Redeeming it requires the service credential of the service it is bound to, so the uid is 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 the http://skalio.com/purpose claim.
  • 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_ref claim. 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 uid to a different service achieves nothing.
  • Subject binding: before a service attaches a grant to anything, it must verify that the subject of 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 list and grant revoke shell 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 create registers a new credential. The secret is shown once.
  • service credential list lists the credentials, including when each was last used.
  • service credential delete removes 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:

  1. Register a new credential with service credential create.
  2. Update the configuration of every instance of the service and reload them, one after another. Both credentials are valid during this time.
  3. Use service credential list to verify that the old credential is no longer used: its last-used timestamp has stopped advancing.
  4. 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.