Skip to main content

OAuth 2.0 Authentication

��� Authentication Method

OAuth 2.0 Authorization Code Flow with automatic token refresh

��� Required Configuration

Environment Variables:

VariableDescriptionRequired
GOOGLE_ANALYTICS_CLIENT_IDOAuth 2.0 Client ID
GOOGLE_ANALYTICS_CLIENT_SECRETOAuth 2.0 Client Secret
GOOGLE_ANALYTICS_CALLBACK_URLOAuth callback URL (production)
PROXY_URLOAuth callback URL (development)
APP_SECRETJWT secret for state token encryption
NODE_ENVEnvironment (development/production)

OAuth Scopes:

  • https://www.googleapis.com/auth/analytics - Full Analytics access
  • https://www.googleapis.com/auth/analytics.readonly - Read-only access
  • profile - User profile information
  • email - User email address

��� API Endpoints

EndpointMethodDescription
/v1/integrations/google/analytics/auth/loginGETInitiate OAuth flow
/v1/integrations/google/analytics/auth/callbackGETOAuth callback endpoint
/v1/integrations/google/analytics/auth/useranalyticsconfigPOSTSave user analytics config
/v1/integrations/google/analytics/authDELETEDisconnect integration

��� OAuth Flow

1. Initiate OAuth

Endpoint: GET /v1/integrations/google/analytics/auth/login

Query Parameters:

  • forward_url (required) - Redirect URL after OAuth

Process:

  1. Delete existing token (force re-auth)
  2. Create JWT state token with account_id, forward_url, created_by
  3. Generate Google OAuth URL
  4. Redirect user to Google consent screen

Example:

GET /v1/integrations/google/analytics/auth/login?forward_url=https://app.dashclicks.com/analytics
Authorization: Bearer {jwt_token}

Redirects to:

https://accounts.google.com/o/oauth2/v2/auth?
client_id=123456789-abc.apps.googleusercontent.com&
redirect_uri=https://api.dashclicks.com/v1/e/google/analytics/auth/callback&
response_type=code&
scope=analytics%20profile%20email&
access_type=offline&
prompt=consent&
state=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

2. OAuth Callback

Endpoint: GET /v1/integrations/google/analytics/auth/callback

Query Parameters:

  • code (required) - Authorization code
  • state (required) - JWT state token

Process:

  1. Verify JWT state token
  2. Exchange authorization code for tokens
  3. Call Google People API to get user email
  4. Save token in google.analytics.tokens collection
  5. Redirect to forward_url with status

Token Response:

{
access_token: "ya29.a0AfH6SMB...",
refresh_token: "1//0gZ1X2Y3Z4...",
expiry_date: 1704844800000,
id_token: "eyJhbGciOiJSUzI1NiIsImtpZCI6...",
scope: "https://www.googleapis.com/auth/analytics email profile",
token_type: "Bearer"
}

Redirect URLs:

# Success
https://app.dashclicks.com/analytics?status=success&integration=google&token={document_id}

# Error
https://app.dashclicks.com/analytics?status=error&integration=google&reason={error_message}

3. Save User Configuration

Endpoint: POST /v1/integrations/google/analytics/auth/useranalyticsconfig

Request:

{
"accountid": "167475709",
"version": "v3",
"propertyid": "UA-167475709-1",
"viewid": "219224050"
}

Process:

  1. Verify account access
  2. Update token with version
  3. Upsert user config
  4. Send FCM notification

Response:

{
"success": true,
"message": "SUCCESS",
"data": {
"acknowledged": true,
"modifiedCount": 1
}
}

4. Disconnect

Endpoint: DELETE /v1/integrations/google/analytics/auth

Process:

  1. Delete user config
  2. Soft delete token (set deleted date)
  3. Send email and FCM notifications

Response:

{
"success": true,
"message": "SUCCESS"
}

⚡ Token Management

Automatic Token Refresh

Purpose: Ensure valid access token for every API call

Implementation:

const checkAccessTokenValidity = async (expirationTime, accessToken, refreshToken, docId) => {
const currentTimeStamp = new Date().getTime();

if (currentTimeStamp > expirationTime) {
// Token expired - refresh it
const tokenData = await provider.getNewAccessToken(refreshToken);

const newTokenData = {
access_token: tokenData.access_token,
expiry_date: tokenData.expires_in * 1000 + currentTimeStamp,
refresh_token: refreshToken,
id_token: tokenData.id_token,
scope: tokenData.scope,
token_type: tokenData.token_type,
};

await keysModel.update({ _id: docId }, { token: newTokenData });
return tokenData.access_token;
}

return accessToken;
};

Called Before Every API Request:

const accessToken = await checkAccessTokenValidity(
token.expiry_date,
token.access_token,
token.refresh_token,
docId,
);

Token Invalidation

Triggers:

  • 401 Unauthorized from Google API
  • 403 Forbidden from Google API
  • invalid_grant error

Process:

if (error?.response?.data?.error == 'invalid_grant' || errorCode == '401' || errorCode == '403') {
await keysModel.updateMany(
{ account_id: ObjectId(accountId) },
{ $set: { token_invalidated: true } },
);

throw {
message:
'Google Analytics token is expired. Please reconnect your analytics account in settings.',
additional_info: 'TOKEN_INVALIDATED',
};
}

��� Keys Model

Purpose: Manage Google Analytics OAuth tokens

Methods:

// Find token by account
Keys.find(accountID, owner);
// Returns: { docId, data: token_document } or false

// Save new token
Keys.save(tokenData);
// Returns: Saved document with id

// Update token
Keys.update(condition, authData);
// Returns: Updated document

// Update many (for invalidation)
Keys.updateMany(condition, updateData);
// Returns: MongoDB update result

// Delete token
Keys.delete(docId);
// Returns: true on success

��� Notifications

Integration Connected

Channels: FCM/Bell, Browser push

Notification:

{
title: "Google Analytics integration added",
body: "A new Google Analytics integration has been added.",
module: "projects",
type: "project_added",
data: { subType: "bell" }
}

Integration Disconnected

Channels: FCM/Bell, Email

Notification:

{
title: "Google Analytics integration is Disconnected",
body: "Google Analytics integration has been disconnected.",
module: "analytics",
type: "integration_disconnected",
click_action: "https://app.dashclicks.com/analytics",
data: { subType: "bell" }
}

Email:

{
subject: "Google Analytics integration disconnected",
content: "Google Analytics integration has been disconnected.",
origin: "analytics",
recipients: [{
name: user.name,
email: user.email
}]
}

��� Error Handling

ErrorCauseHandling
400 forward_url requiredMissing parameterReturn error before OAuth
400 Record Not foundNo token in databaseUser must authenticate
403 Invalid StateJWT token invalidRetry OAuth flow
401/403 from GoogleToken expired/revokedSet token_invalidated flag
invalid_grantRefresh token revokedPrompt re-authentication

⚠️ Important Notes

  • ��� State Token Security: JWT-encrypted to prevent CSRF attacks
  • ��� Automatic Refresh: Access tokens renewed before expiration
  • ���️ Soft Delete: Tokens soft-deleted for audit trail
  • ��� Force Re-auth: Login endpoint always deletes existing token
  • ��� Email Retrieval: Uses Google People API
  • ��� Notification Control: Respects user preferences
  • ��� Sub-account Support: Works with DashClicks sub-accounts
  • Fast Checks: Token validity checked in less than 1ms
💬

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