Loading...
Loading...
Compare original and translation side by side
./references/./references/authentication.mdauthentication.mdbotpress-client.mdbotpress-client.mdtype-generation.md.adk/type-generation.md.adk/calling-actions.mdcalling-actions.md// 1. Cookie helpers
function setCookie(name: string, value: string, days = 365);
function getCookie(name: string): string | null;
function deleteCookie(name: string);
// 2. AuthContext
interface AuthContextType {
token: string | null;
isAuthenticated: boolean;
userProfile: UserProfile | null;
isLoadingProfile: boolean;
login: (token: string) => void;
logout: () => void;
}
// 3. OAuth Flow
// Redirect: https://app.botpress.cloud/cli-login?redirect=...
// Callback: /auth/callback?pat=bp_pat_...
// Store PAT in cookie and navigate to app// 1. Cookie助手
function setCookie(name: string, value: string, days = 365);
function getCookie(name: string): string | null;
function deleteCookie(name: string);
// 2. AuthContext
interface AuthContextType {
token: string | null;
isAuthenticated: boolean;
userProfile: UserProfile | null;
isLoadingProfile: boolean;
login: (token: string) => void;
logout: () => void;
}
// 3. OAuth Flow
// 重定向:https://app.botpress.cloud/cli-login?redirect=...
// 回调:/auth/callback?pat=bp_pat_...
// 将PAT存储在Cookie中并导航到应用// stores/clientsStore.ts
const useClientsStore = create<ClientsState>()((set, get) => ({
APIClients: {},
getAPIClient: (props) => {
const key = props?.botId
? `${props.workspaceId}-${props.botId}`
: (props?.workspaceId ?? DEFAULT_API_CLIENT_KEY);
const cached = get().APIClients[key];
if (cached) return cached;
const newClient = new APIClient({
apiUrl: API_BASE_URL,
workspaceId: props?.workspaceId,
token: getPat() ?? "",
botId: props?.botId,
});
set((state) => ({
APIClients: { ...state.APIClients, [key]: newClient },
}));
return newClient;
},
}));
export const getApiClient = (props?) =>
useClientsStore.getState().getAPIClient(props);// stores/clientsStore.ts
const useClientsStore = create<ClientsState>()((set, get) => ({
APIClients: {},
getAPIClient: (props) => {
const key = props?.botId
? `${props.workspaceId}-${props.botId}`
: (props?.workspaceId ?? DEFAULT_API_CLIENT_KEY);
const cached = get().APIClients[key];
if (cached) return cached;
const newClient = new APIClient({
apiUrl: API_BASE_URL,
workspaceId: props?.workspaceId,
token: getPat() ?? "",
botId: props?.botId,
});
set((state) => ({
APIClients: { ...state.APIClients, [key]: newClient },
}));
return newClient;
},
}));
export const getApiClient = (props?) =>
useClientsStore.getState().getAPIClient(props);// types/index.ts
/// <reference path="../../../bot/.adk/action-types.d.ts" />
/// <reference path="../../../bot/.adk/table-types.d.ts" />
import type { BotActionDefinitions } from "@botpress/runtime/_types/actions";
import type { TableDefinitions } from "@botpress/runtime/_types/tables";
// Create type aliases
export type SendMessageAction = BotActionDefinitions["sendMessage"];
export type TicketTableRow = TableDefinitions["TicketsTable"]["Output"];// types/index.ts
/// <reference path="../../../bot/.adk/action-types.d.ts" />
/// <reference path="../../../bot/.adk/table-types.d.ts" />
import type { BotActionDefinitions } from "@botpress/runtime/_types/actions";
import type { TableDefinitions } from "@botpress/runtime/_types/tables";
// 创建类型别名
export type SendMessageAction = BotActionDefinitions["sendMessage"];
export type TicketTableRow = TableDefinitions["TicketsTable"]["Output"];// services/bot-service.ts
export async function sendMessage(input: SendMessageAction["input"]) {
const client = getApiClient({ botId, workspaceId });
const result = await client.callAction({
type: "sendMessage",
input,
});
return result.output as SendMessageAction["output"];
}
// Component usage with useMutation
const { mutate: send, isPending } = useMutation({
mutationFn: sendMessage,
onSuccess: () => {
toast.success("Message sent");
queryClient.invalidateQueries({ queryKey: ["messages"] });
},
onError: (error) => {
toast.error("Failed to send message");
},
});// services/bot-service.ts
export async function sendMessage(input: SendMessageAction["input"]) {
const client = getApiClient({ botId, workspaceId });
const result = await client.callAction({
type: "sendMessage",
input,
});
return result.output as SendMessageAction["output"];
}
// 组件中结合useMutation使用
const { mutate: send, isPending } = useMutation({
mutationFn: sendMessage,
onSuccess: () => {
toast.success("消息已发送");
queryClient.invalidateQueries({ queryKey: ["messages"] });
},
onError: (error) => {
toast.error("消息发送失败");
},
});Question: "How do I authenticate users in my frontend?"
Answer:
The recommended pattern uses cookie-based PAT storage with OAuth flow.
Here's the complete implementation:
1. Cookie Helpers (authentication.md:89-111)
[code example]
2. AuthContext Setup (authentication.md:64-76)
[code example]
3. OAuth Flow (authentication.md:473-533)
[code example]
Key Security Considerations:
- Use SameSite=Lax for CSRF protection
- Always use HTTPS in production
- Never log PATs to console
- Implement token expiration handling
Related Topics:
- Route protection: authentication.md:369-467
- Profile fetching: authentication.md:304-347
- Client initialization: botpress-client.md:29-51问题:"如何在我的前端应用中对用户进行认证?"
回答:
推荐的模式是使用基于Cookie的PAT存储及OAuth流程。
以下是完整的实现:
1. Cookie助手(authentication.md:89-111)
[代码示例]
2. AuthContext设置(authentication.md:64-76)
[代码示例]
3. OAuth流程(authentication.md:473-533)
[代码示例]
核心安全注意事项:
- 使用SameSite=Lax以防止CSRF攻击
- 生产环境中始终使用HTTPS
- 切勿将PAT记录到控制台
- 实现令牌过期处理
相关主题:
- 路由保护:authentication.md:369-467
- 资料获取:authentication.md:304-347
- 客户端初始化:botpress-client.md:29-51// ✅ CORRECT - Use client store
const client = getApiClient({ workspaceId, botId });
// ❌ WRONG - Create new client every time
const client = new Client({ apiUrl, workspaceId, token, botId });// ✅ 正确 - 使用客户端存储
const client = getApiClient({ workspaceId, botId });
// ❌ 错误 - 每次创建新客户端
const client = new Client({ apiUrl, workspaceId, token, botId });// ✅ CORRECT - Triple-slash at top of file
/// <reference path="../../../bot/.adk/action-types.d.ts" />
import type { BotActionDefinitions } from "@botpress/runtime/_types/actions";
// ❌ WRONG - No triple-slash reference
import type { BotActionDefinitions } from "@botpress/runtime/_types/actions";// ✅ 正确 - 文件顶部使用Triple-slash
/// <reference path="../../../bot/.adk/action-types.d.ts" />
import type { BotActionDefinitions } from "@botpress/runtime/_types/actions";
// ❌ 错误 - 无Triple-slash引用
import type { BotActionDefinitions } from "@botpress/runtime/_types/actions";// ✅ CORRECT - Service layer with types
export async function sendMessage(input: SendMessageAction["input"]) {
const client = getApiClient({ botId, workspaceId });
const result = await client.callAction({ type: "sendMessage", input });
return result.output as SendMessageAction["output"];
}
// ❌ WRONG - Direct call in component
const result = await client.callAction({ type: "sendMessage", input: data });// ✅ 正确 - 带类型的服务层
export async function sendMessage(input: SendMessageAction["input"]) {
const client = getApiClient({ botId, workspaceId });
const result = await client.callAction({ type: "sendMessage", input });
return result.output as SendMessageAction["output"];
}
// ❌ 错误 - 在组件中直接调用
const result = await client.callAction({ type: "sendMessage", input: data });// ✅ CORRECT - Cookie with SameSite
document.cookie = `token=${value};expires=${expires};path=/;SameSite=Lax`;
// ❌ WRONG - localStorage without security
localStorage.setItem("token", value);// ✅ 正确 - 带SameSite的Cookie
document.cookie = `token=${value};expires=${expires};path=/;SameSite=Lax`;
// ❌ 错误 - 无安全措施的localStorage
localStorage.setItem("token", value);