Support Workforce bearer token as alternative to email/password OAuth

Adds bearer_token secret field — if configured, the auth service uses it
directly and skips the OAuth password grant entirely. Falls back to
email/password flow if no token is set.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
jtricerolph 2026-07-07 12:34:52 +00:00
parent 5053b37adf
commit e758add8ae
2 changed files with 20 additions and 10 deletions

View file

@ -30,7 +30,7 @@ export const INTEGRATIONS = {
workforce: { workforce: {
name: 'Workforce', name: 'Workforce',
configFields: ['base_url', 'sync_hours'], configFields: ['base_url', 'sync_hours'],
secretFields: ['email', 'password'], secretFields: ['bearer_token', 'email', 'password'],
}, },
smtp: { smtp: {
name: 'SMTP Email', name: 'SMTP Email',

View file

@ -89,15 +89,25 @@ export async function integrationRoutes(app) {
else if (req.params.slug === 'resos') await resosTest(creds) else if (req.params.slug === 'resos') await resosTest(creds)
else if (req.params.slug === 'workforce') { else if (req.params.slug === 'workforce') {
const baseUrl = creds.base_url || 'https://my.workforce.com' const baseUrl = creds.base_url || 'https://my.workforce.com'
const res = await fetch(`${baseUrl}/api/oauth/token`, { if (creds.bearer_token) {
method: 'POST', // Test bearer token directly with a lightweight API call
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, const res = await fetch(`${baseUrl}/api/v2/users?page=1&page_size=1`, {
body: new URLSearchParams({ grant_type: 'password', username: creds.email, password: creds.password, scope: 'platform' }).toString(), headers: { Authorization: `Bearer ${creds.bearer_token}` },
signal: AbortSignal.timeout(8000), signal: AbortSignal.timeout(8000),
}) })
if (!res.ok) throw new Error(`Workforce auth failed (${res.status})`) if (!res.ok) throw new Error(`Workforce bearer token invalid (${res.status})`)
const data = await res.json() } else {
if (!data.access_token) throw new Error('No access token returned') if (!creds.email || !creds.password) throw new Error('No bearer token or email/password configured')
const res = await fetch(`${baseUrl}/api/oauth/token`, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({ grant_type: 'password', username: creds.email, password: creds.password, scope: 'platform' }).toString(),
signal: AbortSignal.timeout(8000),
})
if (!res.ok) throw new Error(`Workforce auth failed (${res.status})`)
const data = await res.json()
if (!data.access_token) throw new Error('No access token returned')
}
} }
else if (req.params.slug === 'smtp') { else if (req.params.slug === 'smtp') {
const nodemailer = await import('nodemailer') const nodemailer = await import('nodemailer')