# OAuth: Sign in with OneForm | OneForm

> Sign in with OneForm for MCP clients: OAuth 2.1 discovery, authorization code with PKCE (S256), Client ID Metadata Documents and Dynamic Client…

Source: https://oneform.si/developers/oauth/

---

1. [Home](https://oneform.si/)
2. [Developers](https://oneform.si/developers/)
3. OAuth

[View as Markdown](https://oneform.si/developers/oauth.md)

Developers

# Sign in with OneForm

AI tools and other MCP clients connect to [OneForm's MCP server](https://oneform.si/developers/mcp/) by having a person sign in with OneForm. It is OAuth 2.1 as the MCP authorization spec describes it: discovery, the authorization code flow with PKCE, and clients that register themselves.

## How it works

1. Your client calls the MCP server without a token and gets 401 with a pointer to the protected-resource metadata.
2. It reads that, then the authorization server's metadata, and identifies itself (a Client ID Metadata Document, or Dynamic Client Registration).
3. It sends the person to the authorize endpoint with a PKCE challenge. They sign in to OneForm, choose a firm, and choose what to allow.
4. OneForm redirects back with a one-time code, which the client exchanges for an access token and a refresh token.
5. The client calls `https://app.oneform.si/mcp` with `Authorization: Bearer ofa_…`, and refreshes the token when it expires.

For the MCP server only

These tokens work on `https://app.oneform.si/mcp` and nowhere else. They are bound to it as a resource (RFC 8707), and the REST API refuses them: it takes [API keys](https://oneform.si/developers/api/#authentication).

## Discovery

| Protected resource (RFC 9728)   | https://app.oneform.si/.well-known/oauth-protected-resource (also at …/oauth-protected-resource/mcp)                      |
| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| Authorization server (RFC 8414) | https://app.oneform.si/.well-known/oauth-authorization-server, and the same document at /.well-known/openid-configuration |

oauth-authorization-server

```json
{  "issuer": "https://app.oneform.si",  "authorization_endpoint": "https://app.oneform.si/oauth/authorize",  "token_endpoint": "https://app.oneform.si/api/oauth/token",  "registration_endpoint": "https://app.oneform.si/api/oauth/register",  "revocation_endpoint": "https://app.oneform.si/api/oauth/revoke",  "response_types_supported": [    "code"  ],  "response_modes_supported": [    "query"  ],  "grant_types_supported": [    "authorization_code",    "refresh_token"  ],  "code_challenge_methods_supported": [    "S256"  ],  "token_endpoint_auth_methods_supported": [    "none",    "client_secret_post",    "client_secret_basic"  ],  "revocation_endpoint_auth_methods_supported": [    "none",    "client_secret_post",    "client_secret_basic"  ],  "scopes_supported": [    "forms:read",    "responses:read",    "reports:read",    "leads:read",    "workflows:read",    "bookings:read",    "insights:read",    "integrations:read",    "settings:read",    "forms:write",    "reports:write",    "workflows:write",    "bookings:write",    "settings:write",    "ai",    "send"  ],  "client_id_metadata_document_supported": true,  "authorization_response_iss_parameter_supported": true,  "service_documentation": "https://oneform.si/developers/mcp"}
```

A 401 from the MCP server carries `WWW-Authenticate: Bearer resource_metadata="…"` with the protected-resource address.

## Authorization code with PKCE

Only `response_type=code` with PKCE `S256` is accepted. `resource` may be left out; if sent, it must be the MCP server's address. `scope` is optional: without it, the consent screen starts with every read scope ticked, plus the writes and AI the person's role allows; never Act on clients.

GET /oauth/authorize

```text
https://app.oneform.si/oauth/authorize  ?response_type=code  &client_id=https%3A%2F%2Fyourapp.example%2Foauth%2Fclient.json  &redirect_uri=https%3A%2F%2Fyourapp.example%2Fcallback  &scope=forms%3Aread%20responses%3Aread  &state=af0ifjsldkj  &code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM  &code_challenge_method=S256  &resource=https%3A%2F%2Fapp.oneform.si%2Fmcp
```

The request is kept for 10 minutes while the person decides. On approval, the redirect carries `code`, your `state` and `iss` (RFC 9207). The code works once, for 2 minutes.

Exchange the code

```bash
curl -X POST https://app.oneform.si/api/oauth/token \  -d grant_type=authorization_code \  -d code=<code from the redirect> \  -d code_verifier=<the verifier you made the challenge from> \  -d client_id=https://yourapp.example/oauth/client.json \  -d redirect_uri=https://yourapp.example/callback \  -d resource=https://app.oneform.si/mcp
```

200 · application/json

```json
{  "access_token": "ofa_…",  "token_type": "Bearer",  "expires_in": 3600,  "refresh_token": "ofr_…",  "scope": "forms:read responses:read"}
```

## Registering a client

### Client ID Metadata Documents (preferred)

Use an https URL as your `client_id`, serving your app's metadata as JSON. OneForm reads it the first time it sees it and keeps it for as long as its `Cache-Control` says, between 5 minutes and 24 hours. The document's `client_id` must equal its own URL. Being public, it can't hold a secret: use `none` with PKCE.

https://yourapp.example/oauth/client.json

```json
{  "client_id": "https://yourapp.example/oauth/client.json",  "client_name": "Your app",  "client_uri": "https://yourapp.example",  "redirect_uris": [    "https://yourapp.example/callback"  ],  "grant_types": [    "authorization_code",    "refresh_token"  ],  "response_types": [    "code"  ],  "token_endpoint_auth_method": "none"}
```

### Dynamic Client Registration

For clients that don't support metadata documents yet (RFC 7591). The answer has a `client_id` starting `ofk_`, and a `client_secret` (`ofs_…`) only if you asked for `client_secret_post` or `client_secret_basic`. Registration is open: a registered app can do nothing until a person approves it.

Register

```bash
curl -X POST https://app.oneform.si/api/oauth/register \  -H "Content-Type: application/json" \  -d '{"client_name":"Your app","redirect_uris":["http://127.0.0.1/callback"],"token_endpoint_auth_method":"none"}'
```

### Redirect URIs

- https anywhere, matched exactly.
- http only to this machine (127.0.0.1, \[::1\] or localhost), on any port, for desktop and command-line apps.
- An app's own scheme with a dot in it (com.example.app:/callback), or the editors' (cursor:, vscode:, windsurf:, claude:, zed:).
- No fragments, and no javascript:, data: or file: addresses.

## Tokens

| Token                                                  | Starts with | Lasts                                                  |
| ------------------------------------------------------ | ----------- | ------------------------------------------------------ |
| Access token                                           | ofa\_       | 60 minutes                                             |
| Refresh token                                          | ofr\_       | 30 days, renewed with every use                        |
| Agent token (made by a person in Settings → AI agents) | ofp\_       | 30, 90, 180, 365 days (at most 20 per person and firm) |

Tokens are opaque: don't parse them. OneForm stores only their SHA-256 hashes.

## Refreshing

Each refresh token works once. Using it returns a new access token and a new refresh token; keep the new one. You may ask for a narrower `scope`, never a wider one.

Refresh

```bash
curl -X POST https://app.oneform.si/api/oauth/token \  -d grant_type=refresh_token \  -d refresh_token=ofr_… \  -d client_id=https://yourapp.example/oauth/client.json
```

A reused refresh token ends the connection

If a refresh token that was already used shows up again, OneForm assumes it was copied and revokes the whole connection. The person has to sign in again. Store the newest refresh token before you use the new access token, and don't refresh from two places at once.

## Revoking

Revoking any token of a connection disconnects it (RFC 7009). Unknown tokens answer 200 too. People can also disconnect an app in OneForm under Settings → AI agents; leaving the firm or being removed from it disconnects their apps.

Revoke

```bash
curl -X POST https://app.oneform.si/api/oauth/revoke \  -d token=ofr_… \  -d client_id=https://yourapp.example/oauth/client.json
```

## Scopes

A connection never gets more than the person's role allows, checked on every call, so a change of role takes effect at once. The firm can also switch agents off or keep client data from them.

| Scope                             | Allows                                                                                             | Group          |
| --------------------------------- | -------------------------------------------------------------------------------------------------- | -------------- |
| forms:readSee forms               | Forms, their questions, links and embed code                                                       | Read           |
| responses:readSee responses       | What clients answered                                                                              | Read           |
| reports:readSee reports           | Reports and their delivery status                                                                  | Read           |
| leads:readSee leads               | People who filled in forms, with their lead score                                                  | Read           |
| workflows:readSee workflows       | Workflows, their steps and runs                                                                    | Read           |
| bookings:readSee bookings         | Booked calls and booking pages                                                                     | Read           |
| insights:readSee insights         | Form and email statistics                                                                          | Read           |
| integrations:readSee integrations | Which apps are connected (never their keys)                                                        | Read           |
| settings:readSee settings         | Brand, contact details, lead scoring                                                               | Read           |
| forms:writeBuild and edit forms   | Create, change and delete draft forms                                                              | Write          |
| reports:writeEdit reports         | Change, re-draft and delete draft reports                                                          | Write          |
| workflows:writeBuild workflows    | Create and change workflows (switching one on also needs Act on clients)                           | Write          |
| bookings:writeManage bookings     | Notes and marking calls done or missed                                                             | Write          |
| settings:writeChange settings     | Brand, contact details, lead scoring                                                               | Write          |
| aiUse OneFormAI                   | Generate forms and draft reports (uses your AI credits)                                            | AI             |
| sendAct on clients                | Send and approve reports, publish forms, email form links, change bookings, sync leads to your CRM | Act on clients |

[← PreviousAI agents (MCP)](https://oneform.si/developers/mcp/)[Next →Zapier](https://oneform.si/developers/zapier/)

Questions about the API, webhooks or embeds? [Ask a developer](https://oneform.si/contact/?topic=developers).

DevelopersOAuth
- [Overview](https://oneform.si/developers/)
- [REST API](https://oneform.si/developers/api/)
- [Webhooks](https://oneform.si/developers/webhooks/)
- [Embed](https://oneform.si/developers/embed/)
- [AI agents (MCP)](https://oneform.si/developers/mcp/)
- [OAuth](https://oneform.si/developers/oauth/)
- [Zapier](https://oneform.si/developers/zapier/)
- [Form schema](https://oneform.si/developers/form-schema/)

Developers

- [Overview](https://oneform.si/developers/)
- [REST API](https://oneform.si/developers/api/)
- [Webhooks](https://oneform.si/developers/webhooks/)
- [Embed](https://oneform.si/developers/embed/)
- [AI agents (MCP)](https://oneform.si/developers/mcp/)
- [OAuth](https://oneform.si/developers/oauth/)
- [Zapier](https://oneform.si/developers/zapier/)
- [Form schema](https://oneform.si/developers/form-schema/)

[OpenAPI spec](https://oneform.si/developers/openapi.json)[Ask a developer](https://oneform.si/contact/?topic=developers)
