13 KiB
13 KiB
IC Coder 认证系统实现文档
概述
本文档详细说明了 IC Coder 插件如何集成 VSCode Authentication API,实现用户登录功能,并在 VSCode 左下角账户区域显示登录状态。
架构设计
核心组件
- ICCoderAuthenticationProvider - 认证提供者
- VSCode Authentication API - VSCode 官方认证接口
- 本地 HTTP 服务器 - 处理登录回调
- ICViewProvider - 侧边栏视图(根据登录状态显示不同按钮)
工作流程
用户点击登录
↓
调用 vscode.authentication.getSession()
↓
ICCoderAuthenticationProvider.createSession()
↓
启动本地 HTTP 服务器(动态端口)
↓
打开浏览器访问登录页面
↓
用户在网站完成登录
↓
网站重定向到 http://localhost:{port}/callback?token=xxx
↓
本地服务器接收 token
↓
创建 AuthenticationSession
↓
VSCode 左下角显示账户信息
详细实现
1. Authentication Provider 实现
文件:src/services/icCoderAuthProvider.ts
1.1 类定义
export class ICCoderAuthenticationProvider
implements vscode.AuthenticationProvider
{
private _onDidChangeSessions =
new vscode.EventEmitter<vscode.AuthenticationProviderAuthenticationSessionsChangeEvent>();
public readonly onDidChangeSessions = this._onDidChangeSessions.event;
private _sessions: vscode.AuthenticationSession[] = [];
}
关键点:
- 实现
vscode.AuthenticationProvider接口 - 使用
EventEmitter通知会话变化 - 在内存中维护会话列表
1.2 核心方法
getSessions() - 获取会话列表
async getSessions(scopes?: readonly string[]): Promise<readonly vscode.AuthenticationSession[]> {
return this._sessions;
}
createSession() - 创建会话(登录)
async createSession(scopes: readonly string[]): Promise<vscode.AuthenticationSession> {
const token = await this.login();
const session: vscode.AuthenticationSession = {
id: this.generateSessionId(),
accessToken: token,
account: {
id: "iccoder-user",
label: "IC Coder 用户",
},
scopes: [...scopes],
};
this._sessions.push(session);
await this.saveSessions();
this._onDidChangeSessions.fire({
added: [session],
removed: [],
changed: [],
});
return session;
}
关键点:
- 调用
login()方法获取 token - 创建
AuthenticationSession对象 - 保存到
globalState - 触发
onDidChangeSessions事件通知 VSCode
removeSession() - 删除会话(登出)
async removeSession(sessionId: string): Promise<void> {
const sessionIndex = this._sessions.findIndex((s) => s.id === sessionId);
if (sessionIndex > -1) {
const session = this._sessions[sessionIndex];
this._sessions.splice(sessionIndex, 1);
await this.saveSessions();
this._onDidChangeSessions.fire({
added: [],
removed: [session],
changed: [],
});
}
}
2. 本地 HTTP 服务器实现
2.1 动态端口分配
server.listen(0, () => {
const address = server.address();
const port = typeof address === "object" && address ? address.port : 3000;
resolve({ server, port });
});
关键点:
- 使用端口
0让系统自动分配可用端口 - 避免端口冲突问题
- 支持多个用户同时使用
2.2 回调处理
if (url.pathname === "/callback") {
const token = url.searchParams.get("token");
if (token) {
// 返回成功页面
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
res.end(this.getSuccessPage(iconBase64));
// 关闭服务器
server.close();
// 返回 token
if ((server as any)._loginResolve) {
(server as any)._loginResolve(token);
}
}
}
3. package.json 配置
3.1 注册 Authentication Provider
{
"contributes": {
"authentication": [
{
"id": "iccoder",
"label": "IC Coder"
}
]
}
}
关键点:
id必须与代码中使用的 ID 一致label会显示在 VSCode 账户菜单中
3.2 注册命令
{
"contributes": {
"commands": [
{
"command": "ic-coder.login",
"title": "IC Coder: 登录账户",
"category": "IC Coder"
},
{
"command": "ic-coder.logout",
"title": "IC Coder: 退出登录",
"category": "IC Coder"
}
]
}
}
4. extension.ts 注册
4.1 注册 Authentication Provider
export function activate(context: vscode.ExtensionContext) {
// 注册 Authentication Provider
const authProvider = new ICCoderAuthenticationProvider(context);
context.subscriptions.push(
vscode.authentication.registerAuthenticationProvider(
"iccoder",
"IC Coder",
authProvider
)
);
}
4.2 登录命令
const loginCommand = vscode.commands.registerCommand(
"ic-coder.login",
async () => {
try {
await vscode.authentication.getSession("iccoder", [], {
createIfNone: true,
});
} catch (error) {
vscode.window.showErrorMessage(`登录失败: ${error}`);
}
}
);
关键点:
createIfNone: true会在没有会话时自动调用createSession()- VSCode 会自动处理 UI 交互
4.3 登出命令
const logoutCommand = vscode.commands.registerCommand(
"ic-coder.logout",
async () => {
try {
const session = await vscode.authentication.getSession("iccoder", [], {
createIfNone: false,
});
if (session) {
await vscode.authentication.getSession("iccoder", [], {
clearSessionPreference: true,
forceNewSession: true,
});
vscode.window.showInformationMessage("已退出登录");
}
} catch (error) {
vscode.window.showInformationMessage("当前未登录");
}
}
);
5. ICViewProvider 集成
5.1 检查登录状态
private async checkLoginStatus(): Promise<boolean> {
try {
const session = await vscode.authentication.getSession("iccoder", [], { createIfNone: false });
return !!session;
} catch (error) {
return false;
}
}
5.2 根据登录状态显示不同按钮
resolveWebviewView(webviewView: vscode.WebviewView) {
this.checkLoginStatus().then((isLoggedIn) => {
webviewView.webview.html = this.getWebviewContent(
webviewView.webview,
isLoggedIn
);
});
}
${isLoggedIn
? '<button class="btn" onclick="openChat()">开始创作</button>'
: '<button class="btn" onclick="login()">登录账户</button>'
}
6. 网站前端配置
6.1 检测插件登录请求
// 在登录页面检测 redirect_uri 参数
const urlParams = new URLSearchParams(window.location.search);
const redirectUri = urlParams.get("redirect_uri");
if (redirectUri) {
// 保存回调地址
localStorage.setItem("plugin_redirect_uri", redirectUri);
}
6.2 登录成功后重定向
// 用户登录成功,拿到 token
const token = response.data.token;
// 检查是否需要重定向回插件
const redirectUri = localStorage.getItem("plugin_redirect_uri");
if (redirectUri) {
// 重定向回插件,带上 token
window.location.href = `${redirectUri}?token=${token}`;
localStorage.removeItem("plugin_redirect_uri");
} else {
// 正常登录流程
router.push("/dashboard");
}
关键技术点
1. 动态端口分配
问题: 固定端口可能被占用,导致登录失败
解决方案: 使用端口 0 让系统自动分配可用端口
server.listen(0, () => {
const address = server.address();
const port = typeof address === "object" && address ? address.port : 3000;
});
2. Promise 异步等待
问题: 需要等待浏览器登录完成后才能继续
解决方案: 使用 Promise 包装回调逻辑
return new Promise((resolve, reject) => {
(server as any)._loginResolve = resolve;
(server as any)._loginReject = reject;
});
3. 会话持久化
问题: 重启 VSCode 后需要重新登录
解决方案: 使用 globalState 保存会话
await this.context.globalState.update("icCoderSessions", this._sessions);
4. 事件通知机制
问题: VSCode 需要知道会话状态变化
解决方案: 使用 EventEmitter 触发事件
this._onDidChangeSessions.fire({
added: [session],
removed: [],
changed: [],
});
用户体验
登录流程
- 用户点击侧边栏"登录账户"按钮
- 浏览器自动打开登录页面
- 用户在网站完成登录
- 浏览器自动跳转到成功页面
- VSCode 左下角显示"IC Coder 用户"
- 侧边栏按钮变为"开始创作"
登出流程
- 点击 VSCode 左下角账户图标
- 选择"IC Coder"账户
- 点击"退出"按钮
- 或使用命令
IC Coder: 退出登录
常见问题
Q1: 为什么不直接使用 globalState 存储 token?
A: 使用 VSCode Authentication API 的优势:
- ✅ 统一的用户体验(左下角账户区域)
- ✅ VSCode 自动管理会话生命周期
- ✅ 支持多账户切换
- ✅ 更好的安全性(VSCode 负责加密存储)
Q2: 如何处理 token 过期?
A: 可以在 API 请求失败时:
- 检测 401 错误
- 调用
removeSession()清除过期会话 - 提示用户重新登录
Q3: 如何支持多个账户?
A: 修改 account 对象:
account: {
id: userInfo.id,
label: userInfo.username,
}
Q4: 登录页面如何获取用户信息?
A: 可以在登录成功后,通过 API 获取用户信息:
const userInfo = await fetch("https://api.iccoder.com/user/info", {
headers: { Authorization: `Bearer ${token}` },
});
const session: vscode.AuthenticationSession = {
account: {
id: userInfo.id,
label: userInfo.username,
},
// ...
};
安全考虑
1. Token 存储
- ✅ 使用 VSCode
globalState加密存储 - ✅ 不在代码中硬编码敏感信息
- ✅ Token 仅在内存和加密存储中传递
2. 本地服务器
- ✅ 仅监听
localhost,不暴露到外网 - ✅ 使用动态端口,避免固定端口被劫持
- ✅ 接收到 token 后立即关闭服务器
- ✅ 设置 5 分钟超时,防止服务器长期运行
3. HTTPS 考虑
当前实现: 使用 HTTP 本地回调
生产环境建议:
- 网站使用 HTTPS
- 本地回调使用 HTTP(localhost 不受浏览器限制)
- 或使用
vscode://协议(需要网站支持)
测试指南
1. 本地测试
# 启动调试模式
按 F5
# 测试登录
1. 打开侧边栏
2. 点击"登录账户"
3. 在浏览器完成登录
4. 检查左下角是否显示账户
# 测试登出
1. 点击左下角账户
2. 选择"IC Coder"
3. 点击"退出"
2. 调试技巧
// 在 ICCoderAuthenticationProvider 中添加日志
console.log("🔐 创建会话:", session);
console.log("🔑 Token:", token);
// 在 ICViewProvider 中添加日志
console.log("🔍 登录状态:", isLoggedIn);
3. 常见错误排查
| 错误 | 原因 | 解决方案 |
|---|---|---|
getSessions is not a function |
VSCode 版本过低 | 升级到 1.63.0+ |
| 端口被占用 | 固定端口冲突 | 使用动态端口(已实现) |
| 登录后未显示账户 | 未触发事件 | 检查 _onDidChangeSessions.fire() |
| 重启后需要重新登录 | 未保存会话 | 检查 saveSessions() 调用 |
文件结构
ic-coder/
├── src/
│ ├── services/
│ │ └── icCoderAuthProvider.ts # Authentication Provider 实现
│ ├── views/
│ │ └── ICViewProvider.ts # 侧边栏视图(集成登录状态)
│ └── extension.ts # 注册 Provider 和命令
├── package.json # 配置 authentication 和 commands
└── docs/
└── authentication-implementation.md # 本文档
参考资料
总结
本实现通过以下步骤完成了 VSCode Authentication API 的集成:
- ✅ 创建
ICCoderAuthenticationProvider类实现认证逻辑 - ✅ 在
package.json中注册 authentication provider - ✅ 在
extension.ts中注册 provider 和命令 - ✅ 实现本地 HTTP 服务器处理登录回调
- ✅ 使用动态端口避免冲突
- ✅ 集成到侧边栏视图,根据登录状态显示不同按钮
- ✅ 配置网站前端支持插件登录重定向
最终效果:
- 用户登录后,VSCode 左下角显示"IC Coder 用户"
- 侧边栏根据登录状态显示"登录账户"或"开始创作"按钮
- 支持通过账户菜单或命令进行登录/登出操作
文档版本: 1.0
最后更新: 2025-12-29
作者: Roe-xin