Hacker DirectoryHacker Directory

WEB / WEB-15.01

OAuth actors, code flow and scopes

Mina lets Atlas Exporter read selected notes. Separate OAuth roles, code exchange and the API’s own decision through this one delegation.

CORE · RED · BLUE · ARTIFACT · PRACTICE

1. Core · Mechanism

Read every scenario without motion

Read allowed

  1. After callback

    The scene starts after an accepted callback; browser redirects and owner authentication are not drawn in this segment.

    Client
    Code alias held; callback bound
    Authorization server
    Code valid; no token
    Resource server
    No request yet
  2. Exchange ready

    The client prepares the code, redirect and selected verifier context; preparation is a local operation.

    Client
    Token request ready; code not sent to API
    Authorization server
    Code valid; no token
    Resource server
    No request yet
  3. AS received request

    The token request arrived at the AS; client, code, redirect and PKCE checks are not yet complete.

    Client
    Token request ready; code not sent to API
    Authorization server
    Token request received; gate pending
    Resource server
    No request yet
  4. AS gate passed

    The selected code and binding gates passed; code-consumed and token-response-ready state are created locally at the AS.

    Client
    Token request ready; code not sent to API
    Authorization server
    Code consumed; AT+optional RT ready
    Resource server
    No request yet
  5. Token response arrived

    The client received access and optional refresh aliases; this receipt is not yet Notes API permission.

    Client
    AT alias held; no API gate yet
    Authorization server
    Code consumed; AT+optional RT ready
    Resource server
    No request yet
  6. Action selected

    The client prepares a read request to notes-api using access-A. The token audience remains notes-api and its granted scope remains notes:read; the recipient makes its own acceptance decision.

    Client
    notes-api read request ready
    Authorization server
    Code consumed; AT+optional RT ready
    Resource server
    No request yet
  7. API received request

    The request arrived at notes-api. The recipient now holds the access-A context, but audience, time, scope and object conditions have not yet been evaluated.

    Client
    notes-api read request ready
    Authorization server
    Code consumed; AT+optional RT ready
    Resource server
    notes-api received request; policy pending
  8. API decision

    The audience matches notes-api and now=1050 is before expiresAt=2000. The notes:read scope and selected tenant/owner relationship allow the read. This is an allow decision; the scene neither reads note bytes nor sends a body to the client.

    Client
    notes-api read request ready
    Authorization server
    Code consumed; AT+optional RT ready
    Resource server
    Read policy allow; bytes not read

Scope too narrow

  1. After callback

    The scene starts after an accepted callback; browser redirects and owner authentication are not drawn in this segment.

    Client
    Code alias held; callback bound
    Authorization server
    Code valid; no token
    Resource server
    No request yet
  2. Exchange ready

    The client prepares the code, redirect and selected verifier context; preparation is a local operation.

    Client
    Token request ready; code not sent to API
    Authorization server
    Code valid; no token
    Resource server
    No request yet
  3. AS received request

    The token request arrived at the AS; client, code, redirect and PKCE checks are not yet complete.

    Client
    Token request ready; code not sent to API
    Authorization server
    Token request received; gate pending
    Resource server
    No request yet
  4. AS gate passed

    The selected code and binding gates passed; code-consumed and token-response-ready state are created locally at the AS.

    Client
    Token request ready; code not sent to API
    Authorization server
    Code consumed; AT+optional RT ready
    Resource server
    No request yet
  5. Token response arrived

    The client received access and optional refresh aliases; this receipt is not yet Notes API permission.

    Client
    AT alias held; no API gate yet
    Authorization server
    Code consumed; AT+optional RT ready
    Resource server
    No request yet
  6. Action selected

    The client prepares a write request to notes-api using access-A. The token audience remains notes-api and its granted scope remains notes:read; the recipient makes its own acceptance decision.

    Client
    notes-api write request ready
    Authorization server
    Code consumed; AT+optional RT ready
    Resource server
    No request yet
  7. API received request

    The request arrived at notes-api. The recipient now holds the access-A context, but audience, time, scope and object conditions have not yet been evaluated.

    Client
    notes-api write request ready
    Authorization server
    Code consumed; AT+optional RT ready
    Resource server
    notes-api received request; policy pending
  8. API decision

    Audience and time are valid for notes-api, but the write request requires notes:write while the grant contains only notes:read. The result is scope-denied. No write occurs and no resource body is sent to the client in this branch.

    Client
    notes-api write request ready
    Authorization server
    Code consumed; AT+optional RT ready
    Resource server
    Write policy deny; read scope only

Wrong API

  1. After callback

    The scene starts after an accepted callback; browser redirects and owner authentication are not drawn in this segment.

    Client
    Code alias held; callback bound
    Authorization server
    Code valid; no token
    Resource server
    No request yet
  2. Exchange ready

    The client prepares the code, redirect and selected verifier context; preparation is a local operation.

    Client
    Token request ready; code not sent to API
    Authorization server
    Code valid; no token
    Resource server
    No request yet
  3. AS received request

    The token request arrived at the AS; client, code, redirect and PKCE checks are not yet complete.

    Client
    Token request ready; code not sent to API
    Authorization server
    Token request received; gate pending
    Resource server
    No request yet
  4. AS gate passed

    The selected code and binding gates passed; code-consumed and token-response-ready state are created locally at the AS.

    Client
    Token request ready; code not sent to API
    Authorization server
    Code consumed; AT+optional RT ready
    Resource server
    No request yet
  5. Token response arrived

    The client received access and optional refresh aliases; this receipt is not yet Notes API permission.

    Client
    AT alias held; no API gate yet
    Authorization server
    Code consumed; AT+optional RT ready
    Resource server
    No request yet
  6. Action selected

    The client prepares a read request to archive-api using access-A. The token audience remains notes-api and its granted scope remains notes:read; the recipient makes its own acceptance decision.

    Client
    archive-api read request ready
    Authorization server
    Code consumed; AT+optional RT ready
    Resource server
    No request yet
  7. API received request

    The request arrived at archive-api. The recipient now holds the access-A context, but audience, time, scope and object conditions have not yet been evaluated.

    Client
    archive-api read request ready
    Authorization server
    Code consumed; AT+optional RT ready
    Resource server
    archive-api received request; policy pending
  8. API decision

    The request recipient is archive-api while the access-A audience is notes-api. The recipient rejects this mismatch as audience-denied; positive scope or tenant/owner facts do not grant a different recipient permission. No body is produced and the client has not yet received this decision.

    Client
    archive-api read request ready
    Authorization server
    Code consumed; AT+optional RT ready
    Resource server
    archive-api: audience-denied; no body

Code expired

  1. Time boundary

    The accepted callback context holds the code; the selected clock has reached its exact expiration instant.

    Client
    Code alias held; now=1100
    Authorization server
    Code expiresAt=1100; unused
    Resource server
    No request
  2. Exchange ready

    The client prepares the same normal token request; code validity still belongs to the AS local gate.

    Client
    Normal token request ready
    Authorization server
    Code expiresAt=1100; unused
    Resource server
    No request
  3. AS receipt

    The token request arrived at the AS; receipt completes neither the expiry check nor token issuance.

    Client
    Normal token request ready
    Authorization server
    Request received; expiry pending
    Resource server
    No request
  4. Expiry denial

    The zero-leeway fixture rejects the code because now equals expiresAt; no access or refresh token is created.

    Client
    Normal token request ready
    Authorization server
    Code expired; error ready; no tokens
    Resource server
    No request
  5. Error receipt

    The client received the selected exchange error outcome; it does not continue to Notes API with a new credential.

    Client
    Exchange failed; no AT; no API call
    Authorization server
    Code expired; error ready; no tokens
    Resource server
    No request

ACTORS · STATE · DATA FLOW

Limited authority without sharing a password

Mina’s notes live at Notes API; Atlas Exporter is a separate application that wants to export them. Giving it Mina’s password would create authority and credential storage duties beyond that job. Delegated authorization organizes the transfer of selected access to a client. OAuth addresses API access; by itself it does not define a standard user identity or a shared login result across applications. Federation means accepting authentication information from another identity authority under a trust agreement; OIDC makes that distinction concrete in the next lesson.

The four roles are resource owner Mina, client Atlas Exporter, authorization server id.example.org and resource server Notes API. AS and RS can share an operator or process, but token issuance and resource enforcement remain distinct decisions. Client here means a server-side application, not the browser. The browser carries front-channel redirects; a confidential client’s credential is not embedded in browser code. A consent screen is not a mandatory visual step on every visit: AS policy can use an earlier approval.

A code is an intermediate credential

The client first creates a transaction for the selected AS, storing the redirect URI, requested scopes and browser-bound correlation. The browser visits the AS, which authenticates the owner and decides the grant. The callback carries a short-lived, single-use code. After tying that return to its own transaction, the client exchanges the code at the token endpoint. The code is not presented to the API. The AS checks its client, redirect URI, validity and use bindings; our selected S256 PKCE profile also requires the verifier to match the challenge bound to that code.

The animation shows the segment after callback acceptance: client-to-AS token request, the AS’s local gate, token response to the client and a normal resource request to the RS. Mina’s authentication, browser redirects and TLS handshakes are described in the artifact timeline but omitted from the scene. A token response arriving at the client does not mean the RS has read a note. Failure branches are declared policy outcomes of this normal flow, not measured provider errors.

The token’s recipient and the limit of scope

An access token is the credential used for access at the selected RS; OAuth does not require every access token to be a JWT. An opaque token’s audience and scope can come from the RS’s trusted validation context. The artifact shows these as readable policy fields rather than token bytes. If requested notes:read notes:write becomes a notes:read grant, the client accepts that reduction. Scope names are case-sensitive values defined by the AS; “read” is not the same permission in every application. The RS combines audience, time and scope checks with its own resource policy on every request.

A refresh token is for the token endpoint, not Notes API. It requests a new access token; it is neither an automatically endless session nor expanding authority. Issuance is optional, client binding remains, and refresh cannot request scopes outside the original grant. Current BCP requires sender constraint or rotation for public clients; a confidential client’s refresh token remains bound to its client. The fixture represents refresh through an alias and a narrowing matrix, without running rotation or issuing credentials.

2. Red · Trust & Impact

Read every scenario without motion

Foreign tenant

  1. The resource-action scope was selected

    Mina’s atlas-exporter client uses access-A. This access context carries a notes:read grant; an authorization code or refresh token is not treated as an RS credential.

    Access grant context
    case=foreign-tenant
    Resource-server policy
    RS policy not applied
    Resource boundary
    No resource grant
  2. Requested and granted scopes were separated

    The client requested notes:read and notes:write, but grantedScopes contains only notes:read. Requested scope is not permission granted by the consent/grant outcome.

    Access grant context
    Granted: notes:read
    Resource-server policy
    RS policy not applied
    Resource boundary
    No resource grant
  3. The recipient gate was evaluated

    Audience notes-api matches and requiredScope notes:read is granted. These positive conditions do not remove the object-relationship check for a foreign tenant.

    Access grant context
    Granted: notes:read
    Resource-server policy
    Audience match; scope present
    Resource boundary
    No resource grant
  4. The object condition remained separate from scope

    sameTenant=false while ownerMatches=true. Matching ownership cannot override tenant scope; a read-class grant does not grant access to every note.

    Access grant context
    Read grant scope
    Resource-server policy
    Tenant mismatch
    Resource boundary
    No resource grant
  5. The RS action decision remained negative

    The expected result is object-denied. A successful code exchange at another stage does not turn this resource decision into allow.

    Access grant context
    Grant exists; this action denied
    Resource-server policy
    Reason: object-denied
    Resource boundary
    No resource allowance
  6. Each actor’s authority boundary was preserved

    Client callback correlation, AS code binding and RS token/resource policy have different owners. This scene explains the RS reason without adding actual IdP, token-signature or body-delivery outcomes.

    Access grant context
    Known: declared read grant
    Resource-server policy
    Known: RS object-denied
    Resource boundary
    No broadened resource authority

Wrong audience

  1. The resource-action scope was selected

    Mina’s atlas-exporter client uses access-A. This access context carries a notes:read grant; an authorization code or refresh token is not treated as an RS credential.

    Access grant context
    case=wrong-audience
    Resource-server policy
    RS policy not applied
    Resource boundary
    No resource grant
  2. Requested and granted scopes were separated

    The client requested notes:read and notes:write, but grantedScopes contains only notes:read. Requested scope is not permission granted by the consent/grant outcome.

    Access grant context
    Granted: notes:read
    Resource-server policy
    RS policy not applied
    Resource boundary
    No resource grant
  3. The recipient gate was evaluated

    The resource case selects recipient archive-api while the access audience is notes-api. The RS separates this mismatch as audience-denied; it does not label the token generally forged.

    Access grant context
    Granted: notes:read
    Resource-server policy
    Audience mismatch
    Resource boundary
    No resource grant
  4. The object condition remained separate from scope

    True sameTenant and ownerMatches cannot compensate for the failed recipient gate. An object relationship does not establish token authority for another API.

    Access grant context
    Read grant scope
    Resource-server policy
    Audience mismatch retained
    Resource boundary
    No resource grant
  5. The RS action decision remained negative

    The expected result is audience-denied. A successful code exchange at another stage does not turn this resource decision into allow.

    Access grant context
    Grant exists; this action denied
    Resource-server policy
    Reason: audience-denied
    Resource boundary
    No resource allowance
  6. Each actor’s authority boundary was preserved

    Client callback correlation, AS code binding and RS token/resource policy have different owners. This scene explains the RS reason without adding actual IdP, token-signature or body-delivery outcomes.

    Access grant context
    Known: declared read grant
    Resource-server policy
    Known: RS audience-denied
    Resource boundary
    No broadened resource authority

ASSUMPTION · BOUNDARY · CONTROL

Which mistaken assumption expands authority?

A save button being visible after Exporter received notes:read is not proof of permission. The artifact allows own-note/read and denies own-note/write for scope. The latter is not a login failure: even a genuine grant can omit that action. In foreign-tenant/read, scope suffices but object policy denies access. A design that substitutes scope for object ownership extends an allowed action class to every object; impact depends on which data could be read or changed.

In the wrong-audience branch, the client selected the wrong API; the token is valid for another RS. Notes API rejecting it does not make the token globally fake: the recipient is wrong. In code-expired, the AS stops code exchange; the client cannot act as if an access token exists. Separate necessary conditions by actor: callback correlation at the client, code binding at the AS, token audience and resource policy at the RS. A positive decision by one actor does not replace another’s gate.

What bearer possession changes

For bearer use, possession matters for access, so treating a token as ordinary log or frontend display data is a mistaken storage assumption. Sender-constrained mechanisms are mentioned as design choices only; the bearer alias does not claim cryptographic client binding. PKCE also does not sender-constrain the access token at the API or replace a client secret in every context. These limits explain which boundary a credential protects, without providing a payload or intervention method.

3. Blue · Secure development & defense

Read every scenario without motion

Code deadline

  1. The code-exchange evidence was selected

    Code-A carries expiresAt=1100, issuedAt=1000 and client/redirect/PKCE binding metadata. This alias is not an access token granting API resource permission.

    Grant/code observation
    code-A declaration
    AS contract review
    AS gate not examined
    Acceptance scope
    Token issuance not examined
  2. The confidential-client condition was read

    The profile is a confidential server-side client. Normal and time-boundary cases separately supply clientAuthenticated=true; a PKCE match does not replace client authentication in every context.

    Grant/code observation
    Client: confidential server-side
    AS contract review
    Client/redirect/PKCE binding true
    Acceptance scope
    Token issuance not examined
  3. Last-valid and equality were compared

    At now=1099 expected=true, while now=1100 gives expected=false. This fixture’s deadline stops code acceptance; access-token expiry is not assumed to share that deadline.

    Grant/code observation
    Last valid=1099; deadline=1100
    AS contract review
    Equality code exchange denied
    Acceptance scope
    Access issuance unsupported for this case
  4. Code and access outcomes were separated

    The equality branch has a negative code predicate. Possessing the code alias cannot be interpreted as issuance of access-A; the AS outcome is separate evidence.

    Grant/code observation
    Code acceptance=false
    AS contract review
    Known: expiry boundary denial
    Acceptance scope
    No inferred access token
  5. Correlation evidence was scoped

    The profile selects one-use state and a browser-bound transaction. The code matrix does not measure runtime client callback correlation or actual AS interoperability.

    Grant/code observation
    Selected: state + browser binding
    AS contract review
    Declared AS predicates
    Acceptance scope
    No runtime callback evidence
  6. The AS conclusion was not promoted to an RS conclusion

    The code-deadline denial is known; no new access context is inferred from the AS. This record says nothing about RS acceptance of another grant or an earlier access alias.

    Grant/code observation
    Known: code expiry equality
    AS contract review
    No inferred token issuance
    Acceptance scope
    UNKNOWN: other access acceptance

Scope ledger

  1. The grant-scope record was selected

    requestedScopes contains read and write; grantedScopes contains only read. Review preserves the consent request and AS grant outcome as distinct observations.

    Grant/code observation
    Requested: read+write
    AS contract review
    Grant not examined
    Acceptance scope
    Refresh scope not examined
  2. The AS grant outcome was compared

    access-A is supplied with grantedScopes=[notes:read]. A write option in the client UI does not mean the omitted grant was issued.

    Grant/code observation
    Requested: read+write
    AS contract review
    Granted: read only
    Acceptance scope
    Refresh scope not examined
  3. Refresh recipient and scope were separated

    refresh-A is for the AS token endpoint only and carries originalScopes=[notes:read]. It is not the access context presented as an RS credential.

    Grant/code observation
    Refresh original: notes:read
    AS contract review
    Recipient: AS token endpoint
    Acceptance scope
    No rotation runtime
  4. Scope omission and escalation were separated

    A refresh request omitting the scope parameter has expected=true for the original scope. Requesting notes:write gives expected=false; omission creates no new grant.

    Grant/code observation
    Original scope retained
    AS contract review
    Omitted/read accepted; write denied
    Acceptance scope
    No inferred scope expansion
  5. The resource-action outcome was correlated

    The RS allows own-note read and gives scope-denied for write. These match the grant scope; tenant/object conditions remain required even for read.

    Grant/code observation
    Grant: notes:read
    AS contract review
    Read allow; write scope-denied
    Acceptance scope
    Object policy remains required
  6. Grant-scope evidence was bounded

    This ledger shows declared scopes and finite decision outcomes. Refresh rotationExecuted=false; no current revocation, actual token bytes or network-capture result is added.

    Grant/code observation
    Known: declared grant scope
    AS contract review
    Known: no scope expansion
    Acceptance scope
    UNKNOWN: runtime lifecycle

IMPLEMENTATION · SECURITY CONTROL · VERIFICATION

Keep the normal flow narrow and auditable

Atlas selects a confidential server-side client, code flow and S256 PKCE. Transaction correlation binds the browser and selected AS; credentials and verifier are not sent to browser code. Registered redirect URIs are compared exactly; the native localhost port exception is outside this web profile. Current BCP mandates PKCE for public clients and recommends it for confidential clients; CSRF prevention is mandatory but every profile need not use the same state mechanism. We additionally select one-use state. A multi-AS client explicitly designs issuer or distinct-redirect mix-up defenses.

Apply the BCP’s grant choices with their actual force: password grant is MUST NOT; implicit use is SHOULD NOT, with an exception only when the stated risks are mitigated. This lesson selects code flow. Minimum scopes, target RS audience and bounded credential use are explicit design choices. The RS allows access only when token validation, requested action and tenant/object conditions all hold. Neither a UI decision nor the client saying “scope present” is trusted; resource and job access preserve the same policy context.

Evidence from resource regression

The regression matrix includes positive own-note/read and three distinct negative boundaries: no write scope, wrong audience and foreign tenant. It tests the code time boundary as expired at 1100 and valid at 1099, under this fixture’s zero-leeway rule. Result records contain aliases, decision names and safe correlation rather than code or token bytes. This model check does not prove real AS interoperability, token signature verification or a learner lab; those need separate implementation and evidence.

NOW APPLY IT TO THE CASE

Which RS resource decision does an AS grant not complete?

Atlas exporter code, access and refresh aliases have different recipients and purposes. Requested scope, granted scope and object permission remain separate.

Which issuer/recipient relationship supplies identity in the case? Separate login or delegation success from evidence for current tenant/object permission.

Open the related case file ↗

READ THE EXAMPLE TOGETHER

Atlas delegation decision table

Select a field and examine what it supports and where the evidence ends.

Read the raw artifact and field explanations
{
  "profile": {
    "clientType": "confidential server-side",
    "flow": "authorization_code",
    "pkce": "S256",
    "oneUseStateSelected": true,
    "browserBoundTransaction": true,
    "actualProtocolRun": false,
    "actualTokensIncluded": false,
    "actualSignatureVerification": false
  },
  "actors": {
    "resourceOwner": "Mina",
    "client": "atlas-exporter",
    "authorizationServer": "https://id.example.org",
    "resourceServer": "notes-api"
  },
  "timeline": [
    "client creates AS/browser-bound transaction",
    "browser visits AS authorization endpoint",
    "AS authenticates owner and decides grant",
    "browser follows code callback",
    "client validates transaction correlation",
    "client exchanges code at token endpoint",
    "client receives access token plus optional refresh token",
    "RS validates token context and requested resource"
  ],
  "code": {
    "alias": "code-A",
    "clientId": "atlas-exporter",
    "redirectUri": "https://atlas.example.org/callback",
    "issuedAt": 1000,
    "expiresAt": 1100,
    "used": false,
    "pkceBindingDeclared": true
  },
  "codeCases": [
    {
      "name": "normal",
      "now": 1050,
      "clientMatches": true,
      "redirectMatches": true,
      "pkceMatches": true,
      "used": false,
      "expected": true,
      "clientAuthenticated": true
    },
    {
      "name": "last-valid",
      "now": 1099,
      "clientMatches": true,
      "redirectMatches": true,
      "pkceMatches": true,
      "used": false,
      "expected": true,
      "clientAuthenticated": true
    },
    {
      "name": "expiry-equality",
      "now": 1100,
      "clientMatches": true,
      "redirectMatches": true,
      "pkceMatches": true,
      "used": false,
      "expected": false,
      "clientAuthenticated": true
    },
    {
      "name": "already-consumed",
      "now": 1050,
      "clientMatches": true,
      "redirectMatches": true,
      "pkceMatches": true,
      "used": true,
      "expected": false,
      "clientAuthenticated": true
    },
    {
      "name": "binding-unmet",
      "now": 1050,
      "clientMatches": true,
      "redirectMatches": true,
      "pkceMatches": false,
      "used": false,
      "expected": false,
      "clientAuthenticated": true
    },
    {
      "name": "client-auth-unmet",
      "now": 1050,
      "clientMatches": true,
      "redirectMatches": true,
      "pkceMatches": true,
      "clientAuthenticated": false,
      "used": false,
      "expected": false
    }
  ],
  "access": {
    "alias": "access-A",
    "representation": "opaque alias, metadata is declarative validated context",
    "audience": "notes-api",
    "requestedScopes": [
      "notes:read",
      "notes:write"
    ],
    "grantedScopes": [
      "notes:read"
    ],
    "expiresAt": 2000
  },
  "refresh": {
    "alias": "refresh-A",
    "recipient": "AS token endpoint only",
    "optional": true,
    "clientBound": true,
    "originalScopes": [
      "notes:read"
    ],
    "rotationExecuted": false
  },
  "refreshCases": [
    {
      "requestedScopes": [
        "notes:read"
      ],
      "expected": true
    },
    {
      "requestedScopes": [],
      "expected": true,
      "scopeParameterOmitted": true
    },
    {
      "requestedScopes": [
        "notes:write"
      ],
      "expected": false
    }
  ],
  "resourceCases": [
    {
      "case": "normal",
      "audience": "notes-api",
      "action": "read",
      "requiredScope": "notes:read",
      "now": 1050,
      "sameTenant": true,
      "ownerMatches": true,
      "expected": "allow"
    },
    {
      "case": "scope-denied",
      "audience": "notes-api",
      "action": "write",
      "requiredScope": "notes:write",
      "now": 1050,
      "sameTenant": true,
      "ownerMatches": true,
      "expected": "scope-denied"
    },
    {
      "case": "wrong-audience",
      "audience": "archive-api",
      "action": "read",
      "requiredScope": "notes:read",
      "now": 1050,
      "sameTenant": true,
      "ownerMatches": true,
      "expected": "audience-denied"
    },
    {
      "case": "foreign-tenant",
      "audience": "notes-api",
      "action": "read",
      "requiredScope": "notes:read",
      "now": 1050,
      "sameTenant": false,
      "ownerMatches": true,
      "expected": "object-denied"
    }
  ]
}
actors
Role ownership; the four roles make separate decisions.
code
Client, redirect, time, use and PKCE bindings; not code bytes.
access
Granted scope and RS audience context; not an asserted JWT claim.
refreshCases
Scope sets that do not exceed the first grant; no credential renewal runs.
resourceCases
Selected decisions that keep scope and object policy separate.

All code/token values are aliases. The scene starts after callback; it runs no browser, real AS, TLS, PKCE hash production, signature/introspection or refresh.

Two reality checks

An access token is a user login statement.

An OAuth access token is for RS access; OIDC adds a standard authentication statement through the ID token.

Read scope opens all of a user’s notes.

The RS checks audience, scope and tenant/object policy together on each request.

YOUR TURN

Work through this story.

Predict the outcome, find the evidence boundary, and explain it in your own words.

The client requested notes:read and notes:write; the access context grants only notes:read. Is writing allowed?

  1. No; the selected branch is scope-denied.
  2. Yes; requesting write during authorization is sufficient for a grant.
Read the explained answer

No; the selected branch is scope-denied. — The AS grant is limited to read; requested scope creates no permission. The RS checks the required write scope.

Are code, refresh and access aliases sent to the same endpoint?

Selected lesson artifact fields; teaching example

code.clientId
atlas-exporter
refresh.recipient
AS token endpoint only
access.audience
notes-api
  1. The RS accepts a refresh alias instead of access in an ordinary API request.
  2. Code exchange/refresh belong to the AS token endpoint; access belongs to notes-api.
Read the explained answer

Code exchange/refresh belong to the AS token endpoint; access belongs to notes-api. — Each alias has a different stage/purpose; refresh recipient is explicitly AS only.

Audience and read scope are valid, but the resource belongs to a foreign tenant. Why is an object gate still required?

Read the explained answer

The RS evaluates token context for audience/time/granted scopes, then applies requested-object tenant/owner policy. Read scope does not release every note from another tenant. The authorization code is carried through the browser callback, not an API access token; this fixture executes no actual protocol run.

□ I did not count scope as an object grant.

□ I separated the code from the API-access credential.

Open the application console · request / response / browser
Also open request, URL, cookie and DOM examples

Sources and scope