/ docs / authentication.md
authentication.md
  1  # Authentication Guide
  2  
  3  ## Recommended Setup
  4  
  5  For most users, the one-command setup is the easiest way to get started:
  6  
  7  ```bash
  8  # Claude Code users (authentication + MCP + commands + skills + credential guard)
  9  mureo setup claude-code
 10  
 11  # Cursor users (authentication + MCP only)
 12  mureo setup cursor
 13  
 14  # OpenAI Codex CLI users (MCP + credential guard + workflow skills + shared skills)
 15  mureo setup codex
 16  
 17  # Gemini CLI users (extension manifest + MCP)
 18  mureo setup gemini
 19  
 20  # CLI-only users (authentication only, terminal prompts)
 21  mureo auth setup
 22  
 23  # Browser-based auth wizard — localhost HTML form, no terminal input needed
 24  mureo auth setup --web
 25  ```
 26  
 27  ### `--skip-auth` and non-interactive invocation
 28  
 29  Every `mureo setup …` subcommand accepts `--skip-auth`, which installs the MCP config, credential guard, and (where supported) command/skill files without running OAuth. Useful for the double-click installer flow where authentication is handled later via `/onboard` in Claude Code, `$onboard` in Codex, or `mureo auth setup` in a real terminal.
 30  
 31  When `mureo setup …` is invoked from an AI agent's subprocess (Claude Code's Bash tool, Codex, etc.) that has no controlling TTY, `--skip-auth` is implied automatically so the command cannot hang on a `typer.confirm` prompt. A banner in stdout tells the operator to finish authentication in `Terminal.app` afterwards.
 32  
 33  Each subcommand also exposes explicit `--google-ads/--no-google-ads` and `--meta-ads/--no-meta-ads` flags, so you can specify exactly which platforms to configure without any prompt. Passing them alongside `--skip-auth` (or under a non-TTY) emits a warning and is ignored.
 34  
 35  ### `mureo auth setup --web` — browser-based wizard
 36  
 37  Prefer the `--web` flavour when you were pointed to mureo by an AI agent that cannot safely receive terminal input. It starts a short-lived HTTP server on a random localhost port, opens your browser at it, and walks you through Google Ads / Meta Ads credential entry via HTML forms and standard OAuth redirects. Every field in the form has a deep link next to it so you know where to fetch each secret. Security hardening (CSRF rotation, OAuth `state` re-validation, DNS-rebinding guard, localhost-pinned redirect verification, generic error surface, POST size cap, CSP) is described in `SECURITY.md` → "Browser-based auth wizard".
 38  
 39  ## How Credentials Work
 40  
 41  mureo loads credentials from `~/.mureo/credentials.json`, falling back to environment variables if the file is missing or incomplete.
 42  
 43  ## credentials.json Format
 44  
 45  Create `~/.mureo/credentials.json` with the following structure:
 46  
 47  ```json
 48  {
 49    "google_ads": {
 50      "developer_token": "YOUR_DEVELOPER_TOKEN",
 51      "client_id": "YOUR_OAUTH_CLIENT_ID",
 52      "client_secret": "YOUR_OAUTH_CLIENT_SECRET",
 53      "refresh_token": "YOUR_REFRESH_TOKEN",
 54      "login_customer_id": "1234567890"
 55    },
 56    "meta_ads": {
 57      "access_token": "YOUR_ACCESS_TOKEN",
 58      "app_id": "YOUR_APP_ID",
 59      "app_secret": "YOUR_APP_SECRET"
 60    }
 61  }
 62  ```
 63  
 64  You can include only the platforms you use. For example, if you only use Google Ads, the `meta_ads` section can be omitted.
 65  
 66  ### Google Ads Fields
 67  
 68  | Field | Required | Description |
 69  |-------|----------|-------------|
 70  | `developer_token` | Yes | Google Ads API developer token |
 71  | `client_id` | Yes | OAuth 2.0 client ID |
 72  | `client_secret` | Yes | OAuth 2.0 client secret |
 73  | `refresh_token` | Yes | OAuth 2.0 refresh token |
 74  | `login_customer_id` | No | Manager account ID (MCC). If omitted, the target `customer_id` is used as fallback. |
 75  
 76  ### Meta Ads Fields
 77  
 78  | Field | Required | Description |
 79  |-------|----------|-------------|
 80  | `access_token` | Yes | Meta Graph API access token (User or System User token) |
 81  | `app_id` | No | Meta App ID |
 82  | `app_secret` | No | Meta App Secret |
 83  
 84  ## Environment Variable Fallback
 85  
 86  If `~/.mureo/credentials.json` is missing or lacks the required fields, mureo falls back to environment variables.
 87  
 88  ### Google Ads
 89  
 90  | Variable | Required | Description |
 91  |----------|----------|-------------|
 92  | `GOOGLE_ADS_DEVELOPER_TOKEN` | Yes | API developer token |
 93  | `GOOGLE_ADS_CLIENT_ID` | Yes | OAuth 2.0 client ID |
 94  | `GOOGLE_ADS_CLIENT_SECRET` | Yes | OAuth 2.0 client secret |
 95  | `GOOGLE_ADS_REFRESH_TOKEN` | Yes | OAuth 2.0 refresh token |
 96  | `GOOGLE_ADS_LOGIN_CUSTOMER_ID` | No | Manager account (MCC) customer ID |
 97  
 98  ### Meta Ads
 99  
100  | Variable | Required | Description |
101  |----------|----------|-------------|
102  | `META_ADS_ACCESS_TOKEN` | Yes | Graph API access token |
103  | `META_ADS_APP_ID` | No | Meta App ID |
104  | `META_ADS_APP_SECRET` | No | Meta App Secret |
105  
106  **Resolution order**: credentials.json takes priority. Environment variables are only checked if the corresponding section in credentials.json is missing or incomplete.
107  
108  ## Obtaining Google Ads Credentials
109  
110  ### 1. Developer Token
111  
112  1. Sign in to your Google Ads Manager account at [ads.google.com](https://ads.google.com).
113  2. Navigate to **Tools & Settings > Setup > API Center**.
114  3. If you don't have a developer token, apply for one. For testing, you'll receive a test token immediately.
115  4. Copy the developer token.
116  
117  ### 2. OAuth 2.0 Client ID and Secret
118  
119  1. Go to the [Google Cloud Console](https://console.cloud.google.com/).
120  2. Create a new project (or select an existing one).
121  3. Enable the **Google Ads API** under **APIs & Services > Library**.
122  4. Navigate to **APIs & Services > Credentials**.
123  5. Click **Create Credentials > OAuth client ID**.
124  6. Select **Desktop app** as the application type.
125  7. Copy the **Client ID** and **Client Secret**.
126  
127  ### 3. Refresh Token
128  
129  Use the `google-auth-oauthlib` library to obtain a refresh token:
130  
131  ```python
132  from google_auth_oauthlib.flow import InstalledAppFlow
133  
134  flow = InstalledAppFlow.from_client_config(
135      {
136          "installed": {
137              "client_id": "YOUR_CLIENT_ID",
138              "client_secret": "YOUR_CLIENT_SECRET",
139              "auth_uri": "https://accounts.google.com/o/oauth2/auth",
140              "token_uri": "https://oauth2.googleapis.com/token",
141          }
142      },
143      scopes=["https://www.googleapis.com/auth/adwords"],
144  )
145  flow.run_local_server(port=8080)
146  print("Refresh token:", flow.credentials.refresh_token)
147  ```
148  
149  Alternatively, use the [Google OAuth Playground](https://developers.google.com/oauthplayground/) with the `https://www.googleapis.com/auth/adwords` scope.
150  
151  ## Obtaining Meta Ads Credentials
152  
153  ### Access Token
154  
155  **Option A: Graph API Explorer (for testing)**
156  
157  1. Go to [Meta Graph API Explorer](https://developers.facebook.com/tools/explorer/).
158  2. Select your app.
159  3. Click **Generate Access Token** with the `ads_management` and `ads_read` permissions.
160  4. The resulting token is short-lived (1-2 hours).
161  
162  **Option B: Long-Lived Token (for production)**
163  
164  1. Obtain a short-lived user token via the Graph API Explorer.
165  2. Exchange it for a long-lived token (60 days):
166  
167  ```bash
168  curl -X GET "https://graph.facebook.com/v21.0/oauth/access_token?\
169  grant_type=fb_exchange_token&\
170  client_id=YOUR_APP_ID&\
171  client_secret=YOUR_APP_SECRET&\
172  fb_exchange_token=SHORT_LIVED_TOKEN"
173  ```
174  
175  **Option C: System User Token (recommended for automation)**
176  
177  1. Go to [Business Settings](https://business.facebook.com/settings/) > **System Users**.
178  2. Create a System User with **Admin** role.
179  3. Assign the ad account to the system user.
180  4. Generate a token with `ads_management` permission.
181  5. System User tokens do not expire.
182  
183  ### App ID and App Secret
184  
185  1. Go to [Meta for Developers](https://developers.facebook.com/).
186  2. Navigate to your app > **Settings > Basic**.
187  3. Copy the **App ID** and **App Secret**.
188  
189  These are optional for basic use, but **required for automatic token refresh** (see below).
190  
191  ## Meta Ads Token Auto-Refresh
192  
193  mureo can automatically refresh Long-Lived Tokens before they expire, so you never have to manually exchange tokens again.
194  
195  ### How It Works
196  
197  1. When `mureo auth setup` saves a Meta Ads token, it records a `token_obtained_at` ISO 8601 timestamp in `credentials.json`.
198  2. Each time Meta Ads credentials are loaded, mureo checks the token age.
199  3. If the token is **53+ days old** (7-day safety margin before the 60-day expiry), mureo exchanges it for a fresh Long-Lived Token via the Meta Graph API.
200  4. The new token and timestamp are written back to `credentials.json` atomically.
201  
202  ### Requirements
203  
204  | Field | Required | Why |
205  |-------|----------|-----|
206  | `app_id` | Yes | Needed for the token exchange API call |
207  | `app_secret` | Yes | Needed for the token exchange API call |
208  | `token_obtained_at` | Auto | Written by `mureo auth setup`; can be added manually (ISO 8601 format) |
209  
210  If `app_id` or `app_secret` are missing, auto-refresh is silently skipped and the existing token is used as-is.
211  
212  ### credentials.json with auto-refresh fields
213  
214  ```json
215  {
216    "meta_ads": {
217      "access_token": "YOUR_ACCESS_TOKEN",
218      "app_id": "YOUR_APP_ID",
219      "app_secret": "YOUR_APP_SECRET",
220      "token_obtained_at": "2025-12-01T00:00:00Z"
221    }
222  }
223  ```
224  
225  ### Safety Features
226  
227  - **Concurrent protection** -- an `asyncio.Lock` prevents multiple simultaneous refresh attempts.
228  - **Atomic file write** -- credentials are written to a temp file first, then renamed, to prevent corruption.
229  - **0600 permissions** -- the credentials file is restricted to the owner only.
230  - **Graceful fallback** -- if the refresh fails for any reason (network error, expired app secret, etc.), mureo continues with the existing token and logs a warning. No tool calls are blocked.
231  
232  ## Interactive Setup Wizard
233  
234  `mureo auth setup` (also called as part of `mureo setup claude-code`) walks you through authentication interactively:
235  
236  1. **Google Ads OAuth** -- Enter Developer Token + Client ID/Secret, open browser for OAuth, select account.
237  2. **Meta Ads OAuth** -- Enter App ID/Secret, open browser for OAuth, obtain Long-Lived Token, select account.
238  3. **MCP configuration** -- Choose global (`~/.claude/settings.json`) or project-level (`.mcp.json`).
239  
240  ### Project-Level MCP Configuration (`.mcp.json`)
241  
242  If you choose project-level placement, `mureo auth setup` creates a `.mcp.json` file in your project root:
243  
244  ```json
245  {
246    "mcpServers": {
247      "mureo": {
248        "command": "python",
249        "args": ["-m", "mureo.mcp"]
250      }
251    }
252  }
253  ```
254  
255  AI agents that support `.mcp.json` (e.g., Claude Code) will automatically discover and connect to the mureo MCP server when working in that project directory.
256  
257  ## Verifying Credentials
258  
259  Use the `mureo auth` commands to verify your setup:
260  
261  ```bash
262  # Show authentication status for all platforms
263  mureo auth status
264  
265  # Check Google Ads credentials (shows masked values)
266  mureo auth check-google
267  
268  # Check Meta Ads credentials (shows masked values)
269  mureo auth check-meta
270  ```
271  
272  Example output for `mureo auth status`:
273  
274  ```
275  === Authentication Status ===
276  
277  Google Ads: Authenticated
278  Meta Ads: Authenticated
279  ```
280  
281  Example output for `mureo auth check-google`:
282  
283  ```json
284  {
285    "developer_token": "***************abcd",
286    "client_id": "123456789.apps.googleusercontent.com",
287    "client_secret": "***************wxyz",
288    "refresh_token": "***************efgh",
289    "login_customer_id": "1234567890"
290  }
291  ```
292  
293  Secrets are masked, showing only the last 4 characters. This lets you verify the right credentials are loaded without exposing them.