Authentication

Google OAuth

Connect Google Business Profile accounts using OAuth 2.0

Google OAuth enables users to authorize Synoveo to access their Google Business Profile. This is used for dashboard login and connecting GBP accounts.

Overview

The OAuth flow grants Synoveo permission to:

  • Read your Google Business Profile data
  • Update profile information, hours, and attributes
  • Create and manage posts
  • Access reviews and insights
  • Sync data between your source and GBP

Scope requested: https://www.googleapis.com/auth/business.manage


OAuth Flow

┌──────────┐     ┌──────────┐     ┌──────────┐     ┌──────────┐
│  User    │     │ Synoveo  │     │ Synoveo  │     │  Google  │
│ Browser  │     │Dashboard │     │   API    │     │  OAuth   │
└────┬─────┘     └────┬─────┘     └────┬─────┘     └────┬─────┘
     │                │                │                │
     │ Click Login    │                │                │
     │───────────────>│                │                │
     │                │                │                │
     │                │ GET OAuth URL  │                │
     │                │───────────────>│                │
     │                │                │                │
     │                │ { authUrl }    │                │
     │                │<───────────────│                │
     │                │                │                │
     │ Redirect to Google              │                │
     │<───────────────│                │                │
     │                │                │                │
     │ Authorize      │                │                │
     │───────────────────────────────────────────────>│
     │                │                │                │
     │ Redirect with code              │                │
     │<───────────────────────────────────────────────│
     │                │                │                │
     │ POST callback  │                │                │
     │───────────────>│                │                │
     │                │                │                │
     │                │ Exchange code  │                │
     │                │───────────────>│                │
     │                │                │ Get tokens     │
     │                │                │───────────────>│
     │                │                │                │
     │                │                │ { tokens }     │
     │                │                │<───────────────│
     │                │                │                │
     │                │ { jwt, user }  │                │
     │                │<───────────────│                │
     │                │                │                │
     │ Set cookie, redirect            │                │
     │<───────────────│                │                │
     │                │                │                │

Step 1: Generate OAuth URL

Request an OAuth URL to redirect the user to Google.

POST /api/v1/auth/google-oauth-url HTTP/1.1
Host: api.synoveo.com
Content-Type: application/json

{
  "return_to": "https://app.synoveo.com/dashboard",
  "force_consent": false
}

Request Parameters:

ParameterTypeRequiredDescription
return_tostringNoURL to redirect after successful auth
force_consentbooleanNoForce consent screen for new refresh token

Response:

{
  "status": "ok",
  "data": {
    "authUrl": "https://accounts.google.com/o/oauth2/v2/auth?client_id=...&redirect_uri=...&scope=...&state=...",
    "projectId": "synoveo-prod",
    "mode": "production"
  }
}

OAuth URL Parameters

The generated URL includes:

ParameterValueDescription
client_idSynoveo's Google Client IDIdentifies the application
redirect_uriRegistered callback URLWhere Google redirects after auth
scopeopenid email profile https://www.googleapis.com/auth/business.manageRequested permissions
response_typecodeAuthorization code flow
access_typeofflineRequest refresh token
promptselect_account or select_account consentAccount picker / consent screen
include_granted_scopestrueIncremental authorization
stateJWT tokenCSRF protection, contains return_to

Step 2: User Authorization

Redirect the user to the authUrl. Google will:

  1. Show account picker (select Google account)
  2. Show consent screen (if force_consent: true or first authorization)
  3. Redirect to callback URL with authorization code

Step 3: Exchange Authorization Code

After Google redirects back, exchange the code for tokens.

POST /api/v1/auth/google/callback HTTP/1.1
Host: api.synoveo.com
Content-Type: application/json

{
  "code": "4/0Ab32j90xYz...",
  "state": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

Request Parameters:

ParameterTypeRequiredDescription
codestringYesAuthorization code from Google
statestringYesState token from OAuth URL

Response:

{
  "status": "ok",
  "data": {
    "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
    "expires_in": 604800,
    "user": {
      "id": 570,
      "email": "user@gmail.com",
      "name": "John Doe",
      "picture": "https://lh3.googleusercontent.com/a/...",
      "plan": "pro",
      "email_verified": true
    },
    "is_new_user": false,
    "return_to": "https://app.synoveo.com/dashboard"
  }
}

Response Fields:

FieldTypeDescription
access_tokenstringJWT token for API requests
expires_innumberToken TTL in seconds
userobjectUser profile information
is_new_userbooleanTrue if account was just created
return_tostringOriginal redirect URL

Use force_consent: true to:

  • Get a new refresh token
  • Fix "token expired" or "invalid grant" errors
  • Reconnect after revoking access in Google settings
POST /api/v1/auth/google-oauth-url HTTP/1.1
Host: api.synoveo.com
Content-Type: application/json

{
  "force_consent": true
}

When force_consent is true:

  • Google shows the full consent screen
  • A new refresh token is issued
  • Previous refresh token is invalidated

Token Storage

Google OAuth tokens are stored encrypted:

FieldDescription
access_token_encAES-256-GCM encrypted access token
access_token_ivEncryption initialization vector
access_token_tagGCM authentication tag
refresh_token_encAES-256-GCM encrypted refresh token
refresh_token_ivEncryption initialization vector
refresh_token_tagGCM authentication tag
expires_atAccess token expiration timestamp

Synoveo automatically:

  • Refreshes access tokens before expiration
  • Preserves refresh tokens across OAuth flows
  • Sends alerts before refresh token expiration (~6 months)

New User Flow

When a new user signs in via Google OAuth:

  1. Account Created - User record created with plan: "lite"
  2. Trial Started - 14-day trial with Pro features (if enabled)
  3. Email Sent - Welcome email with trial information
  4. Locations Synced - Google Business locations imported
{
  "status": "ok",
  "data": {
    "is_new_user": true,
    "user": {
      "id": 571,
      "email": "newuser@gmail.com",
      "plan": "lite",
      "trial_status": "trialing",
      "trial_ends_at": "2025-02-01T00:00:00Z"
    }
  }
}

Location Sync

After successful OAuth, Synoveo automatically:

  1. Fetches all Google Business accounts
  2. Imports locations within plan limits
  3. Stores verification status
  4. Enables sync for connected locations

Account Hierarchy

{
  "accounts": [
    {
      "name": "accounts/123456789",
      "accountName": "My Business Group",
      "type": "LOCATION_GROUP",
      "locations": [
        {
          "name": "locations/987654321",
          "title": "Downtown Store",
          "address": "123 Main St, City, State 12345",
          "verificationState": "VERIFIED"
        }
      ]
    }
  ]
}

Disconnecting

Via Synoveo

DELETE /api/v1/auth/google-connection HTTP/1.1
Host: api.synoveo.com
Authorization: Bearer <user_jwt_token>

This removes:

  • Stored Google tokens
  • Connected locations
  • Sync settings

Via Google

Users can also revoke access in Google:

  1. Go to Google Account Security
  2. Click Third-party apps with account access
  3. Find Synoveo and click Remove Access

After revoking in Google, use force_consent: true to reconnect.


Error Handling

Authorization Errors

ErrorDescriptionSolution
access_deniedUser denied consentRetry OAuth flow
invalid_grantCode expired or already usedRequest new OAuth URL
invalid_requestMissing or invalid parametersCheck request format

Token Errors

{
  "status": "error",
  "error": {
    "code": "GOOGLE_AUTH_ERROR",
    "message": "Failed to exchange authorization code",
    "details": {
      "google_error": "invalid_grant",
      "google_description": "Token has been expired or revoked"
    }
  }
}

State Token Errors

{
  "status": "error",
  "error": {
    "code": "AUTH_INVALID_TOKEN",
    "message": "Invalid or expired state token"
  }
}

State tokens expire after 15 minutes. If expired, restart the OAuth flow.


Refresh Token Lifecycle

Google refresh tokens:

  • Valid for approximately 6 months
  • Can be revoked by user in Google settings
  • May be invalidated if user changes password
  • New token issued with force_consent: true

Expiration Alerts

Synoveo sends notifications before refresh token expiration:

  • 7 days before: Email warning
  • 3 days before: Email + dashboard alert
  • Expired: Prompts reconnection in dashboard

Security

State Token (CSRF Protection)

The state parameter is a JWT containing:

{
  "return_to": "https://app.synoveo.com/dashboard",
  "ts": 1703030400000
}
  • Signed with HS256 using JWT_SECRET
  • Expires in 15 minutes
  • Prevents CSRF attacks

Redirect URL Validation

Only whitelisted origins are allowed:

  • https://app.synoveo.com
  • https://synoveo.com
  • Development: http://localhost:3000

Invalid redirect URLs are rejected with an error.


Dashboard Integration

The Synoveo dashboard handles OAuth automatically:

// React component example
function LoginButton() {
  const handleLogin = async () => {
    // 1. Get OAuth URL
    const response = await fetch('/api/v1/auth/google-oauth-url', {
      method: 'POST',
      body: JSON.stringify({ return_to: window.location.href })
    })
    const { authUrl } = await response.json()

    // 2. Redirect to Google
    window.location.href = authUrl
  }

  return <button onClick={handleLogin}>Sign in with Google</button>
}

// Callback page handles code exchange automatically

Scopes Reference

ScopeDescription
openidOpenID Connect authentication
emailUser's email address
profileUser's name and picture
https://www.googleapis.com/auth/business.manageFull GBP access

The business.manage scope grants:

  • Read/write access to business information
  • Post creation and management
  • Review access
  • Media management
  • Insights and analytics

On this page