OAuth Configuration Guide
This comprehensive guide covers OAuth authentication for connecting Gmail and Outlook accounts to EDITH for email sending and receiving.
Overview
OAuth integration allows your users to connect their personal email accounts (Gmail, Outlook) to send and receive emails through EDITH. This is essential for:
- Personal email integration - Users can send from their own email addresses
- IMAP monitoring - Automatically fetch incoming emails
- Reply tracking - Monitor and process email replies
- Calendar access - Some providers support calendar integration
Supported Providers:
- 🔵 Google (Gmail, Google Workspace)
- 🟦 Microsoft (Outlook, Office 365, Hotmail)
Prerequisites
Before implementing OAuth, you need:
- Cloud App Registration - Register your app with Google/Microsoft
- OAuth Credentials - Obtain Client ID and Client Secret
- Redirect URIs - Configure callback URLs
- Scopes/Permissions - Request necessary email permissions
Part 1: Cloud App Registration
Register with Google (Gmail)
Step 1: Create Google Cloud Project
- Go to Google Cloud Console
- Create a new project or select existing
- Project name:
YourApp Email Integration
Step 2: Enable Gmail API
- Navigate to APIs & Services → Library
- Search for Gmail API
- Click Enable
Step 3: Configure OAuth Consent Screen
- Go to APIs & Services → OAuth consent screen
- Choose External (for public use) or Internal (for workspace only)
- Fill in required information:
- App name: Your application name
- User support email: Your support email
- Developer contact: Your email
- Add scopes:
https://www.googleapis.com/auth/gmail.send- Send emailshttps://www.googleapis.com/auth/gmail.readonly- Read emailshttps://www.googleapis.com/auth/gmail.modify- Modify emails (labels)
- Save and continue
Step 4: Create OAuth Credentials
- Go to APIs & Services → Credentials
- Click Create Credentials → OAuth client ID
- Application type: Web application
- Add Authorized redirect URIs:
https://api.sparrowmailer.com/oauth/token - Click Create
- Save the Client ID and Client Secret - you'll need these!
Example Credentials:
{
"client_id": "123456789-abcdefg.apps.googleusercontent.com",
"client_secret": "GOCSPX-abc123xyz789",
"redirect_uris": ["https://api.sparrowmailer.com/oauth/token"]
}
Register with Microsoft (Outlook)
Step 1: Go to Azure Portal
- Visit Azure Portal
- Navigate to Azure Active Directory
- Select App registrations
Step 2: Register Application
- Click New registration
- Fill in details:
- Name: Your application name
- Supported account types: Choose appropriate option
- Personal Microsoft accounts only - For Outlook/Hotmail
- Accounts in any organizational directory - For Office 365
- All Microsoft accounts - For both
- Redirect URI:
- Platform: Web
- URI:
https://api.sparrowmailer.com/oauth/token
- Click Register
Step 3: Configure API Permissions
- Go to API permissions
- Click Add a permission
- Select Microsoft Graph
- Choose Delegated permissions
- Add required permissions:
Mail.Send- Send emailsMail.Read- Read emailsMail.ReadWrite- Modify emailsIMAP.AccessAsUser.All- IMAP accessSMTP.Send- SMTP sending
- Click Grant admin consent (if you have admin rights)
Step 4: Create Client Secret
- Go to Certificates & secrets
- Click New client secret
- Description:
EDITH OAuth Secret - Expiration: Choose duration (24 months recommended)
- Click Add
- Copy the secret value immediately - it won't be shown again!
Example Credentials:
{
"client_id": "12345678-1234-1234-1234-123456789abc",
"client_secret": "abc~123XYZ-secretValue",
"tenant_id": "common",
"redirect_uri": "https://api.sparrowmailer.com/oauth/token"
}
Part 2: Register Cloud App in EDITH
After obtaining OAuth credentials from Google/Microsoft, register them in EDITH.
Endpoint
POST /v1/cloudapp/create
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
provider | string | ✅ Yes | OAuth provider: "google" or "microsoft" |
client_id | string | ✅ Yes | OAuth Client ID from provider |
client_secret | string | ✅ Yes | OAuth Client Secret from provider |
redirect_uri | string | ✅ Yes | Callback URL: https://api.sparrowmailer.com/oauth/token |
scopes | string[] | ✅ Yes | Array of OAuth scopes/permissions (see below) |
app_name | string | No | Friendly name for this app configuration |
Required Scopes by Connection Type
Google (Gmail)
For SMTP (Sending only):
"scopes": [
"https://www.googleapis.com/auth/gmail.send",
"https://www.googleapis.com/auth/userinfo.email",
"https://www.googleapis.com/auth/userinfo.profile"
]
For IMAP (Receiving only):
"scopes": [
"https://www.googleapis.com/auth/gmail.readonly",
"https://www.googleapis.com/auth/userinfo.email",
"https://www.googleapis.com/auth/userinfo.profile"
]
For SMTP_IMAP (Both):
"scopes": [
"https://www.googleapis.com/auth/gmail.send",
"https://www.googleapis.com/auth/gmail.readonly",
"https://www.googleapis.com/auth/userinfo.email",
"https://www.googleapis.com/auth/userinfo.profile"
]
Microsoft (Outlook)
For SMTP (Sending only):
"scopes": [
"https://graph.microsoft.com/Mail.Send",
"email",
"offline_access",
"openid",
"profile"
]
For IMAP (Receiving only):
"scopes": [
"https://graph.microsoft.com/Mail.Read",
"https://graph.microsoft.com/Mail.ReadBasic",
"https://graph.microsoft.com/Mail.ReadWrite",
"https://graph.microsoft.com/User.Read",
"email",
"openid",
"offline_access",
"profile"
]
For SMTP_IMAP (Both):
"scopes": [
"https://graph.microsoft.com/Mail.Read",
"https://graph.microsoft.com/Mail.ReadBasic",
"https://graph.microsoft.com/Mail.ReadWrite",
"https://graph.microsoft.com/Mail.Send",
"https://graph.microsoft.com/User.Read",
"email",
"openid",
"offline_access",
"profile"
]
Example: Register Gmail App
curl -X POST https://api.edith.example.com/v1/cloudapp/create \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"provider": "google",
"app_name": "My App Gmail Integration",
"client_id": "123456789-abcdefg.apps.googleusercontent.com",
"client_secret": "GOCSPX-abc123xyz789",
"redirect_uri": "https://api.edith.example.com/oauth/google/callback",
"scopes": [
"https://www.googleapis.com/auth/gmail.send",
"https://www.googleapis.com/auth/gmail.readonly",
"https://www.googleapis.com/auth/gmail.modify"
]
}'
Response
{
"success": true,
"app_id": "cloudapp_abc123xyz",
"message": "Cloud app registered successfully"
}
Save the app_id - you'll use it for OAuth login!
Example: Register Outlook App
curl -X POST https://api.edith.example.com/v1/cloudapp/create \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"provider": "microsoft",
"app_name": "My App Outlook Integration",
"client_id": "12345678-1234-1234-1234-123456789abc",
"client_secret": "abc~123XYZ-secretValue",
"redirect_uri": "https://api.edith.example.com/oauth/microsoft/callback",
"scopes": [
"https://outlook.office.com/Mail.Send",
"https://outlook.office.com/Mail.Read",
"https://outlook.office.com/IMAP.AccessAsUser.All",
"https://outlook.office.com/SMTP.Send"
]
}'
Part 3: OAuth Flows
EDITH supports two OAuth authentication flows:
Flow 1: New OAuth Flow (User Login)
Use this when users need to grant permission for the first time.
Process:
- Your app calls
/v1/oauth/loginwithexchange_token: false - EDITH returns OAuth authorization URL
- Redirect user to this URL
- User logs in and grants permissions
- Provider redirects back to EDITH with authorization code
- EDITH exchanges code for tokens
- EDITH creates email configuration
- User redirected to your app with success parameters
- Your app calls
/v1/oauth/activeto activate the connection
Request Example
curl -X POST https://api.edith.example.com/v1/oauth/login \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"app_id": "cloudapp_abc123xyz",
"type": "smtp_imap",
"exchange_token": false,
"webhook": {
"url": "https://yourapp.com/webhooks/incoming-email",
"method": "POST",
"headers": {
"X-Webhook-Secret": "your-secret"
}
},
"state": "user_123_session_xyz"
}'
Response
{
"success": true,
"url": "https://accounts.google.com/o/oauth2/v2/auth?client_id=...&redirect_uri=...&scope=...&state=..."
}
Redirect user to this URL!
User Authorization
User sees Google/Microsoft login page → Grants permissions → Redirected back
Callback Parameters
After successful OAuth, user is redirected to your app:
https://yourapp.com/oauth/callback?
email=user@gmail.com&
mailer_id=mailer_abc123&
verified=true&
message=success&
state=user_123_session_xyz
| Parameter | Description |
|---|---|
email | User's authenticated email address |
mailer_id | EDITH mailer configuration ID |
verified | true if successful, false if failed |
message | Success message or error details |
state | Your custom state (for tracking user/session) |
error | Present only on failure |
Activate Connection
curl -X PUT https://api.edith.example.com/v1/oauth/active \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"mailer_id": "mailer_abc123",
"active": true
}'
Flow 2: Token Exchange Flow (Existing Tokens)
Use this when you already have OAuth tokens and want to create/update EDITH configuration.
When to use:
- Migrating from another email service
- You've already obtained tokens through your own OAuth flow
- Refreshing expired configurations
Process:
- Your app calls
/v1/oauth/loginwithexchange_token: true - Provide existing
access_tokenandrefresh_token - EDITH validates tokens with provider
- EDITH creates/updates email configuration
- Returns success response (no redirect)
Request Example
curl -X POST https://api.edith.example.com/v1/oauth/login \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"app_id": "cloudapp_abc123xyz",
"mailer_id": "existing_mailer_id",
"type": "smtp_imap",
"exchange_token": true,
"token": {
"access_token": "ya29.a0AfB_byDxxx...",
"refresh_token": "1//0gL9xyz...",
"expires_in": 3600,
"token_type": "Bearer",
"scope": "https://www.googleapis.com/auth/gmail.send https://www.googleapis.com/auth/gmail.readonly",
"id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjU5N..."
},
"webhook": {
"url": "https://yourapp.com/webhooks/incoming",
"method": "POST",
"headers": {}
}
}'
Token Fields Explained
| Field | Required | Description |
|---|---|---|
access_token | ✅ Yes | OAuth access token (short-lived, ~1 hour) |
refresh_token | ✅ Yes | OAuth refresh token (used to get new access tokens) |
expires_in | ✅ Yes | Access token expiration in seconds (typically 3600) |
token_type | ✅ Yes | Usually "Bearer" |
scope | ✅ Yes | Space-separated list of granted permissions |
id_token | No | JWT identity token (optional, contains user info) |
Response
{
"success": true,
"mailer_id": "mailer_abc123",
"message": "Email configuration updated successfully"
}
No redirect - configuration is immediately active!
Part 4: Connection Types
Choose the appropriate connection type based on your needs:
| Type | Description | Use Case |
|---|---|---|
smtp | Sending only | One-way email sending (notifications, alerts) |
imap | Receiving only | Monitor inbox for replies (no sending) |
smtp_imap | Both sending and receiving | Full bidirectional email communication |
SMTP Only (smtp)
{
"type": "smtp",
"app_id": "cloudapp_abc123xyz"
}
Use cases:
- Transactional emails
- Notifications
- Alerts
- One-way communication
Limitations:
- Cannot receive/monitor incoming emails
- No reply tracking
IMAP Only (imap)
{
"type": "imap",
"app_id": "cloudapp_abc123xyz",
"webhook": {
"url": "https://yourapp.com/webhooks/incoming",
"method": "POST"
}
}
Use cases:
- Reply monitoring
- Inbox automation
- Email parsing
- Support ticket systems
Requirements:
- Webhook URL is required
- EDITH monitors inbox and sends new emails to your webhook
Limitations:
- Cannot send emails
SMTP + IMAP (smtp_imap)
{
"type": "smtp_imap",
"app_id": "cloudapp_abc123xyz",
"webhook": {
"url": "https://yourapp.com/webhooks/incoming",
"method": "POST"
}
}
Use cases:
- Full email conversations
- Support systems
- CRM integrations
- Two-way communication
Features:
- Send emails via SMTP
- Monitor inbox via IMAP
- Receive webhooks for incoming emails
- Full threading support
⭐ Recommended for most applications!
Part 5: Labels (Gmail) / Folders (Outlook)
Labels help organize and filter incoming emails.
How Labels Work
Gmail:
- Uses labels system (multiple labels per email)
- Common labels:
INBOX,SENT,DRAFT,SPAM,TRASH - Custom labels: User-created categories
Outlook:
- Uses folders system (one folder per email)
- Common folders:
Inbox,Sent Items,Drafts,Junk Email - Custom folders: User-created folders
EDITH Label Handling
When EDITH fetches emails, it includes label information:
{
"event": "INCOMING_EMAIL",
"details": {
"from": "customer@example.com",
"subject": "Re: Support inquiry",
"label-ids": ["INBOX", "UNREAD"],
"body-html": "...",
"mailer-service": "gmail"
}
}
Common Gmail Labels
| Label | Description |
|---|---|
INBOX | Main inbox |
SENT | Sent emails |
DRAFT | Draft emails |
SPAM | Spam/junk emails |
TRASH | Deleted emails |
UNREAD | Unread marker |
STARRED | Starred/flagged |
IMPORTANT | Marked as important |
Filtering by Label
You can filter which emails to process based on labels:
// In your webhook handler
app.post('/webhooks/incoming', (req, res) => {
const { details } = req.body;
const labels = details['label-ids'] || [];
// Only process inbox emails
if (labels.includes('INBOX')) {
// Process email
processEmail(details);
}
// Ignore spam
if (labels.includes('SPAM')) {
console.log('Ignoring spam email');
return res.status(200).json({ received: true });
}
// Handle sent emails differently
if (labels.includes('SENT')) {
// Track sent emails
trackSentEmail(details);
}
res.status(200).json({ received: true });
});
Label Monitoring Configuration
By default, EDITH monitors:
INBOX- All incoming emails- Excludes
SPAM,TRASHautomatically
Custom Label Monitoring (coming soon):
{
"type": "imap",
"monitor_labels": ["INBOX", "CUSTOM_LABEL"],
"exclude_labels": ["SPAM", "TRASH", "PROMOTIONS"]
}
Part 6: Token Management
Token Lifecycle
Access Token:
- Short-lived (~1 hour)
- Used for API requests
- Automatically refreshed by EDITH
Refresh Token:
- Long-lived (no expiration for Google, 90 days for Microsoft)
- Used to obtain new access tokens
- Stored securely by EDITH
Automatic Token Refresh
EDITH automatically handles token refresh:
- Access token expires
- EDITH detects expiration
- Uses refresh token to get new access token
- Updates configuration
- Continues operations seamlessly
You don't need to worry about token refresh!
Token Revocation
Users can revoke access at any time:
Gmail:
- Go to Google Account Permissions
- Remove your app
Outlook:
- Go to Microsoft Account Apps
- Remove your app
When revoked:
- EDITH receives authentication errors
- Connection becomes inactive
- User must re-authenticate
Handling Revoked Tokens
// Webhook payload when OAuth fails
{
"event": "OAUTH_ERROR",
"details": {
"mailer_id": "mailer_abc123",
"email": "user@gmail.com",
"error": "invalid_grant",
"message": "Token has been revoked"
}
}
Action: Prompt user to reconnect their account
Part 7: Complete Integration Example
Frontend Integration (React)
import React, { useState } from 'react';
function EmailConnection() {
const [loading, setLoading] = useState(false);
const connectGmail = async () => {
setLoading(true);
try {
// Call your backend
const response = await fetch('/api/oauth/initiate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
provider: 'google',
connection_type: 'smtp_imap'
})
});
const data = await response.json();
if (data.success && data.oauth_url) {
// Redirect to OAuth URL
window.location.href = data.oauth_url;
}
} catch (error) {
console.error('OAuth error:', error);
setLoading(false);
}
};
return (
<div>
<h2>Connect Your Email</h2>
<button onClick={connectGmail} disabled={loading}>
{loading ? 'Connecting...' : 'Connect Gmail'}
</button>
</div>
);
}
Backend Integration (Node.js/Express)
const express = require('express');
const axios = require('axios');
const app = express();
// Initiate OAuth
app.post('/api/oauth/initiate', async (req, res) => {
const { provider, connection_type } = req.body;
const userId = req.user.id; // From your auth middleware
try {
// Call EDITH OAuth API
const response = await axios.post(
'https://api.edith.example.com/v1/oauth/login',
{
app_id: process.env.CLOUD_APP_ID,
type: connection_type,
exchange_token: false,
webhook: {
url: `${process.env.APP_URL}/webhooks/incoming-email`,
method: 'POST',
headers: {
'X-Webhook-Secret': process.env.WEBHOOK_SECRET
}
},
state: Buffer.from(JSON.stringify({ userId })).toString('base64')
},
{
headers: {
'Authorization': `Bearer ${process.env.EDITH_API_TOKEN}`,
'Content-Type': 'application/json'
}
}
);
res.json({
success: true,
oauth_url: response.data.url
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// OAuth callback handler
app.get('/oauth/callback', async (req, res) => {
const { email, mailer_id, verified, state } = req.query;
if (!verified) {
return res.redirect('/oauth/error?message=Connection failed');
}
// Decode state to get user ID
const { userId } = JSON.parse(Buffer.from(state, 'base64').toString());
// Activate the connection
try {
await axios.put(
'https://api.edith.example.com/v1/oauth/active',
{ mailer_id, active: true },
{
headers: {
'Authorization': `Bearer ${process.env.EDITH_API_TOKEN}`,
'Content-Type': 'application/json'
}
}
);
// Save to your database
await db.emailConnections.create({
userId,
email,
mailerId: mailer_id,
provider: 'gmail',
active: true
});
res.redirect('/oauth/success');
} catch (error) {
res.redirect('/oauth/error?message=Activation failed');
}
});
// Webhook handler
app.post('/webhooks/incoming-email', (req, res) => {
// Verify webhook secret
if (req.headers['x-webhook-secret'] !== process.env.WEBHOOK_SECRET) {
return res.status(401).send('Unauthorized');
}
const { event, details } = req.body;
if (event === 'INCOMING_EMAIL') {
// Process incoming email
console.log('New email from:', details.from);
console.log('Subject:', details.subject);
console.log('Labels:', details['label-ids']);
// Your business logic here
processIncomingEmail(details);
}
res.status(200).json({ received: true });
});
Best Practices
Security
- Store secrets securely - Use environment variables, never hardcode
- Validate webhooks - Always check webhook secrets
- Use HTTPS - All redirect URIs must be HTTPS
- Encrypt tokens - If storing tokens, encrypt them
- Limit scopes - Only request necessary permissions
User Experience
- Clear permissions - Explain why you need email access
- Handle errors gracefully - Show helpful error messages
- Allow reconnection - Let users reconnect if tokens expire
- Status indicators - Show connection status in UI
- Revocation handling - Detect and handle revoked access
Implementation
- Use state parameter - Track user/session across OAuth flow
- Handle timeouts - OAuth flow can take time
- Test thoroughly - Test with multiple accounts
- Monitor webhooks - Log and monitor webhook deliveries
- Implement retries - Handle temporary failures
Troubleshooting
| Issue | Cause | Solution |
|---|---|---|
invalid_client | Wrong client ID/secret | Verify cloud app credentials |
redirect_uri_mismatch | Redirect URI doesn't match | Check cloud app configuration |
invalid_grant | Token expired/revoked | User must re-authenticate |
insufficient_scope | Missing permissions | Update scopes in cloud app |
access_denied | User denied permission | User refused access |
Webhook not receiving | Wrong URL or headers | Verify webhook configuration |
Token refresh failed | Refresh token invalid | User must reconnect |
API Reference Summary
Register Cloud App
POST /v1/cloudapp/create
Initiate OAuth
POST /v1/oauth/login
Activate Connection
PUT /v1/oauth/active
Get OAuth Config
GET /v1/oauth/config
For detailed API specifications, see the API Reference