skip to content

In a token minted by an RFC 8693 exchange, what does an `act` claim tell a resource server that its absence does not?

level: seniorimportance: must knowfreq 48%

answer

  1. the difference is written in a claim
  2. sub stays, act is added
  3. no act means no trace of the actor
  4. outermost act is the current actor
  5. may_act is permission, act is record

basics

~20 s

An act claim names the party currently acting for the token's subject, so a resource server sees both identities: delegation. Without it the token speaks only as the subject and the acting party leaves no trace: impersonation.

solid answer

~40 s

With `act`, the token is a delegation token: `sub` is still the original subject and `act` names the party acting for them, so both are visible to whatever reads it. Without `act`, it is an impersonation token — the receiver sees the subject and has no way to tell that a service, rather than the subject, is holding the credential. `act` nests to record a chain: the outermost names the current actor and the least recent actor is the most deeply nested. It identifies, it does not authorise — what the token may do is still its `scope` and its audience. The related `may_act` claim runs the other way: who is *permitted* to act for a subject, stated in advance.

code

json · 13 lines
json
{
  "iss": "https://authorization-server.example",
  "sub": "donor-8842",
  "aud": "https://messaging.example",
  "exp": 1758312000,
  "scope": "reminders:send",
  "act": {
    "sub": "reminder-service",
    "act": {
      "sub": "scheduler-service"
    }
  }
}

go deeper

for a junior

Recall the one-line difference: act present means the acting party is named in the token, act absent means the token speaks only as the subject and nobody can tell who called.

for a middle

Explain the nesting direction — outermost is the current actor, deepest is the earliest — and that sub stays the original subject all the way along the chain.

for a senior

Show you can use it in an incident: a token carrying act tells you which service called, while one without it forces you out into logs to answer the same question.

for a principal

The bet is whether every hop mints a delegated token, paying a round trip and a growing claim set, or whether some chains run on impersonation and accept a thinner audit trail.

## Two shapes of token come out of the same grant `RFC 8693` separates two outcomes that look identical on the wire until you read the claims. - **Impersonation.** The issued token says the subject and nothing more. A resource server receiving it sees the donor; it cannot tell whether the donor's own application or some background service is holding the credential. The acting party has disappeared from the record. - **Delegation.** The issued token still says the subject in `sub`, and adds an **`act`** claim naming the party that is acting. Both are visible, at once, to anything that reads the token. The distinction is not a mode the authorization server is in. It is a property of the token that was minted, and `act` is the only thing that carries it. ## What `act` contains `act` is a JSON object whose members identify the current actor — in practice a `sub`, and often an `iss` as well when the actor comes from a different issuer than the subject. It sits alongside the top-level claims rather than inside them: | claim | says | |---|---| | `sub` | who the token is about — unchanged by delegation | | `act` | who is currently acting for that subject | | `act.act` | the actor before them, and so on inward | | `may_act` | who is *permitted* to act for the subject — a permission, not a record | ## Chains of actors A token may pass through more than one hop, and `act` nests to record it. The rule to memorise is the direction: 1. The **outermost** `act` names the **current** actor — the party presenting this token now. 2. Each `act` nested inside names the actor **before** that one. 3. The **least recent** actor is therefore the **most deeply nested**. Reading from the outside inwards gives the chain in reverse chronological order. Getting this backwards is the commonest error on the claim, and it is the kind of error that survives review, because a nested object reads plausibly either way. ## What `act` does and does not prove Being precise here matters, because `act` is identification and it is tempting to read it as authorisation. - It **does** tell a resource server which party is holding the credential, which is what makes an audit trail meaningful: a log line can record the subject and the actor separately. - It **does** let a resource server apply extra policy — refusing a sensitive operation, say, when the caller is a background service rather than the subject's own session. - It **does not** grant the actor's own permissions. What the token may do is still whatever the authorization server put in its `scope` and its audience. - It **does not** appear on every exchanged token. Impersonation is an intended outcome of the grant, not a malfunction, and plenty of exchanges produce a token with no `act` at all. - Its absence **does not** prove nobody was acting — only that the token does not say so. ## `may_act`: permission written down in advance `may_act` is the other side of the same coin, and it lives in a different place. It is a claim in a token stating **which party is authorized to become the actor** for that token's subject. Where `act` is a record of what happened, `may_act` is an authorisation issued ahead of time, and an authorization server can consult it when deciding whether to honour an exchange at all. That gives a deployment somewhere to put the answer to "which of our services may act for a donor?" other than a hard-coded list inside the authorization server: the answer can travel in the subject's own token. Its practical effect still depends on the server's policy — the claim expresses eligibility, it does not compel anyone to honour or to require it. ## Reading a token in production When a downstream service reports "this call looks like the donor, but the donor was asleep", the first thing to read is whether the presented token carries `act`. If it does, the chain names every service that handled the token and the investigation is short: the subject is in `sub`, the caller is in the outermost `act`, and anything before it is nested inside. If it does not, the token is indistinguishable from one the donor's own application would present, and the question of who actually called can only be answered from logs outside the token. That is the real, and often underestimated, cost of choosing impersonation.

  • Where does `may_act` live, and how does it differ from `act`?
    `may_act` sits in a token to say which party is permitted to become the actor for that token's subject — permission granted ahead of time. `act` sits in an issued token to say who the actor actually is. One is an authorisation, the other a record, and a server may consult the first when deciding whether to mint the second.
  • How is a chain of three actors expressed in one token?
    By nesting. The token's `sub` is the original subject; its `act` names the current actor, and that object's own `act` names the one before it. The least recent actor is the most deeply nested, so reading outward gives the order in which the token changed hands.
  • Does an `act` claim mean a resource server should apply the actor's own permissions?
    No. `act` identifies, it does not authorise. What the token may do is still whatever the authorization server placed in its `scope` and audience. A resource server may use `act` to apply extra policy of its own, or to log who really called, but the claim itself grants nothing.

A parcel signed for by a neighbour: the card records both who it was addressed to and who actually took it. Impersonation is the neighbour signing the addressee's name instead, leaving nothing to show anyone else was involved.

saying these in an interview costs you the question

  • Treats impersonation and delegation as two words for one thing.
  • Reads the innermost nested act as the current actor.
  • Thinks act grants the actor's own permissions to the call.
  • Confuses may_act, a permission, with act, a record of who acted.
  • Assumes every exchanged token carries an act claim.