# Lucos IDE — Google Sign-In / Sign-Up Integration Guide

**Audience:** Frontend / IDE client developers  
**Service:** `api.lucos.com` (Adpilot / Lucos gateway)  
**Status:** Implemented on gateway  
**Related:** [API-REFERENCE.md](./API-REFERENCE.md) · [UI-API-INTEGRATION.md](./UI-API-INTEGRATION.md)

---

## Why this flow exists

The web app can finish Google login inside the browser, then call:

`POST /api/v1/auth/authenticate` with `{ authType: "google", idToken, google_id, email }`

**Lucos IDE cannot do that reliably**, because:

- Google consent happens in the **system browser**
- Lucos JWTs must land inside the **desktop app**, not only in the browser
- Cookie sessions are not useful for Electron / VS Code fork clients

So the IDE uses a **gateway-hosted Google OAuth callback** that eventually redirects into the IDE via a deep link (`lucos://...`) or a local loopback URL.

---

## High-level flow

```text
┌────────────┐   1. ide-start        ┌─────────────┐
│  Lucos IDE │ ─────────────────────▶│   Gateway   │
│            │◀──── { redirectUrl } ─│             │
└─────┬──────┘                       └──────┬──────┘
      │ 2. open redirectUrl                  │
      │    in system browser                 │
      ▼                                      │
┌────────────┐  3. user consents             │
│   Google   │───────────────────────────────┘
└─────┬──────┘  4. redirect with ?code&state
      │
      ▼
┌─────────────┐  5. exchange code → issue Lucos JWTs
│   Gateway   │
│ ide-callback│
└─────┬───────┘
      │ 6. 302 → lucos://auth/callback?token=...&refreshToken=...
      ▼
┌────────────┐  7. store tokens → call /auth/me → hand JWT to daemon
│  Lucos IDE │
└────────────┘
```

**Important:** The IDE never talks to Google’s token endpoint directly. The gateway owns the client secret and code exchange.

---

## Base URLs

| Environment | Gateway base |
|---|---|
| Local | `http://localhost:3007` |
| Staging / Prod | e.g. `https://api.lucos.com` or `https://stagingapi.lucos.com` |

All paths below are under **`/api/v1`**.

| Step | Endpoint |
|---|---|
| Start | `GET` or `POST` `/api/v1/auth/google/ide-start` |
| Callback (Google → gateway) | `GET` `/api/v1/auth/google/ide-callback` |
| Validate session after login | `GET` `/api/v1/auth/me` |
| Refresh access token | `POST` `/api/v1/auth/refresh` |
| Logout | `POST` `/api/v1/auth/logout` |

Frontend / IDE must **not** call `/api/v1/auth/google/ide-callback` itself. Google redirects the browser there.

---

## Step 1 — Start OAuth (`ide-start`)

### Request

**Option A — GET**

```http
GET /api/v1/auth/google/ide-start
```

**Option B — POST**

```http
POST /api/v1/auth/google/ide-start
Content-Type: application/json
```

### Optional parameters

| Field | Where | Type | Description |
|---|---|---|---|
| `loopbackPort` | query or JSON body | integer `1024–65535` | If set, final redirect goes to `http://127.0.0.1:<port>/callback` instead of `lucos://...` |
| `deepLink` | query or JSON body | string | Override success deep link. Must use an allowed scheme (default: `lucos`). Default is `lucos://auth/callback`. |

### Example

```bash
curl -sS "http://localhost:3007/api/v1/auth/google/ide-start"
```

```bash
curl -sS -X POST "http://localhost:3007/api/v1/auth/google/ide-start" \
  -H "Content-Type: application/json" \
  -d '{"loopbackPort":43123}'
```

### Success response — `200`

```json
{
  "success": true,
  "redirectUrl": "https://accounts.google.com/o/oauth2/v2/auth?client_id=...&redirect_uri=...&response_type=code&scope=openid%20email%20profile&state=...&prompt=select_account"
}
```

### Error responses

| Status | When |
|---|---|
| `400` | Invalid `loopbackPort` / `deepLink` |
| `503` | Google IDE OAuth env not configured on gateway |

---

## Step 2 — Open Google in the system browser

IDE should open `redirectUrl` **outside** the app webview when possible (OS default browser).

Pseudo-code:

```ts
const gatewayUrl = "http://localhost:3007"; // or configured lucos.cloud.gatewayUrl

const res = await fetch(`${gatewayUrl}/api/v1/auth/google/ide-start`);
const data = await res.json();

if (!data.success || !data.redirectUrl) {
  throw new Error("Failed to start Google IDE login");
}

// Electron / desktop: open external browser
await shell.openExternal(data.redirectUrl);
```

Do **not** build the Google authorize URL yourself unless product explicitly requires it. Prefer `ide-start` so `state` is signed by the gateway.

---

## Step 3 — Google redirects to gateway callback

Google redirects the browser to:

```text
GET {GATEWAY}/api/v1/auth/google/ide-callback?code=...&state=...
```

or on cancel/deny:

```text
GET {GATEWAY}/api/v1/auth/google/ide-callback?error=access_denied&error_description=...&state=...
```

The gateway then:

1. Validates signed `state` (CSRF + 10-minute expiry)
2. Exchanges `code` for Google tokens (server-side, with client secret)
3. Verifies Google `id_token`
4. Creates / links Lucos user (same logic as web Google auth)
5. Issues Lucos access JWT + refresh token
6. HTTP `302` redirects into the IDE

---

## Step 4 — What `ide-callback` returns to the IDE

This endpoint does **not** return JSON to the app.

It returns an **HTTP redirect** to one of:

### Deep link (default)

```text
lucos://auth/callback?success=true&token=<JWT>&refreshToken=<REFRESH>&userId=<ID>&email=<EMAIL>&isNewUser=true|false
```

### Loopback (if `loopbackPort` was sent on start)

```text
http://127.0.0.1:<loopbackPort>/callback?success=true&token=...&refreshToken=...&userId=...&email=...&isNewUser=...
```

### Query parameters on success

| Param | Type | Description |
|---|---|---|
| `success` | `"true"` | Present on successful auth |
| `token` | string | Lucos access JWT (Bearer token for APIs) |
| `refreshToken` | string | Opaque refresh token for `/auth/refresh` |
| `userId` | string | Lucos user id |
| `email` | string | Authenticated email |
| `isNewUser` | `"true"` \| `"false"` | Whether a new Lucos account was created |

### Query parameters on failure

| Param | Type | Description |
|---|---|---|
| `error` | string | e.g. `access_denied`, `missing_code`, `server_error` |
| `error_description` | string | Human-readable detail when available |

Example failure:

```text
lucos://auth/callback?error=access_denied&error_description=User%20denied%20access
```

---

## Step 5 — IDE handling after deep link / loopback

### A) Custom URL scheme (`lucos://`) — recommended default

1. Register OS / Electron protocol handler for `lucos://`
2. On `lucos://auth/callback?...`, parse query params
3. If `error` is present → show login error UI
4. If `success=true` and `token` present:
   - Persist `token` + `refreshToken` securely (OS keychain preferred)
   - Call `GET /api/v1/auth/me` with `Authorization: Bearer <token>`
   - Hand JWT to local daemon (`SetCloudCredentials`) if Lucos daemon is used
   - Navigate to authenticated IDE state

### B) Loopback listener — alternative

Useful when custom protocol registration is unavailable (some Linux setups / debugging).

1. Before calling `ide-start`, bind an ephemeral local HTTP server on `127.0.0.1`
2. Pass that port as `loopbackPort`
3. Handle `GET /callback?...` once, then shut down the server
4. Continue same token storage / `/auth/me` flow as above

---

## After login — use the session

### Call authenticated APIs

```http
Authorization: Bearer <token>
```

Do **not** rely on cookies for IDE clients.

### Validate session

```http
GET /api/v1/auth/me
Authorization: Bearer <token>
```

Example success:

```json
{
  "success": true,
  "user": {
    "id": "507f...",
    "name": "Jane Doe",
    "email": "jane@example.com",
    "role": "USER",
    "createdAt": "...",
    "updatedAt": "..."
  }
}
```

### Refresh access token

```http
POST /api/v1/auth/refresh
Content-Type: application/json

{
  "refreshToken": "<refreshToken from callback>"
}
```

Response includes a new `token` + `refreshToken` (rotation). Store both again.

### Logout

```http
POST /api/v1/auth/logout
Authorization: Bearer <token>
```

Then clear local keychain / in-memory daemon credentials.

---

## Sequence diagram

```mermaid
sequenceDiagram
    participant IDE as Lucos IDE
    participant GW as Gateway
    participant G as Google
    participant D as Local Daemon

    IDE->>GW: GET /api/v1/auth/google/ide-start
    GW-->>IDE: { redirectUrl }
    IDE->>G: Open redirectUrl (system browser)
    G->>GW: GET /api/v1/auth/google/ide-callback?code&state
    GW->>G: Exchange code (server-side)
    GW->>GW: Upsert user + issue JWTs
    GW-->>IDE: 302 lucos://auth/callback?token&refreshToken
    IDE->>IDE: Store tokens securely
    IDE->>GW: GET /api/v1/auth/me (Bearer token)
    IDE->>D: SetCloudCredentials(access_token)
```

---

## How this differs from web Google auth

| | Web (`lucos.com`) | IDE (this flow) |
|---|---|---|
| Google redirect target | Frontend `/google-callback` page | Gateway `/api/v1/auth/google/ide-callback` |
| How Lucos JWT is obtained | Frontend POSTs `idToken` to `/authenticate` | Gateway exchanges `code` and redirects with JWT |
| Transport of tokens into client | SPA memory / cookies | Deep link or loopback query params |
| Client secret usage | Not needed in browser for id_token path | Required on gateway for code exchange |
| Cookies | Used by web | Ignore for IDE |

Use **web authenticate** for browser. Use **ide-start + ide-callback** for desktop IDE.

---

## Frontend / IDE checklist

- [ ] Read gateway base URL from settings (`lucos.cloud.gatewayUrl` or env)
- [ ] Call `ide-start` and open `redirectUrl` externally
- [ ] Register `lucos://` protocol handler **or** implement loopback listener
- [ ] Parse success / error query params from callback URL
- [ ] Store `token` + `refreshToken` in secure storage (not plaintext settings)
- [ ] Call `/auth/me` to confirm session
- [ ] Pass access JWT to local daemon if agent features are enabled
- [ ] Implement refresh on near-expiry / `401`
- [ ] Clear tokens on logout / auth failure

---

## CORS (IDE renderer)

If the IDE calls `ide-start` from the Electron **workbench renderer**, the browser sends:

```text
Origin: vscode-file://vscode-app
```

The gateway must allow that origin (`CORS_ALLOW_ELECTRON_IDE=true`, default) or list it in `CORS_ORIGINS`.

Preferred longer-term approach: call `ide-start` from the **Electron main / Node** side (no CORS), then open `redirectUrl` with `shell.openExternal`.

## Security notes for implementers

1. Treat deep-link / loopback query tokens as **credentials**. Do not log full URLs.
2. Prefer OS keychain over plaintext config files.
3. Prefer `Authorization: Bearer` for subsequent API calls; do not depend on cookies.
4. `state` is signed by the gateway and expires in ~10 minutes — restart flow if the user delays too long.
5. Only accept deep-link schemes allowlisted by gateway (`GOOGLE_IDE_ALLOWED_SCHEMES`, default `lucos`).
6. Loopback must be `127.0.0.1` only (gateway enforces this when redirecting).

---

## Gateway configuration (for local/staging setup)

These must be set on the API gateway (not in the frontend repo):

| Env var | Example | Notes |
|---|---|---|
| `GOOGLE_CLIENT_ID` | Google OAuth client id | Shared with web if desired |
| `GOOGLE_CLIENT_SECRET` | Google client secret | Required for IDE code flow |
| `GOOGLE_IDE_CALLBACK_URL` | `http://localhost:3007/api/v1/auth/google/ide-callback` | Must be listed in Google Cloud Console redirect URIs |
| `GOOGLE_IDE_SUCCESS_REDIRECT_URI` | `lucos://auth/callback` | Default deep link |
| `GOOGLE_IDE_ALLOWED_SCHEMES` | `lucos` | Allowed custom schemes |

Google Cloud Console → OAuth client → **Authorized redirect URIs** must include the exact `GOOGLE_IDE_CALLBACK_URL`.

---

## Quick test (manual)

1. Ensure gateway is running with the env vars above.
2. Call start:

```bash
curl -sS http://localhost:3007/api/v1/auth/google/ide-start | jq
```

3. Open `redirectUrl` in a browser and complete Google login.
4. Confirm browser is redirected to:

```text
lucos://auth/callback?success=true&token=...&refreshToken=...
```

(If the OS does not yet handle `lucos://`, the browser may show a protocol error — that still proves the gateway redirect is correct.)

5. Copy `token` and validate:

```bash
curl -sS http://localhost:3007/api/v1/auth/me \
  -H "Authorization: Bearer <token>" | jq
```

---

## FAQ

**Q: Does `ide-callback` return JSON?**  
No. It returns an HTTP `302` redirect into the IDE.

**Q: Should the IDE call `/authenticate` after deep link?**  
No. Tokens are already issued. Next call should be `/auth/me` (and daemon credential handoff).

**Q: Can we reuse the web `/google-callback` page for IDE?**  
Not for the desktop handoff. The IDE needs gateway → deep link / loopback so tokens reach the desktop process.

**Q: Sign-up vs sign-in?**  
Same Google flow. Gateway creates the Lucos user on first Google login (`isNewUser=true`) or signs into an existing linked account (`isNewUser=false`).

**Q: Path typo — frontend said `/auth/google/ide-callback`?**  
Full gateway path is `/api/v1/auth/google/ide-callback`. Use that exact path in Google Console and docs.

---

## See also

- **ChatGPT / MCP connect OAuth (TW-296)** — separate from this IDE deep-link flow: [MCP-SECURE-TUNNEL-OIDC.md](./MCP-SECURE-TUNNEL-OIDC.md) (`/api/v1/auth/mcp/*`).
