cclawd 1.0.6 → 1.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/build-info.json +3 -3
- package/dist/plugin-sdk/active-listener-CN-tMEvN.js +35 -0
- package/dist/plugin-sdk/api-key-rotation-CimGYMBc.js +176 -0
- package/dist/plugin-sdk/audio-preflight-C-xXBoE2.js +51 -0
- package/dist/plugin-sdk/audio-transcription-runner-CTIHpebA.js +2173 -0
- package/dist/plugin-sdk/audit-membership-runtime-BFatB2LJ.js +58 -0
- package/dist/plugin-sdk/channel-activity-DO0FEzyj.js +95 -0
- package/dist/plugin-sdk/channel-web-Da-__nUF.js +2238 -0
- package/dist/plugin-sdk/commands-registry-6no2NNrY.js +1118 -0
- package/dist/plugin-sdk/compact.runtime-CCoclu5e.js +35 -0
- package/dist/plugin-sdk/compat.js +35 -35
- package/dist/plugin-sdk/config-B9ODwgpz.js +37426 -0
- package/dist/plugin-sdk/deliver-B1fFpKjV.js +1757 -0
- package/dist/plugin-sdk/deliver-runtime-DB-VRMe1.js +15 -0
- package/dist/plugin-sdk/deps-send-discord.runtime-DklqycYG.js +15 -0
- package/dist/plugin-sdk/deps-send-imessage.runtime-Chs8zeon.js +14 -0
- package/dist/plugin-sdk/deps-send-signal.runtime-clW9aSJP.js +13 -0
- package/dist/plugin-sdk/deps-send-slack.runtime-BUx0LYY1.js +13 -0
- package/dist/plugin-sdk/deps-send-telegram.runtime-LECSHgMG.js +16 -0
- package/dist/plugin-sdk/deps-send-whatsapp.runtime-D2d65fw0.js +40 -0
- package/dist/plugin-sdk/diagnostic-CxIvS-C2.js +315 -0
- package/dist/plugin-sdk/dispatch-BqlR4dPx.js +105863 -0
- package/dist/plugin-sdk/env-b9k1PHMI.js +34 -0
- package/dist/plugin-sdk/fetch-PoxzAANT.js +326 -0
- package/dist/plugin-sdk/fetch-guard-4UVSZ0uS.js +164 -0
- package/dist/plugin-sdk/image-Ch6M4tnJ.js +2420 -0
- package/dist/plugin-sdk/image-runtime-CSh2o5wY.js +8 -0
- package/dist/plugin-sdk/ir-CugsqGH8.js +1312 -0
- package/dist/plugin-sdk/local-roots-adnEg9zb.js +217 -0
- package/dist/plugin-sdk/logger-D6zRubj0.js +1164 -0
- package/dist/plugin-sdk/login-CYvkQ0At.js +54 -0
- package/dist/plugin-sdk/login-qr-ll4NtaT5.js +316 -0
- package/dist/plugin-sdk/manager-CHy8IclH.js +3959 -0
- package/dist/plugin-sdk/manager-runtime-C70EkEr7.js +11 -0
- package/dist/plugin-sdk/outbound-Wzs2iN7X.js +216 -0
- package/dist/plugin-sdk/outbound-attachment-khXJwucX.js +17 -0
- package/dist/plugin-sdk/paths-BtVqCdw4.js +3063 -0
- package/dist/plugin-sdk/pi-model-discovery-Dh4ziodY.js +131 -0
- package/dist/plugin-sdk/pi-model-discovery-runtime-b83Xe-HT.js +8 -0
- package/dist/plugin-sdk/pi-tools.before-tool-call.runtime-C1z5CDBF.js +349 -0
- package/dist/plugin-sdk/proxy-fetch-CJEmoBxi.js +54 -0
- package/dist/plugin-sdk/pw-ai-Dj3Cvlzl.js +1990 -0
- package/dist/plugin-sdk/qmd-manager-egHUAseQ.js +1581 -0
- package/dist/plugin-sdk/resolve-outbound-target-BiICvIKs.js +38 -0
- package/dist/plugin-sdk/runtime-whatsapp-login.runtime-DNApufzW.js +9 -0
- package/dist/plugin-sdk/runtime-whatsapp-outbound.runtime-CBmtfIQ8.js +13 -0
- package/dist/plugin-sdk/send-CScblaI4.js +532 -0
- package/dist/plugin-sdk/send-CeHhnld6.js +407 -0
- package/dist/plugin-sdk/send-DP_c8JfR.js +3277 -0
- package/dist/plugin-sdk/send-Dc5fI6e8.js +495 -0
- package/dist/plugin-sdk/send-l-77_s1_.js +2507 -0
- package/dist/plugin-sdk/session-CkOKZaqa.js +166 -0
- package/dist/plugin-sdk/skill-commands-BohYCgkq.js +336 -0
- package/dist/plugin-sdk/slash-commands.runtime-DpLfVTM6.js +8 -0
- package/dist/plugin-sdk/slash-dispatch.runtime-CASMHwpm.js +35 -0
- package/dist/plugin-sdk/slash-skill-commands.runtime-D7rrJEci.js +9 -0
- package/dist/plugin-sdk/sqlite-CJE3X7Mv.js +1005 -0
- package/dist/plugin-sdk/subagent-registry-runtime-B1oo5bih.js +35 -0
- package/dist/plugin-sdk/tables-D5VgpTmm.js +53 -0
- package/dist/plugin-sdk/target-errors-C6zZ_OpA.js +191 -0
- package/dist/plugin-sdk/tokens-DUnJnpMS.js +50 -0
- package/dist/plugin-sdk/web-TfUM1nSi.js +39 -0
- package/dist/plugin-sdk/whatsapp-actions-DuWJ0j1r.js +71 -0
- package/extensions/cclawd-mfa-auth/README.md +118 -0
- package/extensions/cclawd-mfa-auth/index.ts +382 -0
- package/extensions/cclawd-mfa-auth/openclaw.plugin.json +34 -0
- package/extensions/cclawd-mfa-auth/package.json +27 -0
- package/extensions/cclawd-mfa-auth/src/auth-manager.ts +536 -0
- package/extensions/cclawd-mfa-auth/src/config.ts +82 -0
- package/extensions/cclawd-mfa-auth/src/dabby-client.ts +200 -0
- package/extensions/cclawd-mfa-auth/src/notification-service.ts +417 -0
- package/extensions/cclawd-mfa-auth/src/polling-manager.ts +232 -0
- package/extensions/cclawd-mfa-auth/src/providers/base.ts +25 -0
- package/extensions/cclawd-mfa-auth/src/providers/qr-code.ts +98 -0
- package/extensions/cclawd-mfa-auth/src/sensitive-detector.ts +159 -0
- package/extensions/cclawd-mfa-auth/src/session-resolver.ts +159 -0
- package/extensions/cclawd-mfa-auth/src/types.ts +156 -0
- package/extensions/mfa-auth/node_modules/.package-lock.json +21 -0
- package/extensions/mfa-auth/node_modules/ws/LICENSE +20 -0
- package/extensions/mfa-auth/node_modules/ws/README.md +548 -0
- package/extensions/mfa-auth/node_modules/ws/browser.js +8 -0
- package/extensions/mfa-auth/node_modules/ws/index.js +13 -0
- package/extensions/mfa-auth/node_modules/ws/lib/buffer-util.js +131 -0
- package/extensions/mfa-auth/node_modules/ws/lib/constants.js +19 -0
- package/extensions/mfa-auth/node_modules/ws/lib/event-target.js +292 -0
- package/extensions/mfa-auth/node_modules/ws/lib/extension.js +203 -0
- package/extensions/mfa-auth/node_modules/ws/lib/limiter.js +55 -0
- package/extensions/mfa-auth/node_modules/ws/lib/permessage-deflate.js +528 -0
- package/extensions/mfa-auth/node_modules/ws/lib/receiver.js +706 -0
- package/extensions/mfa-auth/node_modules/ws/lib/sender.js +602 -0
- package/extensions/mfa-auth/node_modules/ws/lib/stream.js +161 -0
- package/extensions/mfa-auth/node_modules/ws/lib/subprotocol.js +62 -0
- package/extensions/mfa-auth/node_modules/ws/lib/validation.js +152 -0
- package/extensions/mfa-auth/node_modules/ws/lib/websocket-server.js +554 -0
- package/extensions/mfa-auth/node_modules/ws/lib/websocket.js +1393 -0
- package/extensions/mfa-auth/node_modules/ws/package.json +69 -0
- package/extensions/mfa-auth/node_modules/ws/wrapper.mjs +8 -0
- package/extensions/mfa-auth/package-lock.json +23 -1
- package/package.json +453 -453
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cclawd MFA Auth Plugin - Main Entry
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { OpenClawPluginApi } from 'openclaw/plugin-sdk';
|
|
6
|
+
import { authManager } from './src/auth-manager.js';
|
|
7
|
+
import { config } from './src/config.js';
|
|
8
|
+
import { NotificationService } from './src/notification-service.js';
|
|
9
|
+
import { pollingManager } from './src/polling-manager.js';
|
|
10
|
+
import { SessionResolver } from './src/session-resolver.js';
|
|
11
|
+
import { sensitiveDetector } from './src/sensitive-detector.js';
|
|
12
|
+
import { qrCodeAuthProvider } from './src/providers/qr-code.js';
|
|
13
|
+
import type { AuthSession, PendingAuthContext } from './src/types.js';
|
|
14
|
+
|
|
15
|
+
const notificationService = NotificationService.getInstance();
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* 发送认证消息
|
|
19
|
+
*/
|
|
20
|
+
async function sendAuthMessage(
|
|
21
|
+
channel: string | undefined,
|
|
22
|
+
accountId: string | undefined,
|
|
23
|
+
to: string,
|
|
24
|
+
message: string,
|
|
25
|
+
userId: string,
|
|
26
|
+
overrideSessionKey?: string,
|
|
27
|
+
): Promise<void> {
|
|
28
|
+
const session: AuthSession = {
|
|
29
|
+
userId,
|
|
30
|
+
sessionId: 'notification',
|
|
31
|
+
authMethod: 'qr-code',
|
|
32
|
+
timestamp: Date.now(),
|
|
33
|
+
originalContext: {
|
|
34
|
+
sessionKey: overrideSessionKey || `${channel || 'web'}:${accountId || ''}:${userId}`,
|
|
35
|
+
senderId: userId,
|
|
36
|
+
commandBody: '',
|
|
37
|
+
channel: channel || 'web',
|
|
38
|
+
accountId: accountId || '',
|
|
39
|
+
to,
|
|
40
|
+
toolName: 'notification',
|
|
41
|
+
toolParams: {},
|
|
42
|
+
timestamp: Date.now(),
|
|
43
|
+
triggerType: 'sensitive_operation',
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
await notificationService.sendAuthNotification(session, message);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* 插件注册函数
|
|
52
|
+
*/
|
|
53
|
+
export default function register(api: OpenClawPluginApi) {
|
|
54
|
+
console.log('[cclawd-mfa-auth] Plugin registration started');
|
|
55
|
+
authManager.registerProvider(qrCodeAuthProvider);
|
|
56
|
+
|
|
57
|
+
notificationService.setConfig(api.config);
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* 处理消息接收事件 - 首次对话认证
|
|
61
|
+
*/
|
|
62
|
+
api.on('message_received', async (event, ctx) => {
|
|
63
|
+
await authManager.ensureInitialized();
|
|
64
|
+
|
|
65
|
+
api.logger.info(
|
|
66
|
+
`[cclawd-mfa-auth] First message auth check: config.requireAuthOnFirstMessage=${config.requireAuthOnFirstMessage}`,
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
if (!config.requireAuthOnFirstMessage) {
|
|
70
|
+
api.logger.warn(`[cclawd-mfa-auth] First message auth is disabled in config, skipping.`);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const content = event.content || '';
|
|
75
|
+
const isReauthCommand = content.trim() === '/reauth';
|
|
76
|
+
|
|
77
|
+
if (isReauthCommand) {
|
|
78
|
+
api.logger.info(`[cclawd-mfa-auth] /reauth command detected, skipping first message auth check`);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// 解析会话信息
|
|
83
|
+
// metadata.senderId 是 WebChat 的真实用户标识
|
|
84
|
+
const metadataSenderId = event.metadata?.senderId as string | undefined;
|
|
85
|
+
|
|
86
|
+
const resolved = SessionResolver.resolveFromContext({
|
|
87
|
+
sessionKey: ctx.sessionKey,
|
|
88
|
+
channelId: ctx.channelId,
|
|
89
|
+
conversationId: ctx.conversationId,
|
|
90
|
+
accountId: ctx.accountId,
|
|
91
|
+
from: event.from,
|
|
92
|
+
senderId: metadataSenderId || ctx.senderId,
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
const userId = resolved.userId;
|
|
96
|
+
|
|
97
|
+
api.logger.info(`[cclawd-mfa-auth] Extracted userId="${userId}", sessionKey="${resolved.sessionKey}"`);
|
|
98
|
+
|
|
99
|
+
// 检查是否已认证
|
|
100
|
+
if (authManager.isUserVerifiedForFirstMessage(userId)) {
|
|
101
|
+
api.logger.info(`[cclawd-mfa-auth] User ${userId} already verified for first message`);
|
|
102
|
+
|
|
103
|
+
const notificationInfo = authManager.checkAndConsumeNotification(userId);
|
|
104
|
+
if (notificationInfo) {
|
|
105
|
+
const messageText = notificationInfo.isReauth
|
|
106
|
+
? '✅ 重新认证成功,请继续对话。'
|
|
107
|
+
: '✅ 首次认证成功,请继续对话。';
|
|
108
|
+
|
|
109
|
+
await sendAuthMessage(
|
|
110
|
+
resolved.channel,
|
|
111
|
+
resolved.accountId,
|
|
112
|
+
event.metadata?.to as string | undefined || event.from || userId,
|
|
113
|
+
messageText,
|
|
114
|
+
userId,
|
|
115
|
+
resolved.sessionKey,
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
api.logger.info(`[cclawd-mfa-auth] First message from unauthenticated user ${userId}, requiring auth`);
|
|
122
|
+
|
|
123
|
+
// 生成认证会话
|
|
124
|
+
const session = await authManager.generateSession(userId, {
|
|
125
|
+
sessionKey: resolved.sessionKey,
|
|
126
|
+
senderId: userId,
|
|
127
|
+
commandBody: event.content || '',
|
|
128
|
+
channel: resolved.channel,
|
|
129
|
+
to: event.metadata?.to as string | undefined || event.from,
|
|
130
|
+
accountId: resolved.accountId,
|
|
131
|
+
toolName: '',
|
|
132
|
+
toolParams: {},
|
|
133
|
+
timestamp: Date.now(),
|
|
134
|
+
triggerType: 'first_message',
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
if (!session) {
|
|
138
|
+
api.logger.error(`[cclawd-mfa-auth] Failed to generate first message auth session for user ${userId}`);
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
api.logger.info(`[cclawd-mfa-auth] Blocking first message from ${userId}`);
|
|
143
|
+
|
|
144
|
+
// 发送认证链接
|
|
145
|
+
const messageText = `🔐 首次对话需要进行认证
|
|
146
|
+
|
|
147
|
+
为了您的账户安全,首次对话前需要完成身份验证。
|
|
148
|
+
|
|
149
|
+
📱 请点击以下链接完成扫码认证:
|
|
150
|
+
${session.qrCodeUrl}
|
|
151
|
+
|
|
152
|
+
验证有效期: ${Math.floor(config.timeout / 60000)} 分钟`;
|
|
153
|
+
|
|
154
|
+
try {
|
|
155
|
+
await sendAuthMessage(
|
|
156
|
+
resolved.channel,
|
|
157
|
+
resolved.accountId,
|
|
158
|
+
event.metadata?.to as string | undefined || event.from || userId,
|
|
159
|
+
messageText,
|
|
160
|
+
userId,
|
|
161
|
+
resolved.sessionKey,
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
// 启动轮询
|
|
165
|
+
pollingManager.startPolling(api, userId, session.sessionId, {
|
|
166
|
+
triggerType: 'first_message',
|
|
167
|
+
isReauth: false,
|
|
168
|
+
channel: resolved.channel,
|
|
169
|
+
accountId: resolved.accountId,
|
|
170
|
+
to: event.metadata?.to as string | undefined,
|
|
171
|
+
sessionKey: resolved.sessionKey,
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
api.logger.info(`[cclawd-mfa-auth] Sent first message auth notification`);
|
|
175
|
+
} catch (error) {
|
|
176
|
+
api.logger.error(`[cclawd-mfa-auth] Failed to send first message auth notification: ${String(error)}`);
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* 处理工具调用前事件 - 敏感操作认证
|
|
182
|
+
*/
|
|
183
|
+
api.on('before_tool_call', async (event, ctx) => {
|
|
184
|
+
await authManager.ensureInitialized();
|
|
185
|
+
if (!config.requireAuthOnSensitiveOperation) {
|
|
186
|
+
return undefined;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const { toolName, params } = event;
|
|
190
|
+
|
|
191
|
+
api.logger.info(`[cclawd-mfa-auth] Tool call detected: ${toolName}`);
|
|
192
|
+
|
|
193
|
+
// 检查是否为敏感操作
|
|
194
|
+
const sensitiveCheck = sensitiveDetector.checkToolCall(toolName, params || {});
|
|
195
|
+
if (!sensitiveCheck.isSensitive) {
|
|
196
|
+
api.logger.info(`[cclawd-mfa-auth] Tool ${toolName} is not sensitive, allowing`);
|
|
197
|
+
return undefined;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// 解析会话信息
|
|
201
|
+
const resolved = SessionResolver.resolveFromContext({
|
|
202
|
+
sessionKey: ctx.sessionKey,
|
|
203
|
+
channelId: ctx.channelId,
|
|
204
|
+
conversationId: ctx.conversationId,
|
|
205
|
+
accountId: ctx.accountId,
|
|
206
|
+
from: ctx.from,
|
|
207
|
+
senderId: ctx.senderId,
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
const userId = resolved.userId;
|
|
211
|
+
|
|
212
|
+
// 检查是否已认证
|
|
213
|
+
if (authManager.isUserVerifiedForSensitiveOps(userId)) {
|
|
214
|
+
api.logger.info(`[cclawd-mfa-auth] User ${userId} is verified for sensitive ops, allowing`);
|
|
215
|
+
|
|
216
|
+
const notificationInfo = authManager.checkAndConsumeNotification(userId);
|
|
217
|
+
if (notificationInfo) {
|
|
218
|
+
await sendAuthMessage(
|
|
219
|
+
resolved.channel,
|
|
220
|
+
resolved.accountId,
|
|
221
|
+
resolved.to || userId,
|
|
222
|
+
'✅ 二次认证成功,请重新发送之前的命令(或回复"确认")即可执行。',
|
|
223
|
+
userId,
|
|
224
|
+
resolved.sessionKey,
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return undefined;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
api.logger.info(`[cclawd-mfa-auth] User ${userId} is NOT verified for sensitive ops.`);
|
|
232
|
+
|
|
233
|
+
// 生成认证会话
|
|
234
|
+
const session = await authManager.generateSession(userId, {
|
|
235
|
+
sessionKey: resolved.sessionKey,
|
|
236
|
+
senderId: userId,
|
|
237
|
+
commandBody: sensitiveCheck.preview,
|
|
238
|
+
channel: resolved.channel,
|
|
239
|
+
to: resolved.to,
|
|
240
|
+
accountId: resolved.accountId,
|
|
241
|
+
toolName,
|
|
242
|
+
toolParams: params || {},
|
|
243
|
+
timestamp: Date.now(),
|
|
244
|
+
triggerType: 'sensitive_operation',
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
if (!session) {
|
|
248
|
+
api.logger.error(`[cclawd-mfa-auth] Failed to generate session for user ${userId}`);
|
|
249
|
+
return undefined;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
api.logger.info(`[cclawd-mfa-auth] Blocking sensitive tool call: ${toolName} from ${userId}`);
|
|
253
|
+
|
|
254
|
+
// 发送认证链接
|
|
255
|
+
const messageText = `🔐 该操作需要二次认证
|
|
256
|
+
|
|
257
|
+
检测到敏感操作: ${sensitiveCheck.preview}
|
|
258
|
+
|
|
259
|
+
📱 请点击以下链接完成扫码认证:
|
|
260
|
+
${session.qrCodeUrl}
|
|
261
|
+
|
|
262
|
+
验证有效期: ${Math.floor(config.timeout / 60000)} 分钟
|
|
263
|
+
|
|
264
|
+
验证成功后,请回复"确认"或者重新发送之前的命令以继续执行。`;
|
|
265
|
+
|
|
266
|
+
try {
|
|
267
|
+
await sendAuthMessage(
|
|
268
|
+
resolved.channel,
|
|
269
|
+
resolved.accountId,
|
|
270
|
+
resolved.to || userId,
|
|
271
|
+
messageText,
|
|
272
|
+
userId,
|
|
273
|
+
resolved.sessionKey,
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
// 启动轮询
|
|
277
|
+
pollingManager.startPolling(api, userId, session.sessionId, {
|
|
278
|
+
triggerType: 'sensitive_operation',
|
|
279
|
+
isReauth: false,
|
|
280
|
+
channel: resolved.channel,
|
|
281
|
+
accountId: resolved.accountId,
|
|
282
|
+
to: resolved.to,
|
|
283
|
+
sessionKey: resolved.sessionKey,
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
api.logger.info(`[cclawd-mfa-auth] Sent sensitive operation auth notification`);
|
|
287
|
+
} catch (error) {
|
|
288
|
+
api.logger.error(`[cclawd-mfa-auth] Failed to send sensitive operation auth notification: ${String(error)}`);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// 注册待执行操作
|
|
292
|
+
authManager.registerPendingExecution(userId, session.sessionId);
|
|
293
|
+
|
|
294
|
+
return {
|
|
295
|
+
block: true,
|
|
296
|
+
blockReason: `🔐 该操作需要二次认证`,
|
|
297
|
+
};
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* 注册 /reauth 命令
|
|
302
|
+
*/
|
|
303
|
+
api.registerCommand({
|
|
304
|
+
name: 'reauth',
|
|
305
|
+
description: '重新进行首次对话认证',
|
|
306
|
+
acceptsArgs: false,
|
|
307
|
+
requireAuth: false,
|
|
308
|
+
handler: async (ctx) => {
|
|
309
|
+
const userId = ctx.from || ctx.senderId || 'unknown';
|
|
310
|
+
api.logger.info(`[cclawd-mfa-auth] /reauth command received. userId=${userId}`);
|
|
311
|
+
|
|
312
|
+
// 清除首次消息认证状态
|
|
313
|
+
authManager.clearFirstMessageAuth(userId);
|
|
314
|
+
|
|
315
|
+
// 解析会话信息
|
|
316
|
+
const resolved = SessionResolver.resolveFromContext({
|
|
317
|
+
sessionKey: ctx.sessionKey,
|
|
318
|
+
channelId: ctx.channel,
|
|
319
|
+
accountId: ctx.accountId,
|
|
320
|
+
from: ctx.from,
|
|
321
|
+
senderId: ctx.senderId,
|
|
322
|
+
to: ctx.to,
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
// 生成认证会话
|
|
326
|
+
const session = await authManager.generateSession(userId, {
|
|
327
|
+
sessionKey: resolved.sessionKey,
|
|
328
|
+
senderId: userId,
|
|
329
|
+
commandBody: '/reauth',
|
|
330
|
+
channel: resolved.channel,
|
|
331
|
+
to: resolved.to,
|
|
332
|
+
accountId: resolved.accountId,
|
|
333
|
+
toolName: '',
|
|
334
|
+
toolParams: {},
|
|
335
|
+
timestamp: Date.now(),
|
|
336
|
+
triggerType: 'first_message',
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
if (!session) {
|
|
340
|
+
api.logger.error(`[cclawd-mfa-auth] Failed to generate reauth session for user ${userId}`);
|
|
341
|
+
return { text: '❌ 认证会话创建失败,请稍后重试。' };
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
api.logger.info(`[cclawd-mfa-auth] Reauth requested by user ${userId}, session=${session.sessionId}`);
|
|
345
|
+
|
|
346
|
+
const messageText = `🔐 重新认证
|
|
347
|
+
|
|
348
|
+
📱 请点击以下链接完成扫码认证:
|
|
349
|
+
${session.qrCodeUrl}
|
|
350
|
+
|
|
351
|
+
验证有效期: ${Math.floor(config.timeout / 60000)} 分钟`;
|
|
352
|
+
|
|
353
|
+
try {
|
|
354
|
+
await sendAuthMessage(
|
|
355
|
+
resolved.channel,
|
|
356
|
+
resolved.accountId,
|
|
357
|
+
resolved.to || userId,
|
|
358
|
+
messageText,
|
|
359
|
+
userId,
|
|
360
|
+
resolved.sessionKey,
|
|
361
|
+
);
|
|
362
|
+
|
|
363
|
+
// 启动轮询
|
|
364
|
+
pollingManager.startPolling(api, userId, session.sessionId, {
|
|
365
|
+
triggerType: 'first_message',
|
|
366
|
+
isReauth: true,
|
|
367
|
+
channel: resolved.channel,
|
|
368
|
+
accountId: resolved.accountId,
|
|
369
|
+
to: resolved.to,
|
|
370
|
+
sessionKey: resolved.sessionKey,
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
return { text: '📱 认证链接已发送,请查看最新消息。' };
|
|
374
|
+
} catch (error) {
|
|
375
|
+
api.logger.error(`[cclawd-mfa-auth] Failed to send reauth link: ${String(error)}`);
|
|
376
|
+
return { text: '❌ 认证链接发送失败,请稍后重试。' };
|
|
377
|
+
}
|
|
378
|
+
},
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
api.logger.info('[cclawd-mfa-auth] Plugin loaded successfully');
|
|
382
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "cclawd-mfa-auth",
|
|
3
|
+
"name": "Cclawd MFA Auth",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"description": "MFA authentication plugin for Cclawd with multi-channel support",
|
|
6
|
+
"main": "index.ts",
|
|
7
|
+
"hooks": ["before_tool_call"],
|
|
8
|
+
"author": "",
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"keywords": [
|
|
11
|
+
"mfa",
|
|
12
|
+
"authentication",
|
|
13
|
+
"security"
|
|
14
|
+
],
|
|
15
|
+
"config": {
|
|
16
|
+
"env": [
|
|
17
|
+
"MFA_AUTH_API_KEY",
|
|
18
|
+
"DABBY_API_BASE_URL",
|
|
19
|
+
"MFA_VERIFICATION_DURATION",
|
|
20
|
+
"MFA_FIRST_MESSAGE_AUTH_DURATION",
|
|
21
|
+
"MFA_REQUIRE_AUTH_ON_FIRST_MESSAGE",
|
|
22
|
+
"MFA_REQUIRE_AUTH_ON_SENSITIVE_OPERATION",
|
|
23
|
+
"MFA_SENSITIVE_KEYWORDS",
|
|
24
|
+
"MFA_GATEWAY_HOST",
|
|
25
|
+
"MFA_ENABLE_AUTH_NOTIFICATION",
|
|
26
|
+
"MFA_AUTH_STATE_DIR"
|
|
27
|
+
]
|
|
28
|
+
},
|
|
29
|
+
"configSchema": {
|
|
30
|
+
"type": "object",
|
|
31
|
+
"additionalProperties": false,
|
|
32
|
+
"properties": {}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "cclawd-mfa-auth",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "MFA authentication plugin for Cclawd with multi-channel support",
|
|
6
|
+
"main": "index.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"dev": "tsx watch index.ts",
|
|
9
|
+
"build": "tsc"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"@larksuiteoapi/node-sdk": "^1.59.0"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"@types/node": "^20.0.0",
|
|
16
|
+
"tsx": "^4.21.0",
|
|
17
|
+
"typescript": "^5.0.0"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"openclaw",
|
|
21
|
+
"mfa",
|
|
22
|
+
"authentication",
|
|
23
|
+
"plugin"
|
|
24
|
+
],
|
|
25
|
+
"author": "",
|
|
26
|
+
"license": "MIT"
|
|
27
|
+
}
|