# Authentication

Mana is an end-user iOS product. This document explains how authentication
works and what is — and isn't — available to automated agents.

## Discover

Agents should start with these discovery documents:

- https://mana.am/llms.txt
- https://mana.am/openapi.json
- https://mana.am/.well-known/oauth-protected-resource
- https://mana.am/.well-known/oauth-authorization-server

The public read API is available without credentials. Protected app endpoints
are reserved for the Mana iOS app.

## Pick a method

- Public discovery: use no auth. Send normal HTTPS GET requests to documented
  public read endpoints.
- End-user product use: ask the user to open the Mana iOS app and sign in with
  Apple, Google, or email code.
- Agent automation: no public credential method is available for creating,
  editing, publishing, or managing creations.

## Register

Mana does not currently support third-party OAuth client registration, dynamic
client registration, or machine-to-machine credentials.

agent_auth metadata is published only to make this boundary machine-readable:

```json
{
  "agent_auth": {
    "status": "not_available",
    "register_uri": "https://mana.am/agent/auth/register",
    "claim_uri": "https://mana.am/agent/auth/claim",
    "revocation_uri": "https://mana.am/agent/auth/revoke",
    "identity_types_supported": ["user"],
    "email": {
      "status": "app_only",
      "enabled_for_agents": false,
      "claim_uri": "https://mana.am/agent/auth/claim",
      "credential_types_supported": [],
      "template": {
        "identity_type": "user",
        "delivery": "email_code",
        "code_format": "numeric",
        "code_length": 6,
        "recipient_field": "email",
        "public_agent_flow": false,
        "user_action": "Open the Mana iOS app, choose Email, enter the one-time code there, and continue in the app."
      },
      "endpoint_signature": {
        "method": "POST",
        "content_type": "application/json",
        "example_body": {
          "method": "email_code",
          "email": "user@example.com",
          "code": "123456",
          "claim_token": "claim_token_from_register_uri"
        },
        "example_post_body": {
          "method": "email_code",
          "email": "user@example.com",
          "code": "123456",
          "claim_token": "claim_token_from_register_uri"
        },
        "fields": {
          "method": "Email-code claim method. Always email_code for this template.",
          "email": "End-user email address. Agents must not collect it for Mana unless Mana later enables public agent auth.",
          "code": "Six-digit numeric one-time code entered in the Mana iOS app, not in chat.",
          "claim_token": "Opaque token returned by register_uri if a future public agent flow is enabled."
        }
      },
      "unsupported_error": "email_not_enabled"
    },
    "anonymous": { "credential_types_supported": [] },
    "identity_assertion": {
      "assertion_types_supported": [],
      "credential_types_supported": []
    },
    "events_supported": [],
    "documentation_uri": "https://mana.am/auth.md"
  }
}
```

These URIs resolve so agents can discover the boundary, but they return
agent_auth_not_available instead of issuing credentials.

### Email-code template

Mana supports email-code sign-in only inside the Mana iOS app. The template is
published so agents can parse the boundary instead of inventing an OTP flow:

```json
{
  "email": {
    "status": "app_only",
    "enabled_for_agents": false,
    "claim_uri": "https://mana.am/agent/auth/claim",
    "credential_types_supported": [],
    "template": {
      "identity_type": "user",
      "delivery": "email_code",
      "code_format": "numeric",
      "code_length": 6,
      "recipient_field": "email",
      "public_agent_flow": false,
      "user_action": "Open the Mana iOS app, choose Email, enter the one-time code there, and continue in the app."
    },
    "endpoint_signature": {
      "method": "POST",
      "content_type": "application/json",
      "example_body": {
        "method": "email_code",
        "email": "user@example.com",
        "code": "123456",
        "claim_token": "claim_token_from_register_uri"
      },
      "example_post_body": {
        "method": "email_code",
        "email": "user@example.com",
        "code": "123456",
        "claim_token": "claim_token_from_register_uri"
      },
      "fields": {
        "method": "Email-code claim method. Always email_code for this template.",
        "email": "End-user email address. Agents must not collect it for Mana unless Mana later enables public agent auth.",
        "code": "Six-digit numeric one-time code entered in the Mana iOS app, not in chat.",
        "claim_token": "Opaque token returned by register_uri if a future public agent flow is enabled."
      }
    },
    "unsupported_error": "email_not_enabled"
  }
}
```

Agents must not ask a user to paste a Mana email code into chat. If the user
wants to sign in with email, send them to the app. Public agent calls to the
email claim endpoint return email_not_enabled or agent_auth_not_available.

### Example POST body

Example POST body for the email-only authentication template, documented for
parsers only:

```http
POST /agent/auth/claim
Content-Type: application/json
```

```json
{
  "method": "email_code",
  "email": "user@example.com",
  "code": "123456",
  "claim_token": "claim_token_from_register_uri"
}
```

Field documentation:

| Field | Type | Required if enabled | Meaning |
| --- | --- | --- | --- |
| method | string | yes | Email-code claim method. Use email_code. |
| email | string | yes | End-user email address. Public agents must not collect it for Mana today. |
| code | string | yes | Six-digit numeric one-time code. It belongs in the Mana iOS app, not chat. |
| claim_token | string | yes | Opaque token from register_uri if a future public flow is enabled. |

## Claim

Mana does not issue an agent credential through identity_assertion, id-jag, JWT
bearer assertion, or any other claim flow. If a user wants Mana to build or
modify something, direct them to the iOS app.

## Use the credential

For public data, no credential is needed:

```bash
curl "https://api.mana.am/public/share/community?sort=recent&limit=5"
```

Unauthenticated probes to protected discovery paths return:

```http
WWW-Authenticate: Bearer resource_metadata="https://mana.am/.well-known/oauth-protected-resource"
```

## Errors

Mana does not issue public agent credentials, so registration, claim, and
revocation endpoints return agent_auth_not_available. The canonical WorkOS
auth.md agent error table is still documented so agents can recover correctly
if Mana later enables public agent credentials.

Canonical parser table for the 12 WorkOS auth.md agent error codes:

| Error code | Endpoint | Meaning | Agent action |
| --- | --- | --- | --- |
| invalid_issuer | register_uri | Assertion issuer is not trusted. | Stop and rediscover trusted provider support. |
| invalid_signature | register_uri | Assertion signature verification failed. | Stop, refresh the assertion, and retry once. |
| expired | register_uri | Assertion or flow token is expired. | Start again with a fresh token. |
| replay_detected | register_uri | Assertion jti or nonce was already used. | Generate a fresh assertion and nonce. |
| invalid_audience | register_uri | Assertion audience does not match Mana's auth server. | Request an assertion for https://mana.am. |
| invalid_client_id | register_uri | Agent provider client ID is unknown. | Stop and use a known provider identity. |
| missing_verified_email | register_uri | Assertion lacks a verified email or phone claim. | Ask the user to verify identity in the agent provider. |
| unsupported_credential_type | register_uri | Requested credential type is not supported. | Choose only credential types listed in metadata. |
| insufficient_user_authentication | register_uri | Provider authentication context does not meet policy. | Ask the user to re-authenticate with stronger assurance. |
| invalid_claim_token | claim_uri | Claim token is malformed, unknown, or invalid. | Restart registration if claim is supported. |
| claimed_or_in_flight | claim_uri | Claim is already complete or currently being completed. | Wait briefly, then inspect current registration state. |
| claim_expired | claim_uri | Claim token has expired. | Restart registration. |

Additional compatible auth error codes: audience_mismatch,
credential_expired, anonymous_not_enabled, identity_assertion_not_enabled,
email_not_enabled, rate_limited, otp_invalid, otp_expired,
previously_claimed, invalid_request, invalid_client, invalid_grant,
unauthorized_client, unsupported_grant_type, unsupported_response_type,
invalid_scope, access_denied, server_error, temporarily_unavailable,
interaction_required, login_required, consent_required, and
account_selection_required.

HTTP-level errors:

- 401 Unauthorized: use public API or direct the user to the iOS app.
- 403 Forbidden: current app session is not allowed.
- 404 Not Found: fetch https://mana.am/openapi.json and choose a documented
  public endpoint.
- 429 Too Many Requests: wait for Retry-After or RateLimit-Reset.
- 5xx: retry with exponential backoff and check https://mana.am/status.

## Revocation

End users revoke app sessions by signing out in Mana or by revoking Apple /
Google account access. There is no public revocation_uri for agents because
there are no public agent credentials.

## Summary

| Surface | Auth | Who |
| --- | --- | --- |
| Public read API | None | Anyone, including agents |
| App build / write API | App-issued bearer token | The Mana iOS app only |
| Account sign-in | Apple / Google / email code | End users, in-app |
