# OAuth apps

> Let other people connect their MockFlow account to your app, with the authorization code flow.

Source: https://mockflow.com/developers/concepts/oauth-apps

An API key acts as you. An OAuth app acts as whoever connects it: each person signs in to MockFlow, approves your app, and your app gets a token that works on their boards with the scopes they approved. Use an app when people other than you will connect, and a key from the [developer console](/developers/console/) when the only account is yours.

### URLs

- `Authorization URL` (GET): `https://mockflow.com/mcp-oauth/`. Where the person signs in and approves your app.
- `Token URL` (POST): `https://mockflow.com/mcp-oauth/token`. Where your server exchanges the code, and later the refresh token, for tokens.
- `Revoke URL` (POST): `https://mockflow.com/mcp-oauth/revoke`. Where your server gives tokens back when someone disconnects your app.
- `API base URL`: `https://api.mockflow.com/v1`. Where your app calls the API with the access token.

## Register an app

Open the **OAuth apps** tab of the [developer console](/developers/console/?tab=oauth) and register the app. You get a client ID, and for a server app a client secret starting `mf_cs_`. The console can reveal the secret again, rotate it, disable the app or delete it.

### Fields

- `Name` (string, required): Shown on the consent screen as "Connect *name*". Up to 200 characters.
- `Description` (string): Shown under the name on the consent screen. Up to 500 characters.
- `Logo` (image): Shown beside MockFlow's logo on the consent screen. A square PNG, JPEG or WebP, up to 512 KB.
- `Redirect URI` (string, required): Where MockFlow sends the person back with a code. One per app, `https` only, with `http` allowed for `localhost` and `127.0.0.1` while you develop. No `#fragment`.
- `Scopes` (string, required): The [scopes](/developers/concepts/scopes) your app may ask for. A request for a scope outside this list is refused on the consent screen, and the consent screen shows the person exactly what the token will be allowed to do.
- `Cannot keep a secret` (boolean): Tick it for an app that runs on someone's device, such as a desktop, mobile or command line app. It gets no secret and must authorise with PKCE: a request without a `code_challenge` is refused. This cannot be changed later.

Registering asks you to accept the [MockFlow developer terms](/developers/terms/) on behalf of whoever builds and ships the app.

A workspace can register up to 25 apps.

### In development, then published

A new app is **in development**: you and the people in your workspace can connect it, so you can build and test it together. Anyone else who tries sees that the app is still in development. When it is ready, press **Publish** in the console and anyone with a MockFlow account can connect it.

Publishing needs what a stranger sees on the consent screen: a description, a logo and an `https` redirect on your own domain rather than `localhost`. It also asks you to accept the current [Developer Terms](/developers/terms/).

> **Note** **Unpublish** takes the app back into development and disconnects everyone outside your workspace at once. They can connect again after you publish it.

## 1. Send the person to MockFlow

Open the consent page in the browser. The console shows this URL ready made for each app.

```http
GET https://mockflow.com/mcp-oauth/?response_type=code
    &client_id=YOUR_CLIENT_ID
    &redirect_uri=https%3A%2F%2Fyourapp.com%2Fcallback
    &scope=user%3Aread%20boards%3Aread
    &state=RANDOM_VALUE
    &code_challenge=BASE64URL_SHA256_OF_VERIFIER
    &code_challenge_method=S256
```

### Query parameters

- `response_type` (string, required): Always `code`.
- `client_id` (string, required): Your app's client ID.
- `redirect_uri` (string, required): The redirect URI you registered, exactly as registered: no extra path, query or trailing slash.
- `scope` (string): Space separated scopes, from the ones you registered. Leave it out for all of them. A scope you did not register is refused, not dropped.
- `state` (string): A random value you keep in the person's session. It comes back unchanged; refuse the callback when it does not match.
- `code_challenge` (string): The base64url SHA-256 of a random `code_verifier` of 43 to 128 characters. Required for an app registered as unable to keep a secret, and worth sending from every app.
- `code_challenge_method` (string): `S256`, the only method supported.

Someone who is not signed in can sign in on the same page, with Google, Microsoft or their email. They then see your app's name and description, and choose **Authorize** or **Deny**.

## 2. Receive the code

On approval MockFlow redirects to your redirect URI with a `code` and your `state`.

```http
https://yourapp.com/callback?code=AUTHORIZATION_CODE&state=RANDOM_VALUE
```

When the person denies, the redirect carries `error=access_denied` instead, with your `state`. Any other problem, such as an unknown client ID or a redirect URI that does not match, is shown on the consent page and is not sent back to your app.

## 3. Exchange the code for tokens

Make a `POST` request to `https://mockflow.com/mcp-oauth/token` from your server, with the `Content-Type: application/x-www-form-urlencoded` header. The client ID and secret go in the body, or in an HTTP Basic `Authorization` header; JSON bodies are not read. The code only works for the client it was issued to, and an app that has a secret must send it.

> **Note** The code expires **10 minutes** after it is issued and works once. Exchange it as soon as the person lands on your redirect URI.

```bash
curl -X POST https://mockflow.com/mcp-oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d grant_type=authorization_code \
  -d code=AUTHORIZATION_CODE \
  -d redirect_uri=https://yourapp.com/callback \
  -d client_id=YOUR_CLIENT_ID \
  -d client_secret=YOUR_CLIENT_SECRET \
  -d code_verifier=YOUR_CODE_VERIFIER
```

### Form fields

- `grant_type` (string, required): `authorization_code`.
- `code` (string, required): The code from the redirect.
- `redirect_uri` (string, required): The same redirect URI you sent in step 1.
- `client_id` (string, required): Your app's client ID.
- `client_secret` (string): Your app's secret. Required for every app that has one; an app without one proves itself with `code_verifier`.
- `code_verifier` (string): The verifier behind the `code_challenge`, when you sent one.

```json
{
  "access_token": "4f9c\u2026",
  "refresh_token": "a71e\u2026",
  "token_type": "Bearer",
  "expires_in": 86400,
  "scope": "user:read boards:read"
}
```

## 4. Call the API

Send the access token as a bearer token, exactly like an API key. The call acts as the person who approved your app, in their workspace, and can only reach the boards they can open.

```bash
curl https://api.mockflow.com/v1/me \
  -H "Authorization: Bearer ACCESS_TOKEN"
```

A missing, expired or revoked token answers `401`. A token without the scope an operation needs answers `403`; each operation's page lists its scope.

## 5. Refresh the token

Access tokens last 24 hours and refresh tokens 30 days. Before the access token runs out, trade the refresh token for a new pair.

```bash
curl -X POST https://mockflow.com/mcp-oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d grant_type=refresh_token \
  -d refresh_token=REFRESH_TOKEN \
  -d client_id=YOUR_CLIENT_ID \
  -d client_secret=YOUR_CLIENT_SECRET
```

> **Note** A refresh returns a new refresh token as well, and the old one stops working. Store both new tokens every time. After 30 days without a refresh, the person connects again.

## 6. Disconnect

When someone disconnects your app, deletes their account with you, or asks you to delete their data, give their tokens back. Post either the access token or the refresh token: both stop working at once, because they are one connection.

```bash
curl -X POST https://mockflow.com/mcp-oauth/revoke \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d token=REFRESH_TOKEN \
  -d client_id=YOUR_CLIENT_ID \
  -d client_secret=YOUR_CLIENT_SECRET
```

### Form fields

- `token` (string, required): The access token or the refresh token to revoke.
- `token_type_hint` (string): `access_token` or `refresh_token`. Accepted for compatibility; MockFlow finds the token either way.
- `client_id` (string, required): Your app's client ID.
- `client_secret` (string): Your app's secret, for an app that has one.

The answer is `200` with an empty object, whether or not the token was still valid, as [RFC 7009](https://www.rfc-editor.org/rfc/rfc7009) asks. A wrong secret answers `401 invalid_client`. Delete your copy of the tokens either way.

## Token endpoint errors

Failures answer with `{"error": …, "error_description": …}`, the OAuth 2.0 shape, rather than the API's error envelope.

### Codes

- 400 `invalid_request`: A required field is missing.
- 400 `unsupported_grant_type`: Only `authorization_code` and `refresh_token` are supported.
- 401 `invalid_client`: The client is unknown or disabled, or its secret is missing or wrong.
- 400 `invalid_grant`: The code or refresh token is unknown, used or expired, belongs to another client, the redirect URI differs from step 1, or the code verifier does not match the challenge.
- 500 `server_error`: Something failed on our side. Try again.

## Ending access

Deleting or disabling an app in the console ends every token it holds at once, and a disabled app cannot be authorised again until you enable it. Rotating the secret leaves existing tokens working, so do it whenever the secret may have been seen.

People can also disconnect your app themselves, under **Connected apps** in their MockFlow account settings. From then on their tokens answer `401`, and the next refresh fails with `invalid_grant`. Treat either as the person having disconnected.

> **Warning** Keep the client secret and the tokens on your server. An app that ships to people's devices should be registered as one that cannot keep a secret, and use PKCE.
