OAuth Integration
Add OAuth authentication to your DAIN service
Overview
DAIN services support OAuth2 authentication through a built-in OAuth system. This allows your service to authenticate users on their assistant with providers like GitHub, Google, and others.
Configuration
Configure OAuth in your service definition:
import { defineDAINService, createOAuth2Tool } from "@dainprotocol/service-sdk";
const dainService = defineDAINService({
metadata: { /* ... */ },
oauth2: {
baseUrl: process.env.TUNNEL_URL,
providers: {
github: {
clientId: process.env.GITHUB_CLIENT_ID,
clientSecret: process.env.GITHUB_CLIENT_SECRET,
authorizationUrl: "https://github.com/login/oauth/authorize",
tokenUrl: "https://github.com/login/oauth/access_token",
scopes: ["user", "gist"],
onSuccess: async (agentId, tokens) => {
await tokenStore.set(agentId, tokens);
}
}
}
}
});
Token Storage
Implement secure token storage:
interface OAuth2Tokens {
accessToken: string;
refreshToken?: string;
expiresAt: number;
provider: string;
agentId: string;
}
// Development
const tokenStore = new Map<string, OAuth2Tokens>();
// Production
class DatabaseTokenStore {
async set(agentId: string, tokens: OAuth2Tokens) {
await db.tokens.upsert({ agentId, ...tokens });
}
async get(agentId: string): Promise<OAuth2Tokens | null> {
return db.tokens.findUnique({ where: { agentId } });
}
}
Authentication Methods
Method 1: OAuth UI Builder
Use the OAuthUIBuilder for clean authentication flows:
import { OAuthUIBuilder } from '@dainprotocol/utils';
const githubTool: ToolConfig = {
id: "github-tool",
name: "GitHub Tool",
description: "Interact with GitHub",
handler: async (inputs, agentInfo, { app }) => {
const tokens = await tokenStore.get(agentInfo.id);
if (!tokens) {
const authUrl = await app.oauth2?.generateAuthUrl(
"github",
agentInfo.id
);
return {
text: "GitHub authentication required",
data: null,
ui: new OAuthUIBuilder()
.title("GitHub Authentication")
.content("Please authenticate to continue")
.url(authUrl)
.provider("github")
.build()
};
}
// Continue with authenticated request...
}
};
Method 2: Authentication Helper
Create a reusable authentication helper:
import { Hono } from 'hono';
import { AgentInfo } from '@dainprotocol/service-sdk/service';
import { OAuthUIBuilder } from '@dainprotocol/utils';
interface RequestAuthentication {
app: Hono;
agentInfo: AgentInfo;
}
export const requestAuthenticationTool = (
provider: string,
{ app, agentInfo }: RequestAuthentication
) => {
const authUrl = await app.oauth2?.generateAuthUrl(provider, agentInfo.id);
return {
text: `Please authenticate with ${provider}`,
data: null,
ui: new OAuthUIBuilder()
.title(`${provider} Authentication`)
.content(`Authentication required`)
.url(authUrl)
.provider(provider)
.build()
};
};
Complete Example: GitHub Gist Service
import {
defineDAINService,
ToolConfig,
createOAuth2Tool
} from "@dainprotocol/service-sdk";
import { OAuthUIBuilder } from '@dainprotocol/utils';
const createGistConfig: ToolConfig = {
id: "create-gist",
name: "Create GitHub Gist",
description: "Creates a new GitHub Gist",
input: z.object({
description: z.string(),
filename: z.string(),
content: z.string()
}),
handler: async ({ description, filename, content }, agentInfo, { app }) => {
const tokens = tokenStore.get(agentInfo.id);
if (!tokens) {
return requestAuthenticationTool("github", { app, agentInfo });
}
// Make authenticated request
const response = await fetch("https://api.github.com/gists", {
method: "POST",
headers: {
Authorization: `Bearer ${tokens.accessToken}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
description,
public: true,
files: {
[filename]: { content }
}
})
});
const gist = await response.json();
return {
text: `Created Gist: ${gist.html_url}`,
data: { url: gist.html_url },
ui: new CardUIBuilder()
.title("Gist Created")
.content(`URL: ${gist.html_url}`)
.build()
};
}
};
const dainService = defineDAINService({
metadata: {
title: "GitHub Service",
description: "GitHub integration with OAuth"
},
oauth2: {
baseUrl: process.env.TUNNEL_URL,
providers: {
github: {
clientId: process.env.GITHUB_CLIENT_ID,
clientSecret: process.env.GITHUB_CLIENT_SECRET,
authorizationUrl: "https://github.com/login/oauth/authorize",
tokenUrl: "https://github.com/login/oauth/access_token",
scopes: ["gist"],
onSuccess: async (agentId, tokens) => {
await tokenStore.set(agentId, tokens);
}
}
}
},
tools: [createOAuth2Tool("github"), createGistConfig]
});
Provider Setup
- Register your application in the provider's developer portal
- Configure callback URLs:
https://your-base-url/oauth2/callback/{provider}
- Store credentials securely:
GITHUB_CLIENT_ID=your_client_id GITHUB_CLIENT_SECRET=your_client_secret
Important Notes
- Use environment variables for credentials
- Store tokens securely in production
- HTTPS is required for callbacks
- Default callback path is
/oauth2/callback/{provider}
- Token refresh is handled automatically
- Test with tunnel URLs in development