OAuth Basics
OAuth allows your agent to access user data from services like Gmail, GitHub, and Notion without handling passwords.
How It Works
- User clicks "Connect" in PIE
- PIE redirects to the service (e.g., Google)
- User authorizes your agent
- Service redirects back to PIE with a code
- PIE exchanges code for tokens
- PIE stores tokens securely (encrypted)
- Your agent can now make authenticated requests
┌──────┐ ┌─────┐ ┌─────────┐
│ User │────▶│ PIE │────▶│ Google │
└──────┘ └─────┘ └─────────┘
│ │ │
│ Click │ Redirect │
│ Connect │ to OAuth │
│ │ │
│ │◀────────────│
│ │ Auth code │
│ │ │
│ │────────────▶│
│ │ Exchange │
│ │ for tokens │
│ │ │
│ │◀────────────│
│ │ Tokens │
│ │ (stored) │
│ │ │
│ Ready! │ │
└────────────┘ │Setting Up OAuth
1. Create OAuth App
Register your app with the service:
| Service | Developer Console |
|---|---|
| Cloud Console | |
| GitHub | Developer Settings |
| Notion | Integrations |
| Slack | API Apps |
2. Configure Redirect URI
Set the callback URL in your OAuth app settings:
https://your-pie-domain.com/api/oauth/plugin/{pluginId}/callback3. Add OAuth Config to Manifest
For built-in providers:
{
"oauth": {
"provider": "google",
"scopes": ["https://www.googleapis.com/auth/gmail.readonly"],
"clientIdSecret": "GOOGLE_CLIENT_ID",
"clientSecretSecret": "GOOGLE_CLIENT_SECRET"
}
}For custom providers:
{
"oauth": {
"provider": "custom",
"providerName": "Notion",
"authorizationUrl": "https://api.notion.com/v1/oauth/authorize",
"tokenUrl": "https://api.notion.com/v1/oauth/token",
"scopes": [],
"clientIdSecret": "NOTION_CLIENT_ID",
"clientSecretSecret": "NOTION_CLIENT_SECRET"
}
}4. Store Credentials
Add your OAuth credentials as developer secrets:
{
"developerSecrets": {
"GOOGLE_CLIENT_ID": {
"description": "Google OAuth Client ID",
"required": true
},
"GOOGLE_CLIENT_SECRET": {
"description": "Google OAuth Client Secret",
"required": true
}
}
}Using OAuth in Code
Check Connection
Always check if the user has connected:
async function handler(input, context) {
const isConnected = await context.oauth.isConnected();
if (!isConnected) {
return {
error: true,
message: 'Please connect the service first',
requiresAuth: true,
};
}
// Continue with authenticated requests...
}Make Authenticated Requests
Use context.oauth.fetch() instead of context.fetch():
// PIE automatically adds: Authorization: Bearer {token}
const response = await context.oauth.fetch(
'https://api.example.com/data'
);The token is never visible to your code. PIE injects it automatically.
Full Example
async function handler(input, context) {
// Check connection
if (!await context.oauth.isConnected()) {
return { error: true, requiresAuth: true };
}
// Make authenticated request
const response = await context.oauth.fetch(
'https://api.github.com/user/repos',
{
headers: { 'Accept': 'application/vnd.github.v3+json' }
}
);
if (!response.ok) {
return { error: true, message: `API error: ${response.status}` };
}
const repos = JSON.parse(response.body);
return { repos: repos.map(r => r.name) };
}Token Refresh
PIE handles token refresh automatically:
- Before making a request, PIE checks if token is expired
- If expired (or expiring soon), PIE uses refresh token
- New access token is stored
- Request proceeds with valid token
You don't need to handle this in your code.
Scopes
Scopes define what your agent can access:
{
"oauth": {
"scopes": [
"https://www.googleapis.com/auth/gmail.readonly",
"https://www.googleapis.com/auth/gmail.send"
]
}
}Best practice: Request minimal scopes. Only ask for what you need.
Security
Token Isolation
OAuth tokens are never exposed to your agent code:
context.oauth.fetch()- PIE adds the token- Tokens are encrypted at rest (AES-256-GCM)
- Tokens are scoped to the agent that created them
User Control
Users can disconnect at any time:
- Tokens are deleted from PIE
- Revocation request sent to provider (if supported)
- Agent loses access
Common Issues
"Not Connected" Error
The user hasn't authorized yet. Return requiresAuth: true:
return { error: true, requiresAuth: true };Token Expired
PIE auto-refreshes, but if refresh fails:
- User needs to reconnect
- Check if refresh token was revoked
Wrong Scopes
If you get 403 errors, you may need additional scopes:
- Update manifest with required scopes
- Users need to reconnect to grant new scopes