Praecise account/ Guides

Machines, agents and assistants

The device grant, agent clients, assistants acting for a person, and APIs that accept these tokens.

https://accounts.praecise.com

For anything that relies on the Praecise account and is not a person in a product's browser tab: a server being enrolled, an agent that runs unattended, an assistant acting for a person, an API that accepts this service's tokens. A product signing people in is in sign-in.md; the shared services and the detail of tokens for an API are in api.md.

Everything here is discoverable without this page: the metadata is at /.well-known/openid-configuration (and, with the same content, /.well-known/oauth-authorization-server) and the keys at /jwks.

Which one are you

You areYou useA person is involved
A program acting as one account on the shared services (domains, mailboxes)a service token, sent as Authorization: Bearer to /api (api.md)once, to mint the token on their account page
A machine with no browser that should belong to an accountthe device grantonce, to approve it on another device
An agent that runs with nobody presentthe client credentials grant, for one APInever; the API decides what the agent may do
An assistant or tool acting for a person (an MCP host, a command line)authorization code with PKCE, as a client described by a metadata document or registered by itselfeach time, on a consent page
An API that accepts these tokensthe JWKS, and optionally a claims hookno

A machine with no browser: the device grant (RFC 8628)

The client is furcate-node, public, with no secret.

  1. POST /device/auth with client_id=furcate-node&scope=openid. The answer holds device_code, user_code (eight letters, shown as XXXX-XXXX), verification_uri (/device), verification_uri_complete, interval and expires_in (five minutes).
  2. Optionally say what is being claimed, so the person approving sees it and not only a client name: POST /device/enrolment with JSON {"user_code", "node_id", "site", "site_addr", "machines": [{"node_id", "name", "attested", "administers"}]} (at most 64 machines). It grants nothing; it only fills the approval screen for that code.
  3. Show the person the code and the address. They open it on a device with a browser, sign in, and approve what the screen lists.
  4. Poll POST /token with grant_type=urn:ietf:params:oauth:grant-type:device_code&device_code=…&client_id=furcate-node, no faster than interval. authorization_pending means not yet; expired_token or access_denied means start again.

An agent with nobody present: client credentials

An agent client is an entry in the service's AGENT_CLIENTS setting (an id and a secret of at least 32 characters; optionally dpop_required). It may ask for a token for one API, and only an API that has a claims hook, because the API, not this service, says what that agent is and may do:

POST /token
Authorization: Basic base64(<id>:<secret>)

grant_type=client_credentials&resource=<the API's address>&scope=<its scopes>&<the API's own parameter>

The token is refused unless the API's hook approves this client for it. It lasts ten minutes at most. No other kind of client may use this grant.

An assistant acting for a person

Authorization code with PKCE (S256), as a public client, identified in one of two ways:

  • A metadata document. The client_id is an HTTPS URL and the JSON document there is the registration (the form the MCP authorization specification asks for). It is fetched over HTTPS from a public host, without following redirects, at most 5 kB within 2.5 seconds; its client_id must be the URL it was fetched from; it must name the client and its redirect addresses; a loopback redirect address may use any port.
  • Registering itself (RFC 7591): POST /reg with its metadata. The answer holds the client_id and a token with which it can read, change and delete its own registration.

Either way the person signs in and sees a consent page naming the assistant (for a document, its host) and exactly what it asked for; nothing is granted that was not shown. One that asks only who the person is (openid, email, profile) is shown a plain page listing what it will be told; within one Praecise sign-in it is asked once, and a silent request (prompt=none) before that is answered consent_required. Such a client never gets the client credentials grant.

A token is for exactly one API: name it with resource on the authorization request (RFC 8707), ask for that API's scopes, and send the token only there. What the API's token carries, how long it lasts and how it is refreshed is in api.md. Add offline_access to stay connected; a refresh token for an API with a hook is replaced at every use, and a second use of an old one ends the grant.

Binding a token to a key (DPoP, RFC 9449)

Any client may send a DPoP proof with its token request (ES256 or EdDSA). The access token then carries the key's thumbprint in cnf.jkt, and the API asks for a proof with every call. A client marked dpop_required is refused a token without one.

Ending a token

  • POST /token/revocation ends a refresh token and its grant. A JWT access token cannot be revoked; it ends when it expires (fifteen minutes at most for an API with a hook).
  • POST /token/introspection says whether a token is active, for an API that cannot verify a JWT itself.
  • A person ends everything a browser holds with "Sign out everywhere" on their account page; service tokens, enrolled machines and connected assistants are ended one by one there, not by that.

An API that accepts these tokens

Verify the JWT against /jwks: iss is exactly the issuer, aud is exactly your own address, exp has not passed, and, when the token is bound, the DPoP proof matches cnf.jkt. To decide yourself what a token for you may say (which account at your service, which permissions), offer a claims hook: this service asks it at every issuance and refresh and issues nothing when it refuses, fails or times out. The hook's request, signature and answers are in api.md.

An ID token from a fresh passkey confirmation can serve as evidence for one action: the product sends nonce tied to the action, and the API checks iss, aud (the product's client id), nonce, auth_time, acr: "phr", amr and sub itself (sign-in.md, "Asking for a passkey").

What does not exist

  • No password grant, no implicit flow, no sign-in by emailed code, no sign-in by passkey alone.
  • No way for a program to make an account: accounts are made by a person, with an invitation and a code sent to their mailbox.
  • No way for a program to mint its first service token: a person does that on their account page.

Written and kept by Praecise account. Read from https://accounts.praecise.com/guides/agents.md.

This page as Markdown