AuthConnection
AuthConnection enables OAuth2 authentication connections with external providers, featuring server-side secret management and seamless Function runtime integration. Unlike traditional auth integrations focused on user authentication, AuthConnection is designed for application-to-application OAuth2 flows where your functions need to access external APIs on behalf of your application.
Overview
AuthConnection provides a secure way to:
- Manage OAuth2 connections with external providers (Google, Microsoft 365, QuickBooks)
- Store client secrets server-side eliminating client-side secret exposure
- Access tokens from Functions
- Handle token refresh automatically for long-running integrations
Prerequisites
- A Tailor Platform workspace with Auth service enabled
- OAuth2 application configured with your chosen provider
- Basic understanding of OAuth2 flows
- Function service enabled (for runtime integration)
- An SDK project with a
tailor.config.ts— see the SDK Quickstart
Setup Flow
An auth connection can be managed two ways: through your SDK config (defineAuth() in tailor.config.ts, deployed with the rest of your app) or entirely through the Console. Either way, setting up a connection is two steps:
- Create the connection (registers the OAuth2 provider credentials)
- Authorize the connection (runs the OAuth2 flow to obtain and store tokens)
A given connection can only be managed by one side at a time — see Choosing a Management Method below.
Option A: Manage via SDK Config
1. Configure the Connection
Add a connections block to defineAuth() in tailor.config.ts:
import { defineAuth } from "@tailor-platform/sdk";
export const auth = defineAuth("my-auth", {
// ...other auth config
connections: {
"google-connection": {
type: "oauth2",
providerUrl: "https://accounts.google.com",
issuerUrl: "https://accounts.google.com",
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
},
},
});Store the client ID and secret in your .env file or CI secrets — never commit them.
Connection config fields:
| Field | Type | Required | Description |
|---|---|---|---|
type | string | Yes | Connection type. Currently only "oauth2". |
providerUrl | string | Yes | OAuth2 provider URL. |
issuerUrl | string | Yes | Token issuer URL for JWT validation. |
clientId | string | Yes | OAuth2 client ID from the provider. |
clientSecret | string | Yes | OAuth2 client secret from the provider. |
authUrl | string | No | Override for the authorization endpoint. |
tokenUrl | string | No | Override for the token endpoint. |
Deploy to register the connection with the platform:
tailor-sdk deployThis creates (or updates) the connection record. A newly created connection exists but is not yet authorized — it has no tokens yet.
NOTE
Deploy updates existing connections in-place, preserving the OAuth token. If a change requires re-authorization (for example, the provider URL or client ID changed), the deploy will warn you to run authconnection authorize again.
TIP
Once a connection has been deployed at least once, later deploys can pass clientSecret: "" (an empty string) without erasing the secret already stored on the platform — an empty value is simply left out of the update, so nothing about the secret changes. This lets a CI pipeline redeploy an existing connection (for example, to pick up a providerUrl change) without ever having access to the real secret. The very first deploy that creates the connection still needs the real secret at least once, from wherever that value is available.
clientSecret is a required field, so it must resolve to an actual string — undefined fails config validation before deploy even runs. Use a fallback so an unset variable still resolves to a string:
clientSecret: process.env.GOOGLE_CLIENT_SECRET ?? "", // Falls back to "" when the var is unset, e.g. in CIWith the .env file itself, an explicit empty assignment (GOOGLE_CLIENT_SECRET=) also works, since that sets the variable to "" rather than leaving it unset.
2. Authorize the Connection
tailor-sdk authconnection authorize --name google-connection \
--scopes "openid,profile,email" \
--port 8080Parameters:
--name(required): Name of the connection defined indefineAuth()--scopes(optional): OAuth2 scopes to request (default:openid,profile,email)--port(optional): Local callback server port (default:8080)--no-browser(optional): Don't open browser automatically
This command:
- Starts a local HTTP server for the OAuth2 callback
- Opens your browser to the provider's authorization page
- Handles the callback after authorization
- Exchanges the authorization code for tokens using the client secret from your config
- Stores tokens securely on the server
Alternatively, run tailor-sdk authconnection open to authorize the connection from the Console instead of the local CLI flow — useful on a machine without browser access:
tailor-sdk authconnection openVerify the connection is authorized:
tailor-sdk authconnection listOption B: Manage Entirely via the Console
No tailor.config.ts changes are needed — create, authorize, and manage the connection directly in the Tailor Platform Console:
tailor-sdk authconnection openThis opens the workspace's connections settings page, where you can:
- Create a new connection — enter the type, provider URL, issuer URL, client ID, and client secret directly in the Console form
- Authorize it — the Console walks you through the OAuth2 consent flow in the browser
- View its status, re-authorize, revoke its tokens, or delete it later from the same page
This is a good fit for connections you don't want tracked in git — for example, ones that differ per developer, or that only ever target a single shared workspace and don't need to travel with the rest of your app's config.
Choosing a Management Method
A connection name is owned by exactly one side at a time — the same connection cannot be managed by both the Console and your SDK config simultaneously:
- A connection created via the Console (Option B) carries no SDK ownership label.
tailor-sdk deployleaves it completely untouched as long as it isn't also declared inconnections. - If you later add a connection with the same name to
defineAuth()'sconnectionsand rundeploy, the SDK finds it already exists without an ownership label and pauses to ask whether it should take it over ("Allow tailor-sdk to manage these resources?"). Confirming adopts it into config management — from that point on, everydeployoverwrites its fields to match your config, and removing it fromconnectionsdeletes the connection. (--yesconfirms automatically, which is useful for CI but means this adoption happens without a prompt.) - Until you confirm that handover,
deployrefuses to proceed, so a Console-managed connection is never silently overwritten by config just because a connection with the same name appears intailor.config.ts.
In short: use the Console (Option B) for connections you intend to manage by hand, and SDK config (Option A) for connections you want defined, reviewed, and deployed alongside the rest of your app. Don't mix the two for the same connection name.
Provider Configuration Examples
Google OAuth2
First, create OAuth2 credentials in Google Cloud Console:
- Go to "APIs & Services" > "Credentials"
- Create OAuth 2.0 Client ID
- Configure authorized redirect URIs
connections: {
"google-oauth": {
type: "oauth2",
providerUrl: "https://accounts.google.com",
issuerUrl: "https://accounts.google.com",
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
},
},# 1. Deploy the connection
tailor-sdk deploy
# 2. Authorize and get tokens
tailor-sdk authconnection authorize --name google-oauth \
--scopes "https://www.googleapis.com/auth/admin.directory.user.readonly"Common Google OAuth2 URLs:
- Authorization endpoint:
https://accounts.google.com/o/oauth2/v2/auth - Token endpoint:
https://oauth2.googleapis.com/token - User info endpoint:
https://www.googleapis.com/oauth2/v2/userinfo
Microsoft 365 / Azure AD
First, register an application in Azure Portal:
- Go to "Azure Active Directory" > "App registrations"
- Create new registration
- Configure redirect URIs under "Authentication"
- Create client secret under "Certificates & secrets"
connections: {
"ms365-oauth": {
type: "oauth2",
providerUrl: "https://login.microsoftonline.com/YOUR_TENANT_ID",
issuerUrl: "https://login.microsoftonline.com/YOUR_TENANT_ID/v2.0",
clientId: process.env.AZURE_CLIENT_ID!,
clientSecret: process.env.AZURE_CLIENT_SECRET!,
},
},# 1. Deploy the connection
tailor-sdk deploy
# 2. Authorize and get tokens
tailor-sdk authconnection authorize --name ms365-oauth \
--scopes "https://graph.microsoft.com/.default"Replace YOUR_TENANT_ID with your Azure AD tenant ID or use common for multi-tenant applications.
Common Microsoft OAuth2 URLs:
- Authorization endpoint:
https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize - Token endpoint:
https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token - User info endpoint:
https://graph.microsoft.com/v1.0/me
QuickBooks OAuth2
First, create an app in Intuit Developer Portal:
- Create a new app
- Select OAuth 2.0 for authorization
- Configure redirect URIs
- Get your client ID and secret from "Keys & OAuth"
connections: {
"quickbooks-oauth": {
type: "oauth2",
providerUrl: "https://appcenter.intuit.com/connect/oauth2",
issuerUrl: "https://oauth.platform.intuit.com/op/v1",
clientId: process.env.QUICKBOOKS_CLIENT_ID!,
clientSecret: process.env.QUICKBOOKS_CLIENT_SECRET!,
},
},tailor-sdk deployCommon QuickBooks OAuth2 URLs:
- Authorization endpoint:
https://appcenter.intuit.com/connect/oauth2 - Token endpoint:
https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer - User info endpoint:
https://accounts.platform.intuit.com/v1/openid_connect/userinfo
QuickBooks Scopes:
com.intuit.quickbooks.accounting- QuickBooks Online APIcom.intuit.quickbooks.payment- Payments APIopenid- OpenID Connectprofile- User profile informationemail- User email address
Function Runtime Integration
AuthConnection integrates seamlessly with the Function service, allowing your functions to access OAuth2 tokens and call external APIs.
Basic Usage
Use authconnection.getConnectionToken() from @tailor-platform/sdk/runtime to retrieve the current access token by connection name. When connections is defined in defineAuth(), the connection name is type-checked and autocompleted against the defined keys:
import { authconnection } from "@tailor-platform/sdk/runtime";
export default async () => {
// Get access token for a connection
const tokens = await authconnection.getConnectionToken("google-oauth");
// Use the access token to call external APIs
const response = await fetch("https://www.googleapis.com/oauth2/v2/userinfo", {
headers: {
Authorization: `Bearer ${tokens.access_token}`,
},
});
if (!response.ok) {
throw new Error(`Failed to fetch user info: ${response.statusText}`);
}
const userInfo = await response.json();
return userInfo;
};// authconnection.getConnectionToken("unknown-connection"); // ❌ TypeScript errorType narrowing is provided by the generated tailor.d.ts (the ConnectionNameRegistry interface). Run tailor-sdk generate (or deploy) after defining new connections to refresh it. Before the first generate, or when connections is not defined in defineAuth(), getConnectionToken() accepts any string.
Advanced Usage with Error Handling
import { authconnection } from "@tailor-platform/sdk/runtime";
export default async () => {
try {
// Get tokens for multiple connections
const googleTokens = await authconnection.getConnectionToken("google-oauth");
const msTokens = await authconnection.getConnectionToken("ms365-oauth");
// Call Google API
const googleResponse = await fetch("https://www.googleapis.com/oauth2/v2/userinfo", {
headers: {
Authorization: `Bearer ${googleTokens.access_token}`,
},
});
// Call Microsoft Graph API
const msResponse = await fetch("https://graph.microsoft.com/v1.0/me", {
headers: {
Authorization: `Bearer ${msTokens.access_token}`,
},
});
const [googleData, msData] = await Promise.all([googleResponse.json(), msResponse.json()]);
return {
google: googleData,
microsoft: msData,
};
} catch (error) {
console.error("Failed to fetch user data:", error);
throw error;
}
};Token Properties
The getConnectionToken() method returns an object with the following property:
Managing Connections via CLI
These commands work on any connection regardless of which option created it:
# Open the connections page in the Console
tailor-sdk authconnection open
# Authorize (opens browser for OAuth2 flow)
tailor-sdk authconnection authorize --name <connection-name>
# List all connections
tailor-sdk authconnection list
# Revoke a connection's tokens (keeps the connection; re-authorize later)
tailor-sdk authconnection revoke --name <connection-name>
# Delete a connection entirely
tailor-sdk authconnection delete --name <connection-name>To delete a connection managed via SDK config (Option A), remove it from connections and redeploy instead of using authconnection delete directly — manage each connection through one method only, as described above.
For connections not defined in defineAuth()'s connections, authconnection.getConnectionToken() still accepts any string, so you can read tokens for Console-managed connections the same way — you just lose the type-checked autocompletion.
Best Practices
Security Considerations
- Client Secrets: Treat OAuth2 client secrets as passwords. Rotate them regularly.
- HTTPS Only: Always use HTTPS for redirect URIs in production.
- Scope Management: Configure minimal required scopes for your application needs.
- Token Handling: Never log or expose access tokens in your Function code.
Naming Conventions
Use descriptive names that indicate the provider and environment:
google-oauth-prodms365-oauth-devquickbooks-oauth-staging
Environment-Specific Configurations
Since each environment is deployed to its own workspace from the same tailor.config.ts, keep a single connection definition and vary the value via a per-environment .env file — the same pattern used for any other environment-specific value (see Multi-Environment Configuration):
connections: {
"google-oauth": {
type: "oauth2",
providerUrl: "https://accounts.google.com",
issuerUrl: "https://accounts.google.com",
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
},
},tailor-sdk deploy -w <production-workspace-id> --env-file .env.productionTroubleshooting
Common Issues
Invalid Client Error
- Verify client ID and secret are correct
- Check if the OAuth2 app is enabled/active in the provider's console
Redirect URI Mismatch
- Ensure the redirect URI configured in your provider matches exactly with your application
Scope Errors
- Verify requested scopes are enabled in your OAuth2 application
- Some providers require admin consent for certain scopes
Token Expiration
- Implement refresh token flow in your application
- Monitor token expiration times
Re-authorization Required After Deploy
- If
tailor-sdk deploywarns that a connection needs re-authorization, runtailor-sdk authconnection authorize --name <connection-name>again - This happens when identity-changing fields (
providerUrl,issuerUrl,clientId,type) are modified
Function Runtime Errors
- Ensure the connection name exists and is authorized
- Check that the connection has valid, non-expired tokens
- Verify network connectivity to external APIs
Next Steps
- Setting up Auth Connections tutorial - Step-by-step SDK walkthrough
- Auth Service SDK reference - Full auth connections API reference
- Runtime API -
@tailor-platform/sdk/runtimenamespaces, includingauthconnection - Learn about Function service - Understand Function runtime capabilities
- Secret Manager documentation - Secure secret storage patterns
- Auth service overview - Complete authentication system
- Function examples - More Function integration patterns