Skip to main content

Google Ads - Authentication

๐Ÿ“– Overviewโ€‹

The Google Ads authentication module handles OAuth 2.0 authorization, token management, and automatic token refresh for accessing the Google Ads API. It manages both manager (MCC) and client account credentials with support for long-lived refresh tokens.

Source Files:

  • Controller: external/Integrations/GoogleAds/Controllers/Auth/AuthController.js
  • Model: external/Integrations/GoogleAds/Models/token.js
  • Routes: external/Integrations/GoogleAds/Routes/auth.js

External API: Google OAuth 2.0 + Google Ads API

๐Ÿ—„๏ธ Collections Usedโ€‹

  • Operations: Create, Read, Update
  • Model: external/Integrations/GoogleAds/Models/token.js
  • Usage Context: Store OAuth tokens, manager/client IDs, token expiration timestamps

accountโ€‹

  • Operations: Read
  • Model: external/models/account.js
  • Usage: Validate sub-account relationships for multi-tenant access

userโ€‹

  • Operations: Read
  • Model: external/models/user.js
  • Usage: Fetch user details for notifications

user-configโ€‹

  • Operations: Read
  • Model: external/models/user-config.js
  • Usage: Get notification preferences

๐Ÿ”„ Data Flowโ€‹

OAuth Authentication Flowโ€‹

sequenceDiagram
participant User
participant DashClicks
participant Google
participant DB

User->>DashClicks: GET /auth/login?forward_url
DashClicks->>DashClicks: Create JWT state token
DashClicks->>DashClicks: Check existing token
alt Token Exists
DashClicks-->>User: Redirect to forward_url?status=success
else No Token
DashClicks->>Google: Redirect to OAuth consent
Note over Google: Scope: adwords
User->>Google: Grant permissions
Google->>DashClicks: GET /auth/callback?code&state
DashClicks->>DashClicks: Verify JWT state
DashClicks->>Google: Exchange code for tokens
Google-->>DashClicks: access_token, refresh_token
DashClicks->>DB: Save tokens
DashClicks->>User: Send notification (email/FCM)
DashClicks-->>User: Redirect to forward_url?status=success&token={id}
end

Token Refresh Flowโ€‹

sequenceDiagram
participant Controller
participant TokenModel
participant Google

Controller->>TokenModel: Fetch token
TokenModel-->>Controller: Token data
alt Token Expired
Controller->>Google: POST /token (grant_type=refresh_token)
Google-->>Controller: New access_token
Controller->>TokenModel: Update access_token & generated_at
TokenModel-->>Controller: Updated token
end
Controller->>Google: API Request with access_token

๐Ÿ”ง Business Logic & Functionsโ€‹

login()โ€‹

Purpose: Initiates OAuth 2.0 flow or returns existing token

Source: Controllers/Auth/AuthController.js

External API Endpoint: GET https://accounts.google.com/o/oauth2/v2/auth

Parameters:

  • forward_url (String, Required) - URL to redirect after authentication
  • subaccountid (String, Optional) - Sub-account ID for child account connection

Returns: 302 Redirect

Business Logic Flow:

  1. Validate Required Parameters

    if (!this.req.query.forward_url) {
    return this.res.status(400).json({
    success: false,
    errno: 400,
    message: 'forward_url is required',
    });
    }
  2. Handle Sub-Account

    • Validate sub-account belongs to parent account
    • Switch accountId context to sub-account
  3. Check Existing Token

    const tokenData = await TokenModel.findTokenDoc(accountId);
    if (tokenData) {
    // Token exists, redirect to success
    return this.res.redirect(
    `${successRedirectUrl}?status=success&integration=googleads&token=${tokenId}`,
    );
    }
  4. Create JWT State Token

    const stateData = {
    account_id: accountId,
    forward_url: successRedirectUrl,
    };
    const state = jwt.sign({ data: stateData }, process.env.APP_SECRET);
  5. Build OAuth URL

    const authUrl = this.oauth2Client.generateAuthUrl({
    access_type: 'offline',
    scope: ['https://www.googleapis.com/auth/adwords'],
    state: state,
    prompt: 'consent', // Force consent to get refresh token
    });
  6. Redirect to Google OAuth

    • User grants permissions
    • Google redirects to callback with code and state

OAuth Authorization URL:

GET https://accounts.google.com/o/oauth2/v2/auth
Query Parameters:
- client_id: GOOGLE_CLIENT_ID
- redirect_uri: GOOGLE_ADS_REDIRECT_URL
- response_type: code
- scope: https://www.googleapis.com/auth/adwords
- access_type: offline
- state: {JWT_TOKEN}
- prompt: consent

Error Handling:

  • 400 Missing forward_url: Returns error JSON
  • 400 Invalid sub-account: Sub-account doesn't belong to parent
  • 500 Token Check Error: Logs error, proceeds with OAuth
  • Redirect on Error: Redirects to forward_url?status=error&reason={message}

Example Usage:

// Connect Google Ads for account
GET /v1/e/google/ads/auth/login
?forward_url=https://app.dashclicks.com/integrations

// Connect for sub-account
GET /v1/e/google/ads/auth/login
?forward_url=https://app.dashclicks.com/integrations
&subaccountid=507f1f77bcf86cd799439011

Side Effects:

  • โš ๏ธ Creates JWT state token
  • โš ๏ธ May query existing token from database
  • โš ๏ธ Redirects user to Google OAuth consent page

callback()โ€‹

Purpose: OAuth callback handler - exchanges authorization code for tokens

Source: Controllers/Auth/AuthController.js

External API Endpoint: POST https://oauth2.googleapis.com/token

Parameters:

  • code (String) - Authorization code from Google
  • state (String) - JWT token containing original request parameters

Returns: 302 Redirect to forward_url

Business Logic Flow:

  1. Verify State Token

    let payload;
    try {
    payload = jwt.verify(state, process.env.APP_SECRET);
    } catch (error) {
    return this.res.status(401).json({
    success: false,
    errno: 401,
    message: 'Invalid State',
    });
    }
    const { account_id, forward_url } = payload.data;
  2. Check Existing Token (prevent duplicate connections)

    const existingToken = await TokenModel.findTokenDoc(account_id);
    if (existingToken) {
    // Already connected, redirect to success
    return this.redirectOnAuthentication(payload.data, 'success');
    }
  3. Exchange Authorization Code

    const { tokens } = await this.oauth2Client.getToken(code);
    /*
    tokens = {
    access_token: "ya29...",
    refresh_token: "1//...",
    scope: "https://www.googleapis.com/auth/adwords",
    token_type: "Bearer",
    expiry_date: 1234567890000
    }
    */
  4. Prepare Token Data

    const tokenData = {
    account_id: account_id,
    access_token: tokens.access_token,
    refresh_token: tokens.refresh_token,
    generated_at: moment().toISOString(),
    expires_in: 3600, // 1 hour
    scope: tokens.scope,
    token_type: tokens.token_type,
    };
  5. Save to Database

    const savedToken = await TokenModel.save(tokenData);
  6. Send Notifications

    // Email notification
    await sendMailNotification({
    account_id: account_id,
    integration: 'googleads',
    status: 'connected',
    });

    // FCM push notification
    await sendFCMNotification({
    account_id: account_id,
    title: 'Google Ads Connected',
    body: 'Your Google Ads account has been successfully connected',
    });
  7. Redirect to Success

    this.res.redirect(
    `${forward_url}?status=success&integration=googleads&token=${savedToken.id}&accounts=${account_id}`,
    );

API Request Example:

// Token exchange (handled by googleapis SDK)
POST https://oauth2.googleapis.com/token
Content-Type: application/x-www-form-urlencoded

code={authorization_code}
&client_id={GOOGLE_CLIENT_ID}
&client_secret={GOOGLE_CLIENT_SECRET}
&redirect_uri={GOOGLE_ADS_REDIRECT_URL}
&grant_type=authorization_code

API Response Example:

{
access_token: "ya29.a0AfH6SMBx...",
refresh_token: "1//0gKZ1x2y3z...",
scope: "https://www.googleapis.com/auth/adwords",
token_type: "Bearer",
expiry_date: 1697654400000
}

Error Handling:

  • 401 Invalid State: JWT verification failed
  • 400 Missing Code: Authorization code not provided
  • 500 Token Exchange Error: Google API error
  • Database Save Error: Logged and redirected to error URL

Example Usage:

// OAuth callback from Google
GET /v1/e/google/ads/auth/callback
?code=4/0AZEOvh...
&state=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Side Effects:

  • โš ๏ธ Exchanges OAuth code for access and refresh tokens
  • โš ๏ธ Saves tokens to database
  • โš ๏ธ Sends email notification to account owner
  • โš ๏ธ Sends FCM push notification
  • โš ๏ธ External API call to Google (quota counted)

Token Refresh (Automatic)โ€‹

Purpose: Automatically refresh expired access tokens using refresh token

Triggered By: Any API request when token is expired

Logic:

// Check if token is expired
const generatedAt = moment(tokenData.generated_at);
const expiresIn = tokenData.expires_in || 3600; // Default 1 hour
const now = moment();
const secondsSinceGenerated = now.diff(generatedAt, 'seconds');

if (secondsSinceGenerated >= expiresIn) {
// Token expired, refresh it
this.oauth2Client.setCredentials({
refresh_token: tokenData.refresh_token,
});

const { credentials } = await this.oauth2Client.refreshAccessToken();

// Update in database
await TokenModel.update(tokenData._id, {
access_token: credentials.access_token,
generated_at: moment().toISOString(),
});
}

Token Lifetime:

  • Access Token: 1 hour (3600 seconds)
  • Refresh Token: Long-lived (doesn't expire unless revoked)

Refresh Frequency:

  • Automatic refresh when access token expires
  • No manual refresh needed
  • Refresh token used to obtain new access token

๐Ÿ”€ Integration Pointsโ€‹

Internal Servicesโ€‹

  • Campaign Management: Uses tokens to fetch/manage campaigns
  • Ad Group Operations: Uses tokens for ad group API calls
  • Keyword Management: Uses tokens for keyword operations
  • MCC Operations: Uses tokens for manager account access

External API Dependenciesโ€‹

  • Provider: Google OAuth 2.0 + Google Ads API
  • Endpoints:
    • OAuth: https://accounts.google.com/o/oauth2/v2/auth
    • Token: https://oauth2.googleapis.com/token
    • API: https://googleads.googleapis.com/
  • Authentication: Bearer tokens with automatic refresh
  • Rate Limits: Varies by API version and developer token status

Notificationsโ€‹

Email Notification:

  • Sent on successful connection
  • Template: Integration connected notification
  • Includes integration name and account details

FCM Push Notification:

  • Sent to mobile devices
  • Title: "Google Ads Connected"
  • Body: Success message with account info

๐Ÿงช Edge Cases & Special Handlingโ€‹

Token Expirationโ€‹

Issue: Access tokens expire after 1 hour
Handling:

  • Automatic refresh using refresh token
  • Refresh happens transparently before API calls
  • No user intervention required

Refresh Token Revocationโ€‹

Issue: User may revoke access in Google account settings
Handling:

  • API calls return 401 Unauthorized
  • User must re-authenticate
  • Requires new OAuth flow

Duplicate Connection Attemptsโ€‹

Issue: User may try to connect already-connected account
Handling:

  • Check for existing token before OAuth flow
  • Redirect to success immediately if token exists
  • Prevents duplicate token records

Sub-Account Validationโ€‹

Issue: Users may provide invalid sub-account IDs
Handling:

  • Validate sub-account belongs to parent
  • Return 400 error for invalid relationships
  • Prevents unauthorized access

State Token Tamperingโ€‹

Issue: Malicious users may modify state parameter
Handling:

  • JWT signature verification
  • Return 401 for invalid signatures
  • Prevents CSRF attacks

โš ๏ธ Important Notesโ€‹

  • ๐Ÿ” OAuth Scopes: Requires adwords scope for full API access
  • ๐Ÿ’ฐ Developer Token: Requires approved Google Ads developer token
  • โฑ๏ธ Token Lifetime: Access tokens expire after 1 hour
  • ๐Ÿ”„ Automatic Refresh: Tokens refreshed automatically on expiration
  • ๐Ÿ“ Logging: All auth operations logged with initiator
  • ๐Ÿšจ Error Handling: OAuth errors redirect to forward_url with error reason
  • ๐Ÿ”— Notifications: Email and FCM notifications sent on successful connection
  • ๐ŸŽฏ Account Isolation: Tokens stored per DashClicks account


๐ŸŽฏ Authentication Checklistโ€‹

Before Connecting:

  • Set GOOGLE_CLIENT_ID environment variable
  • Set GOOGLE_CLIENT_SECRET environment variable
  • Configure GOOGLE_ADS_REDIRECT_URL
  • Obtain approved developer token
  • Enable Google Ads API in Google Cloud Console

During Connection:

  • Provide forward_url parameter
  • Grant adwords scope permission
  • Verify callback receives code and state

After Connection:

  • Verify token saved in database
  • Check access_token and refresh_token present
  • Test API call with token
  • Confirm email and FCM notifications received
๐Ÿ’ฌ

Documentation Assistant

Ask me anything about the docs

Hi! I'm your documentation assistant. Ask me anything about the docs!

I can help you with:
- Code examples
- Configuration details
- Troubleshooting
- Best practices

Try asking: How do I configure the API?
09:30 AM