Install
openclaw skills install google-services-secureSecure Google Workspace integration (Gmail, Drive, Calendar, Sheets, Docs, Contacts, etc.) with enterprise-grade security. Features credential isolation, input validation, audit logging, rate limiting, and granular permissions. Use when working with Google APIs, sending emails, managing files, scheduling calendar events, or automating Google Workspace workflows.
openclaw skills install google-services-secureThis skill implements enterprise-grade security protections for Google Workspace APIs:
CRITICAL SECURITY REQUIREMENTS:
# NEVER store these in openclaw.json or any config file
export GOOGLE_API_KEY="your-api-key"
export GOOGLE_CLIENT_ID="your-client-id"
export GOOGLE_CLIENT_SECRET="your-client-secret"
export GOOGLE_REDIRECT_URI="http://localhost:8080/callback"
cd /data/.openclaw/workspace/skills/google-services-secure
./scripts/validate-setup.sh
# Generate OAuth URL and authenticate
./scripts/auth-google.sh
# Add to ~/.bashrc or /etc/environment
export GOOGLE_API_KEY="your-api-key"
export GOOGLE_CLIENT_ID="your-client-id.apps.googleusercontent.com"
export GOOGLE_CLIENT_SECRET="your-client-secret"
export GOOGLE_REDIRECT_URI="http://localhost:8080/callback"
# Reload shell
source ~/.bashrc
cd /data/.openclaw/workspace/skills/google-services-secure
./scripts/validate-setup.sh
This will:
cd /data/.openclaw/workspace/skills/google-services-secure
./scripts/auth-google.sh
This will:
curl -s "https://gmail.googleapis.com/gmail/v1/users/me/messages?maxResults=10" \
-H "Authorization: Bearer $ACCESS_TOKEN"
-H "Content-Type: application/json"
❌ NEVER do this:
{
"env": {
"GOOGLE_API_KEY": "AIza...", // ❌ INSECURE
"GOOGLE_CLIENT_ID": "...apps.googleusercontent.com", // ❌ INSECURE
"GOOGLE_CLIENT_SECRET": "..." // ❌ CRITICAL SECURITY ISSUE
}
}
✅ CORRECT approach:
# Set at system level, never in files
export GOOGLE_API_KEY="your-api-key"
export GOOGLE_CLIENT_ID="your-client-id.apps.googleusercontent.com"
export GOOGLE_CLIENT_SECRET="your-client-secret"
Step 1: Generate Authorization URL
https://accounts.google.com/o/oauth2/v2/auth?client_id=$GOOGLE_CLIENT_ID \
&redirect_uri=$GOOGLE_REDIRECT_URI \
&response_type=code \
&scope=https://www.googleapis.com/auth/gmail.readonly,https://www.googleapis.com/auth/drive.readonly
Step 2: Exchange Code for Token
curl -X POST https://oauth2.googleapis.com/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "client_id=$GOOGLE_CLIENT_ID" \
-d "client_secret=$GOOGLE_CLIENT_SECRET" \
-d "code=$AUTHORIZATION_CODE" \
-d "redirect_uri=$GOOGLE_REDIRECT_URI" \
-d "grant_type=authorization_code"
The skill operates in three permission modes:
| Mode | Read | Write | Delete | Share | Risk Level |
|---|---|---|---|---|---|
readonly | ✅ | ❌ | ❌ | ❌ | 🟢 LOW |
restricted | ✅ | ✅* | ❌ | ❌ | 🟡 MEDIUM |
full | ✅ | ✅ | ✅* | ✅* | 🔴 HIGH |
Default mode: readonly
To change mode:
export GOOGLE_PERMISSION_MODE="readonly" # Recommended for production
All actions are logged to:
/data/.openclaw/logs/google-services-audit.log
Log format:
{
"timestamp": "2024-04-04T00:30:45.123Z",
"user": "nelson",
"service": "gmail",
"action": "LIST_MESSAGES",
"status": "success",
"messageCount": 10,
"durationMs": 234,
"permissionMode": "readonly"
}
Default limits (configurable):
| Service | Limit | Window |
|---|---|---|
| Gmail API | 100 | per minute |
| Drive API | 500 | per minute |
| Calendar API | 100 | per minute |
| Sheets API | 100 | per minute |
| Contacts API | 50 | per minute |
Customize limits:
export GOOGLE_RATE_LIMIT_GMAIL="100/minute"
export GOOGLE_RATE_LIMIT_DRIVE="500/minute"
curl -s "https://gmail.googleapis.com/gmail/v1/users/me/messages?maxResults=10" \
-H "Authorization: Bearer $ACCESS_TOKEN"
curl -s "https://gmail.googleapis.com/gmail/v1/users/me/messages/$MESSAGE_ID" \
-H "Authorization: Bearer $ACCESS_TOKEN"
⚠️ Confirmation required before sending
curl -X POST "https://gmail.googleapis.com/gmail/v1/users/me/messages/send" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"raw": "From: me@example.com\r\nTo: recipient@example.com\r\nSubject: Test\r\n\r\nTest message body"
}'
curl -s "https://www.googleapis.com/drive/v3/files?pageSize=10" \
-H "Authorization: Bearer $ACCESS_TOKEN"
⚠️ Confirmation required before uploading
curl -X POST "https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-F "metadata={\"name\":\"test.txt\"};type=application/json; charset=UTF-8" \
-F "file=@test.txt"
curl -s "https://www.googleapis.com/drive/v3/files/$FILE_ID?alt=media" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-O downloaded_file.txt
curl -s "https://www.googleapis.com/calendar/v3/calendars/primary/events?maxResults=10" \
-H "Authorization: Bearer $ACCESS_TOKEN"
⚠️ Confirmation required before creating
curl -X POST "https://www.googleapis.com/calendar/v3/calendars/primary/events" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"summary": "Meeting",
"start": {"dateTime": "2024-04-04T10:00:00Z"},
"end": {"dateTime": "2024-04-04T11:00:00Z"}
}'
curl -s "https://sheets.googleapis.com/v4/spreadsheets" \
-H "Authorization: Bearer $ACCESS_TOKEN"
curl -s "https://sheets.googleapis.com/v4/spreadsheets/$SPREADSHEET_ID/values/Sheet1!A1:B10" \
-H "Authorization: Bearer $ACCESS_TOKEN"
⚠️ Confirmation required before updating
curl -X PUT "https://sheets.googleapis.com/v4/spreadsheets/$SPREADSHEET_ID/values/Sheet1!A1:B10" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"values": [["New Value"]]
}'
curl -s "https://www.googleapis.com/drive/v3/files?q=mimeType%3D'application/vnd.google-apps.document'" \
-H "Authorization: Bearer $ACCESS_TOKEN"
curl -s "https://www.googleapis.com/drive/v3/files/$DOC_ID/export?mimeType=text/plain" \
-H "Authorization: Bearer $ACCESS_TOKEN"
curl -s "https://people.googleapis.com/v1/people/me/connections?personFields=names,emailAddresses" \
-H "Authorization: Bearer $ACCESS_TOKEN"
⚠️ Confirmation required before creating
curl -X POST "https://people.googleapis.com/v1/people:createContact" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"emailAddresses": [{"value": "new@example.com"}],
"names": [{"givenName": "John", "familyName": "Doe"}]
}'
function validateEmail(email) {
// Must be valid email format
if (!email.match(/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/)) {
throw new Error('Invalid email format');
}
// No dangerous characters
if (email.match(/<script|javascript:|onerror|onload/i)) {
throw new Error('Email contains dangerous patterns');
}
return email;
}
function validateFilePath(path) {
// Prevent directory traversal
if (path.includes('..') || path.includes('~')) {
throw new Error('Invalid file path');
}
// Validate file extension
const allowedExtensions = ['.txt', '.pdf', '.doc', '.docx', '.xls', '.xlsx'];
const extension = path.slice(-5);
if (!allowedExtensions.includes(extension)) {
throw new Error('File type not allowed');
}
return path;
}
function sanitizeData(data) {
const sensitive = ['password', 'apiKey', 'secret', 'token', 'credential'];
const sanitized = JSON.parse(JSON.stringify(data));
function clean(obj) {
for (const key in obj) {
if (sensitive.some(s => key.toLowerCase().includes(s))) {
obj[key] = '***REDACTED***';
} else if (typeof obj[key] === 'object') {
clean(obj[key]);
} else if (typeof obj[key] === 'string') {
// Remove potential HTML/Script tags
obj[key] = obj[key].replace(/<[^>]*>/g, '');
}
}
}
clean(sanitized);
return sanitized;
}
#!/bin/bash
# Send automated email notifications
EMAIL_SUBJECT="Daily Report"
RECIPIENT="manager@example.com"
MESSAGE_BODY="Daily report attached."
curl -X POST "https://gmail.googleapis.com/gmail/v1/users/me/messages/send" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"raw\": \"From: me@example.com\\r\\nTo: $RECIPIENT\\r\\nSubject: $EMAIL_SUBJECT\\r\\n\\r\\n$MESSAGE_BODY\"
}"
#!/bin/bash
# Backup files from Drive to local storage
curl -s "https://www.googleapis.com/drive/v3/files?pageSize=100" \
-H "Authorization: Bearer $ACCESS_TOKEN" | \
jq -r '.files[] | "\(.id) \t \(.name)"' | \
while read id name; do
curl -s "https://www.googleapis.com/drive/v3/files/$id?alt=media" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-O "backups/$name"
done
#!/bin/bash
# Schedule automated calendar events
EVENT_SUMMARY="Weekly Meeting"
START_TIME=$(date -u +"%Y-%m-%dT%H:%M:00Z")
END_TIME=$(date -u -d +1hour +"%Y-%m-%dT%H:%M:00Z")
curl -X POST "https://www.googleapis.com/calendar/v3/calendars/primary/events" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"summary\": \"$EVENT_SUMMARY\",
\"start\": {\"dateTime\": \"$START_TIME\"},
\"end\": {\"dateTime\": \"$END_TIME\"}
}"
# Set environment variables at system level
export GOOGLE_API_KEY="your-api-key"
export GOOGLE_CLIENT_ID="your-client-id.apps.googleusercontent.com"
export GOOGLE_CLIENT_SECRET="your-client-secret"
# Use readonly mode in production
export GOOGLE_PERMISSION_MODE="readonly"
# Review audit logs regularly
tail -100 /data/.openclaw/logs/google-services-audit.log
# Validate setup before use
./scripts/validate-setup.sh
# NEVER store credentials in config files
{
"env": {
"GOOGLE_CLIENT_SECRET": "..." # ❌ CRITICAL SECURITY ISSUE
}
}
# NEVER use HTTP (unencrypted)
export GOOGLE_API_KEY="http://..." # ❌ INSECURE
# NEVER share access tokens
echo "$ACCESS_TOKEN" # ❌ LEAKS CREDENTIALS
Error: Invalid Credentials
Solution:
Error: Invalid grant
Solution:
Error: User Rate Limit Exceeded
Solution:
Error: 403 Forbidden
Solution:
Before using this skill in production, verify:
{
"agents": {
"google-services": {
"id": "google-services",
"name": "Google Services (Secure)",
"skills": ["google-services-secure"],
"sandbox": "require",
"tools": {
"allowlist": ["curl", "read", "write"],
"denylist": ["exec", "eval", "shell"]
},
"maxConcurrent": 1
}
}
}
# Add skill to main agent
openclaw agent add-skill main google-services-secure
# Or create dedicated agent
openclaw agent create google-services \
--skills google-services-secure \
--sandbox require \
--max-concurrent 1
references/security.md - Complete security guidescripts/validate-setup.sh - Setup verificationscripts/auth-google.sh - OAuth 2.0 flowMIT License - See LICENSE.md for details
Security is top priority. All contributions must:
references/ directory⚠️ IMPORTANT: This skill prioritizes security over convenience. Read-only operations work immediately. Dangerous operations require explicit confirmation and appropriate permission levels.
Note: This skill uses OAuth 2.0 flow for secure authorization. Never share your access tokens or store them in files.