Files
Guillaume BOURGEOIS 84be770928 feat(gmail-single-user): new single-mailbox Gmail integration
Gmail for one mailbox over OAuth 2.0 (no service account / delegation):
auth-link + exchange-code to obtain a refresh token, connectivity test,
message search/get, send/reply with attachments, attachment retrieval,
and get_incidents ingestion with an OCSF mapper. Refresh-token grant,
stdlib-only (no extra Python dependencies).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 23:29:12 +02:00

169 lines
8.2 KiB
YAML

id: gmail_single_user
name: Gmail Single User
version: 1.0.0
description: "Gmail for a single mailbox over OAuth 2.0 (no service account, no domain-wide delegation) — search/read messages, send and reply (with attachments), retrieve attachments, and ingest messages (get_incidents) with an OCSF mapper. Uses a per-mailbox OAuth refresh token; stdlib-only, no extra Python dependencies."
changelog: "1.0.0 — Initial release: OAuth authorization link + code exchange, connectivity test, message ingestion with OCSF mapper, send/reply mail with attachments, and attachment retrieval."
category: email
# Per-instance configuration. This integration authenticates as ONE Gmail user
# via OAuth 2.0 (not a service account). One-time setup:
# 1. Create an OAuth client (type: Web application) in Google Cloud, authorize
# the Gmail scopes, and note the Client ID / Client Secret. Add the redirect
# URI below to the client's authorized redirect URIs.
# 2. Run gmail-single-user-auth-link, open the URL, consent, and copy the
# "code" value from the redirect.
# 3. Run gmail-single-user-exchange-code with that code to get a refresh_token.
# 4. Paste the refresh_token into the config below and save.
# Every command then exchanges the refresh token for a short-lived access token.
config_schema:
properties:
email:
type: string
description: "The Gmail address of the mailbox (used as the default From address)"
client_id:
type: string
description: "OAuth client ID"
client_secret:
type: string
description: "OAuth client secret"
x-soar-sensitive: true
refresh_token:
type: string
description: "OAuth refresh token (obtain it once with gmail-single-user-exchange-code, then paste it here)"
x-soar-sensitive: true
redirect_uri:
type: string
description: "OAuth redirect URI registered on the client (must match at auth-link and exchange-code time)"
default: http://localhost
send_as:
type: string
description: "Optional address to send from (a verified send-as alias of the mailbox)"
required:
- email
- client_id
- client_secret
commands:
- id: auth_link
name: gmail-single-user-auth-link
description: "Generate the OAuth consent URL. Open it, approve access, and copy the returned code for gmail-single-user-exchange-code."
risk: read
inputs_schema:
properties: {}
required: []
outputs_schema: { properties: {} }
- id: exchange_code
name: gmail-single-user-exchange-code
description: "Exchange an OAuth authorization code for a refresh token (paste the refresh_token into the instance config afterwards)."
inputs_schema:
properties:
code: { type: string, description: "Authorization code from the consent redirect" }
redirect_uri: { type: string, description: "Override the configured redirect URI (must match the one used for auth-link)" }
required: [code]
outputs_schema: { properties: {} }
- id: get_incidents
name: gmail-single-user-get-incidents
description: "Fetch messages from the mailbox for ingestion. Returns {result:[...]}; use result as the alert rule results path."
risk: read
inputs_schema:
properties:
query: { type: string, description: "Extra Gmail search query (same syntax as the Gmail search box)" }
after: { type: string, description: "Lower bound on message date, epoch (s/ms) or ISO8601 (incremental fetch watermark)" }
subject: { type: string, description: "Filter by subject" }
from: { type: string, description: "Filter by sender" }
to: { type: string, description: "Filter by recipient" }
filename: { type: string, description: "Filter by attachment filename or type" }
label_id: { type: string, description: "Restrict to a label ID" }
has_attachments: { type: boolean, description: "Only messages with attachments" }
max: { type: number, description: "Maximum messages to fetch (default 50)" }
required: []
outputs_schema: { properties: {} }
ingest:
results_path: result
dedup_key: id
incremental_field: after
- id: search
name: gmail-single-user-search
description: "Search the mailbox and return flattened messages (subject/from/to/body/attachments)."
risk: read
inputs_schema:
properties:
query: { type: string, description: "Gmail search query" }
subject: { type: string, description: "Filter by subject" }
from: { type: string, description: "Filter by sender" }
to: { type: string, description: "Filter by recipient" }
filename: { type: string, description: "Filter by attachment filename or type" }
label_id: { type: string, description: "Restrict to a label ID" }
has_attachments: { type: boolean, description: "Only messages with attachments" }
max_results: { type: number, description: "Maximum messages (default 100, max 500)" }
page_token: { type: string, description: "Page token for the next results page" }
include_spam_trash: { type: boolean, description: "Include SPAM and TRASH" }
required: []
outputs_schema: { properties: {} }
- id: get_mail
name: gmail-single-user-get-mail
description: "Retrieve a single message (flattened) by ID."
risk: read
inputs_schema:
properties:
message_id: { type: string, description: "Message ID" }
required: [message_id]
outputs_schema: { properties: {} }
- id: send_mail
name: gmail-single-user-send-mail
description: "Send an email from the mailbox (optional single attachment as base64)."
inputs_schema:
properties:
to: { type: string, description: "Recipient(s), comma-separated" }
subject: { type: string, description: "Subject" }
body: { type: string, description: "Plain-text body" }
html_body: { type: string, description: "HTML body" }
cc: { type: string, description: "CC recipient(s), comma-separated" }
bcc: { type: string, description: "BCC recipient(s), comma-separated" }
reply_to: { type: string, description: "Reply-To address" }
attachment_name: { type: string, description: "Attachment file name (with attachment_base64)" }
attachment_base64: { type: string, description: "Attachment content, base64-encoded" }
required: [to, subject]
outputs_schema: { properties: {} }
- id: reply_mail
name: gmail-single-user-reply-mail
description: "Reply to a message (sets In-Reply-To/References so it threads correctly)."
inputs_schema:
properties:
to: { type: string, description: "Recipient(s), comma-separated" }
subject: { type: string, description: "Subject (keep the original for correct threading)" }
in_reply_to: { type: string, description: "Message-ID header of the message being replied to" }
references: { type: string, description: "References header (space-separated Message-IDs)" }
body: { type: string, description: "Plain-text body" }
html_body: { type: string, description: "HTML body" }
cc: { type: string, description: "CC recipient(s), comma-separated" }
bcc: { type: string, description: "BCC recipient(s), comma-separated" }
reply_to: { type: string, description: "Reply-To address" }
attachment_name: { type: string, description: "Attachment file name (with attachment_base64)" }
attachment_base64: { type: string, description: "Attachment content, base64-encoded" }
required: [to, subject, in_reply_to]
outputs_schema: { properties: {} }
- id: get_attachments
name: gmail-single-user-get-attachments
description: "Retrieve a message's attachments as base64."
risk: read
inputs_schema:
properties:
message_id: { type: string, description: "Message ID" }
required: [message_id]
outputs_schema: { properties: {} }
- id: test_connection
name: gmail-single-user-test-connection
description: "Verify the OAuth refresh token by reading the mailbox profile (used by the Test button)."
risk: read
inputs_schema:
properties: {}
required: []
outputs_schema: { properties: {} }
ingestion:
command: get_incidents
mapper: get_incidents
default_incident_type: "Gmail Single User Message"