Authentication for agents
Atlas by Friendlym is an OAuth 2.1 authorization server and a protected resource. Agents get short lived access tokens bound to one person and one workspace, with the scopes that person approved.
Discovery
| What | Where |
|---|---|
| Protected resource metadata, MCP (RFC 9728) | https://tilqui-atlas.pages.dev/.well-known/oauth-protected-resource/mcp |
| Protected resource metadata, REST API | https://tilqui-atlas.pages.dev/.well-known/oauth-protected-resource/api |
| Authorization server metadata (RFC 8414) | https://tilqui-atlas.pages.dev/.well-known/oauth-authorization-server |
A request without a token to /mcp or /api answers 401 with WWW-Authenticate: Bearer resource_metadata="…", so a client can start from the URL it was given.
Endpoints
| Endpoint | Use |
|---|---|
POST /oauth/register |
Dynamic client registration (RFC 7591). Public clients: token_endpoint_auth_method is none. |
GET /oauth/authorize |
Authorization code with PKCE. code_challenge_method=S256 is required. |
POST /oauth/token |
authorization_code, refresh_token and urn:ietf:params:oauth:grant-type:device_code. |
POST /oauth/device_authorization |
The device grant (RFC 8628) for agents without a browser. |
GET /device |
Where the human types the code the agent shows. |
POST /oauth/revoke |
Token revocation (RFC 7009). |
Client ID metadata documents are supported: a client may use an https URL as its client_id, and Atlas by Friendlym reads the client's name and redirect URIs from that document.
Authorization code with PKCE
This is what Claude Code, Codex and Cursor do on their own.
- Register:
POST /oauth/registerwithclient_nameandredirect_uris(https,http://localhostorhttp://127.0.0.1with any port, or an app scheme). - Send the human's browser to
/oauth/authorizewithresponse_type=code,client_id,redirect_uri,code_challenge(S256),state, thescopeyou need andresource=https://tilqui-atlas.pages.dev/mcp. - The human signs in and sees the consent screen: your agent's name (verified when it comes from a client ID metadata document on your own domain, otherwise marked as self reported), the workspace, and a checkbox per scope you asked for. Only
readis ticked by default; the human ticks the rest. They approve or deny. - Your redirect URI gets
code,stateandiss. Exchange the code at/oauth/tokenwith yourcode_verifierwithin five minutes.
Device flow, for headless agents
# 1. register once (no redirect URI needed)
curl -s https://tilqui-atlas.pages.dev/oauth/register -H 'content-type: application/json' \
-d '{"client_name":"Build server agent","grant_types":["urn:ietf:params:oauth:grant-type:device_code","refresh_token"]}'
# 2. ask for a code
curl -s https://tilqui-atlas.pages.dev/oauth/device_authorization -d client_id=CLIENT_ID -d resource=https://tilqui-atlas.pages.dev/mcp
# => {"device_code":"…","user_code":"BCDF-GHJK","verification_uri":"https://tilqui-atlas.pages.dev/device",
# "verification_uri_complete":"https://tilqui-atlas.pages.dev/device?code=BCDF-GHJK","expires_in":900,"interval":5}
# 3. show user_code and the link to your human, then poll every `interval` seconds
curl -s https://tilqui-atlas.pages.dev/oauth/token -d grant_type=urn:ietf:params:oauth:grant-type:device_code \
-d device_code=DEVICE_CODE -d client_id=CLIENT_ID
While the human has not decided, the token endpoint answers authorization_pending; poll faster than the interval and it answers slow_down. After the human approves on /device you get the tokens.
The link only fills in the code box: the human confirms that their agent is showing this code right now before they see the consent screen, which shows how old the request is and the country it came from. A client registered under the name of a well known agent (Claude, Codex, Cursor, ChatGPT and so on) cannot use the device flow; register under your own name.
Tokens
- Access tokens start with
ato_and last one hour. Send them asAuthorization: Bearer …. - Refresh tokens start with
atr_, last 30 days and rotate: every refresh returns a new one and the old one stops working. Presenting a used refresh token again revokes the whole grant, so a stolen token cannot be replayed quietly. - Every token is bound to the person who approved it and to their workspace, and never outlives the approval (90 days by default).
- Every token has its own scopes and audiences (RFC 8707). Send
resource=https://tilqui-atlas.pages.dev/mcp,https://tilqui-atlas.pages.dev/apior both; a token for one does not work on the other. A refresh may narrow the scopes or audiences, never widen them. - The person can remove the agent at any time on the Connect page; its tokens stop working at once.
Scopes
| Scope | Grants |
|---|---|
read |
Read the projects, dossiers and sources the person can see |
ask |
ask_atlas, with a daily limit per agent |
inbox |
Questions between the customer and the agency; answers are drafts the person confirms |
tasks |
The tasks assigned to the person, and progress notes on them |
agent_context |
Project context the agency shares with agents |
direction |
Direction packages for the person's projects, and confirming the tasks they propose |
Ask only for what you need with scope=…; without it every scope is offered. The person ticks among them on the consent screen, with only read ticked by default, and the token response's scope says what was granted.
Pairing tokens
Before OAuth, agents connected with a pairing line from the Connect page (atl_ tokens). Those keep working and show on the Connect page next to OAuth agents.