From 36915a3f7513a35a6c3b1eff954fe963e22c2f49 Mon Sep 17 00:00:00 2001 From: Unchained Date: Mon, 30 Mar 2026 17:56:49 +0200 Subject: [PATCH] feat: add OAuth 2.0 support for GSC monitoring - Updated monitor.py to support both OAuth and Service Account - Created setup-oauth-local.py for easy local authorization - Created cronjob-oauth.yaml for OAuth-based deployment - Updated README with both authentication options - OAuth is now the recommended method (no key file needed) --- scripts/gsc-monitoring/README.md | 76 +++++++++ scripts/gsc-monitoring/cronjob-oauth.yaml | 32 ++++ scripts/gsc-monitoring/monitor.py | 63 ++++++-- scripts/gsc-monitoring/setup-oauth-local.py | 164 ++++++++++++++++++++ scripts/gsc-monitoring/setup-oauth.py | 133 ++++++++++++++++ 5 files changed, 458 insertions(+), 10 deletions(-) create mode 100644 scripts/gsc-monitoring/cronjob-oauth.yaml create mode 100644 scripts/gsc-monitoring/setup-oauth-local.py create mode 100644 scripts/gsc-monitoring/setup-oauth.py diff --git a/scripts/gsc-monitoring/README.md b/scripts/gsc-monitoring/README.md index cab6e58..ec1326b 100644 --- a/scripts/gsc-monitoring/README.md +++ b/scripts/gsc-monitoring/README.md @@ -8,6 +8,82 @@ This setup creates an automated monitoring system for Google Search Console that 2. Access to Google Search Console for manoonoils.com 3. kubectl access to your Kubernetes cluster +## Authentication Methods + +Choose one of the following authentication methods: + +### Option A: OAuth 2.0 (Recommended - No Service Account Key) + +This is the **easiest method** if you can't create service account keys. + +#### Step 1: Enable Search Console API +1. Go to https://console.cloud.google.com +2. Create/select project: `manoonoils-monitoring` +3. Go to **APIs & Services → Library** +4. Search: "Google Search Console API" +5. Click: **Enable** + +#### Step 2: Create OAuth Credentials +1. Go to **APIs & Services → Credentials** +2. Click: **Create Credentials → OAuth client ID** +3. Click: **Configure Consent Screen** +4. User Type: **External** +5. Fill in: + - App name: `ManoonOils GSC Monitor` + - User support email: your email + - Developer contact: your email +6. Click: **Save and Continue** (3 times) +7. Click: **Back to Dashboard** +8. Back on Credentials page +9. Click: **Create Credentials → OAuth client ID** +10. Application type: **Desktop app** +11. Name: `GSC Desktop Client` +12. Click: **Create** +13. Click: **DOWNLOAD JSON** + +#### Step 3: Run Local Authorization +On your local machine (laptop): + +```bash +# Go to the monitoring directory +cd scripts/gsc-monitoring + +# Install dependencies +pip3 install google-auth google-auth-oauthlib google-auth-httplib2 google-api-python-client + +# Run the OAuth setup +python3 setup-oauth-local.py +``` + +This will: +- Open a browser for you to authorize the app +- Generate a `gsc-oauth-credentials.json` file +- The refresh token never expires! + +#### Step 4: Deploy to Kubernetes + +```bash +# Copy the credentials to server +scp gsc-oauth-credentials.json doorwaysftw:/tmp/ + +# Create the secret +ssh doorwaysftw "kubectl create secret generic gsc-oauth-credentials \ + --namespace=manoonoils \ + --from-file=oauth-credentials.json=/tmp/gsc-oauth-credentials.json" + +# Deploy the monitoring +ssh doorwaysftw "kubectl apply -f -" < cronjob-oauth.yaml + +# Verify +ssh doorwaysftw "kubectl get cronjob gsc-monitoring-oauth -n manoonoils" +``` + +--- + +### Option B: Service Account (Requires Key Creation) + +**Note:** This only works if you can create service account keys in Google Cloud. + ## Setup Steps ### Step 1: Create Google Cloud Project diff --git a/scripts/gsc-monitoring/cronjob-oauth.yaml b/scripts/gsc-monitoring/cronjob-oauth.yaml new file mode 100644 index 0000000..1ed4d99 --- /dev/null +++ b/scripts/gsc-monitoring/cronjob-oauth.yaml @@ -0,0 +1,32 @@ +apiVersion: batch/v1 +kind: CronJob +metadata: + name: gsc-monitoring-oauth + namespace: manoonoils +spec: + schedule: "0 9 * * *" # Run daily at 9 AM UTC + jobTemplate: + spec: + template: + spec: + containers: + - name: gsc-monitor + image: gcr.io/manoonoils/gsc-monitoring:latest + env: + - name: GSC_OAUTH_FILE + value: /etc/gsc-monitoring/oauth-credentials.json + - name: PYTHONUNBUFFERED + value: "1" + volumeMounts: + - name: gsc-oauth-credentials + mountPath: /etc/gsc-monitoring + readOnly: true + - name: logs + mountPath: /var/log/gsc-monitoring + volumes: + - name: gsc-oauth-credentials + secret: + secretName: gsc-oauth-credentials + - name: logs + emptyDir: {} + restartPolicy: OnFailure diff --git a/scripts/gsc-monitoring/monitor.py b/scripts/gsc-monitoring/monitor.py index f9af1bc..0ad6b9e 100644 --- a/scripts/gsc-monitoring/monitor.py +++ b/scripts/gsc-monitoring/monitor.py @@ -2,6 +2,10 @@ """ Google Search Console Monitoring Script Monitors search performance, crawl errors, and indexing status + +Supports both: +1. Service Account (with JSON key file) +2. OAuth 2.0 (user authentication) """ import os @@ -9,6 +13,8 @@ import json import sys from datetime import datetime, timedelta from google.oauth2 import service_account +from google.oauth2.credentials import Credentials as OAuthCredentials +from google.auth.transport.requests import Request from googleapiclient.discovery import build from googleapiclient.errors import HttpError @@ -16,14 +22,54 @@ from googleapiclient.errors import HttpError SITE_URL = "https://manoonoils.com/" SCOPES = ["https://www.googleapis.com/auth/webmasters.readonly"] KEY_FILE = os.environ.get("GSC_KEY_FILE", "/etc/gsc-monitoring/service-account.json") +OAUTH_FILE = os.environ.get( + "GSC_OAUTH_FILE", "/etc/gsc-monitoring/oauth-credentials.json" +) def get_service(): """Authenticate and return Search Console service""" - credentials = service_account.Credentials.from_service_account_file( - KEY_FILE, scopes=SCOPES - ) - return build("webmasters", "v3", credentials=credentials) + + # Try OAuth first + if os.path.exists(OAUTH_FILE): + print("Using OAuth authentication...") + with open(OAUTH_FILE, "r") as f: + creds_info = json.load(f) + + creds = OAuthCredentials( + token=creds_info["token"], + refresh_token=creds_info["refresh_token"], + token_uri=creds_info["token_uri"], + client_id=creds_info["client_id"], + client_secret=creds_info["client_secret"], + scopes=creds_info["scopes"], + ) + + # Refresh if expired + if creds.expired: + creds.refresh(Request()) + # Save updated credentials + creds_info["token"] = creds.token + with open(OAUTH_FILE, "w") as f: + json.dump(creds_info, f, indent=2) + + return build("webmasters", "v3", credentials=creds) + + # Fall back to service account + elif os.path.exists(KEY_FILE): + print("Using Service Account authentication...") + credentials = service_account.Credentials.from_service_account_file( + KEY_FILE, scopes=SCOPES + ) + return build("webmasters", "v3", credentials=credentials) + + else: + raise FileNotFoundError( + f"No credentials found. Please set up either:\n" + f" 1. OAuth: {OAUTH_FILE}\n" + f" 2. Service Account: {KEY_FILE}\n" + f"\nSee README.md for setup instructions." + ) def get_search_analytics(service, days=7): @@ -157,12 +203,6 @@ def main(): """Main monitoring function""" print("šŸ” Starting Google Search Console monitoring...") - # Check for credentials - if not os.path.exists(KEY_FILE): - print(f"āŒ Error: Service account key file not found at {KEY_FILE}") - print("Please set up Google Cloud credentials first.") - sys.exit(1) - try: service = get_service() @@ -182,6 +222,9 @@ def main(): f.write(report) print(f"\nšŸ’¾ Report saved to: {report_file}") + except FileNotFoundError as e: + print(f"āŒ {e}") + sys.exit(1) except Exception as e: print(f"āŒ Error: {e}") sys.exit(1) diff --git a/scripts/gsc-monitoring/setup-oauth-local.py b/scripts/gsc-monitoring/setup-oauth-local.py new file mode 100644 index 0000000..3a3f5ff --- /dev/null +++ b/scripts/gsc-monitoring/setup-oauth-local.py @@ -0,0 +1,164 @@ +#!/usr/bin/env python3 +""" +OAuth Setup for Google Search Console Monitoring +Run this locally (not on the server) to generate OAuth credentials +""" + +import os +import json +import webbrowser +from pathlib import Path + + +def setup_oauth(): + """Interactive OAuth setup""" + + print("=" * 70) + print("GOOGLE SEARCH CONSOLE - OAUTH 2.0 SETUP") + print("=" * 70) + print() + print("This method uses OAuth 2.0 (no service account key needed)") + print("You'll authenticate once with your Google account.") + print() + + # Step 1: Enable API + print("STEP 1: Enable Search Console API") + print("-" * 70) + print("1. Go to: https://console.cloud.google.com") + print("2. Create/select project: manoonoils-monitoring") + print("3. Go to: APIs & Services → Library") + print("4. Search: 'Google Search Console API'") + print("5. Click: Enable") + print() + input("Press Enter when you've enabled the API...") + + # Step 2: Create OAuth credentials + print() + print("STEP 2: Create OAuth Credentials") + print("-" * 70) + print("1. Go to: APIs & Services → Credentials") + print("2. Click: Create Credentials → OAuth client ID") + print("3. Click: Configure Consent Screen") + print("4. User Type: External") + print("5. App name: ManoonOils GSC Monitor") + print("6. User support email: your-email@manoonoils.com") + print("7. Developer contact: your-email@manoonoils.com") + print("8. Click: Save and Continue (3 times)") + print("9. Click: Back to Dashboard") + print() + print("10. Back on Credentials page:") + print("11. Click: Create Credentials → OAuth client ID") + print("12. Application type: Desktop app") + print("13. Name: GSC Desktop Client") + print("14. Click: Create") + print("15. Click: DOWNLOAD JSON") + print() + + # Get the file path + json_path = input("Enter the path to the downloaded JSON file: ").strip() + + if not os.path.exists(json_path): + print(f"āŒ File not found: {json_path}") + return + + # Load credentials + with open(json_path, "r") as f: + client_config = json.load(f) + + # Step 3: Install dependencies and run auth + print() + print("STEP 3: Install Dependencies") + print("-" * 70) + print("Run these commands:") + print() + print( + " pip3 install google-auth google-auth-oauthlib google-auth-httplib2 google-api-python-client" + ) + print() + input("Press Enter after installing...") + + # Step 4: Authorization + print() + print("STEP 4: Authorize Application") + print("-" * 70) + print("Running authorization...") + + # Import here so we can check if installed + try: + from google_auth_oauthlib.flow import InstalledAppFlow + from google.auth.transport.requests import Request + import pickle + except ImportError: + print("āŒ Please install the required packages first (Step 3)") + return + + SCOPES = ["https://www.googleapis.com/auth/webmasters.readonly"] + + # Create flow + flow = InstalledAppFlow.from_client_secrets_file( + json_path, + SCOPES, + redirect_uri="urn:ietf:wg:oauth:2.0:oob", # For console-based auth + ) + + # Get authorization URL + auth_url, _ = flow.authorization_url(prompt="consent") + + print() + print("šŸ“± Open this URL in your browser:") + print(auth_url) + print() + + # Try to open browser automatically + try: + webbrowser.open(auth_url) + print("(Browser should open automatically)") + except: + pass + + # Get the code + print() + code = input("Enter the authorization code from the browser: ").strip() + + # Exchange code for credentials + flow.fetch_token(code=code) + creds = flow.credentials + + # Save credentials + creds_info = { + "token": creds.token, + "refresh_token": creds.refresh_token, + "token_uri": creds.token_uri, + "client_id": creds.client_id, + "client_secret": creds.client_secret, + "scopes": creds.scopes, + } + + output_file = "gsc-oauth-credentials.json" + with open(output_file, "w") as f: + json.dump(creds_info, f, indent=2) + + print() + print("=" * 70) + print("āœ… SUCCESS! OAuth credentials saved to:", output_file) + print("=" * 70) + print() + print("NEXT STEPS:") + print("1. Copy this file to your server:") + print(f" scp {output_file} doorwaysftw:/tmp/") + print() + print("2. Create Kubernetes secret:") + print(" ssh doorwaysftw") + print(" kubectl create secret generic gsc-oauth-credentials \\") + print(" --namespace=manoonoils \\") + print(" --from-file=oauth-credentials.json=/tmp/gsc-oauth-credentials.json") + print() + print("3. Deploy monitoring:") + print(" kubectl apply -f scripts/gsc-monitoring/cronjob-oauth.yaml") + print() + print("Your refresh token is valid indefinitely (until revoked).") + print("The monitoring will run automatically every day!") + + +if __name__ == "__main__": + setup_oauth() diff --git a/scripts/gsc-monitoring/setup-oauth.py b/scripts/gsc-monitoring/setup-oauth.py new file mode 100644 index 0000000..2686711 --- /dev/null +++ b/scripts/gsc-monitoring/setup-oauth.py @@ -0,0 +1,133 @@ +#!/usr/bin/env python3 +""" +Google Search Console OAuth Setup Script +Generates OAuth credentials and stores refresh token +""" + +import os +import json +import sys +from pathlib import Path + + +def create_oauth_credentials(): + """Guide user through OAuth setup""" + + print("=" * 70) + print("GOOGLE SEARCH CONSOLE - OAUTH SETUP (No Service Account Key Needed)") + print("=" * 70) + print() + print("This method uses OAuth 2.0 instead of service account keys.") + print("You'll authenticate once with your Google account.") + print() + + # Step 1: Create credentials + print("STEP 1: Create OAuth Credentials") + print("-" * 70) + print("1. Go to: https://console.cloud.google.com") + print("2. Select/create project: manoonoils-monitoring") + print("3. Go to: APIs & Services → Credentials") + print("4. Click: Create Credentials → OAuth client ID") + print("5. Application type: Desktop app") + print("6. Name: GSC Monitor") + print("7. Click Create") + print("8. Download the JSON file (client_secret_*.json)") + print() + input("Press Enter when you have downloaded the credentials file...") + + # Step 2: Get credentials file path + print() + print("STEP 2: Upload Credentials") + print("-" * 70) + print("Copy the downloaded file to this server:") + print() + print(" scp /path/to/client_secret_*.json doorwaysftw:/tmp/gsc-credentials.json") + print() + input("Press Enter after uploading...") + + # Step 3: Run authorization + print() + print("STEP 3: Authorize Application") + print("-" * 70) + print("Running authorization flow...") + print() + + # Create auth script + auth_script = """#!/usr/bin/env python3 +import os +import json +import pickle +from google_auth_oauthlib.flow import InstalledAppFlow +from google.auth.transport.requests import Request + +SCOPES = ['https://www.googleapis.com/auth/webmasters.readonly'] +CREDS_FILE = '/tmp/gsc-credentials.json' +TOKEN_FILE = '/tmp/gsc-token.pickle' + +def main(): + creds = None + + if os.path.exists(TOKEN_FILE): + with open(TOKEN_FILE, 'rb') as token: + creds = pickle.load(token) + + if not creds or not creds.valid: + if creds and creds.expired and creds.refresh_token: + creds.refresh(Request()) + else: + flow = InstalledAppFlow.from_client_secrets_file( + CREDS_FILE, SCOPES) + creds = flow.run_local_server(port=0) + + with open(TOKEN_FILE, 'wb') as token: + pickle.dump(creds, token) + + print("\\nāœ… Authorization successful!") + print(f"Token saved to: {TOKEN_FILE}") + + # Save credentials info + creds_info = { + 'token': creds.token, + 'refresh_token': creds.refresh_token, + 'token_uri': creds.token_uri, + 'client_id': creds.client_id, + 'client_secret': creds.client_secret, + 'scopes': creds.scopes + } + + with open('/tmp/gsc-token.json', 'w') as f: + json.dump(creds_info, f, indent=2) + + print(f"Credentials saved to: /tmp/gsc-token.json") + print("\\nYou can now deploy the monitoring system!") + +if __name__ == '__main__': + main() +""" + + # Save and run auth script + with open("/tmp/gsc-auth.py", "w") as f: + f.write(auth_script) + + print("Authorization script created at: /tmp/gsc-auth.py") + print() + print("Run this on the server to authorize:") + print() + print(" ssh doorwaysftw") + print(" cd /tmp") + print(" python3 gsc-auth.py") + print() + print("This will open a browser for you to authorize the app.") + print("If running on a remote server without browser, use SSH tunnel:") + print() + print(" ssh -L 8080:localhost:8080 doorwaysftw") + print(" Then run python3 gsc-auth.py") + print() + + +def main(): + create_oauth_credentials() + + +if __name__ == "__main__": + main()