Handling OAuth2 tokens is complex. dataflows abstracts this away completely.
connections TableTokens are stored in the connections table.
access_token and refresh_token are encrypted using AES-256-GCM before being written to the database.expires_at and refreshes the token before a workflow step runs.We separate the "Application" (Service Client) from the "User" (Connection).
client_id and client_secret.access_token.Workflows never access secrets directly. The engine injects pre-authenticated clients into each step.
// ❌ BAD: Handling tokens manually
const token = await db.tokens.find(...)
await fetch('https://api.github.com', {
headers: { Authorization: token }
})
// ✅ GOOD: Authenticated client injected by the engine
export const syncIssues = defineWorkflow({
id: 'sync-github-issues',
trigger: schedule.every('1h'),
async run({ step, clients }) {
// The engine resolves the connection, refreshes tokens
// if needed, and hands you a ready-to-use client.
return step.run('fetch-issues', () =>
clients.github.listIssues({ repo: 'dataflows-io/app' })
)
}
})
Each workflow execution runs in an isolated context.