@@ -20,6 +20,8 @@ import { Defaults } from "../shared/defaults.js";
2020import { Api } from "../shared/api.js" ;
2121import { Scenes } from "../shared/scenes.js" ;
2222import { Router } from "../shared/router.js" ;
23+ // 2026-04-10 SSO B2: Token 自动刷新
24+ import { Sso } from "../shared/sso.js" ;
2325
2426let _backgroundInitialized = false ;
2527
@@ -124,6 +126,78 @@ export function setupBackground() {
124126 }
125127 } ) ;
126128
129+ /* ─── SSO Token Refresh Alarm (B2) ─── */
130+ // 2026-04-10 新增 — SSO Plan v2 B2
131+ // 设计思路:
132+ // 1. 扩展安装/启动时检查 token 过期时间,设置 chrome.alarms 定时刷新
133+ // 2. Alarm 在 token 过期前 5 分钟触发(最小间隔 50 分钟)
134+ // 3. 刷新失败不阻塞正常功能(匿名模式仍可用)
135+ // 影响范围:background service-worker,不影响 popup/content
136+ // 潜在风险:service worker 被浏览器休眠后 alarm 可能延迟,但 401 重试兜底
137+
138+ const SSO_REFRESH_ALARM = "ep-sso-token-refresh" ;
139+ const REFRESH_BUFFER_MS = 5 * 60 * 1000 ; // 提前 5 分钟刷新
140+ const MIN_ALARM_INTERVAL_MIN = 50 ; // 最小定时间隔(分钟)
141+
142+ /**
143+ * 根据 token 过期时间调度下次刷新 alarm
144+ * 无 token 时清除 alarm
145+ */
146+ async function scheduleTokenRefresh ( ) {
147+ const expiresAt = await Sso . getExpiresAt ( ) ;
148+ if ( ! expiresAt ) {
149+ // 无 token,清除 alarm
150+ chrome . alarms . clear ( SSO_REFRESH_ALARM ) ;
151+ return ;
152+ }
153+
154+ const now = Date . now ( ) ;
155+ const refreshAt = expiresAt - REFRESH_BUFFER_MS ;
156+ const delayMs = Math . max ( refreshAt - now , 60 * 1000 ) ; // 至少 1 分钟后
157+ const delayMin = delayMs / 60000 ;
158+
159+ // 设置一次性 alarm + 周期性兜底(防止单次 alarm 丢失)
160+ chrome . alarms . create ( SSO_REFRESH_ALARM , {
161+ delayInMinutes : delayMin ,
162+ periodInMinutes : MIN_ALARM_INTERVAL_MIN ,
163+ } ) ;
164+ }
165+
166+ // 安装/启动时调度
167+ chrome . runtime . onInstalled . addListener ( ( ) => scheduleTokenRefresh ( ) ) ;
168+ chrome . runtime . onStartup . addListener ( ( ) => scheduleTokenRefresh ( ) ) ;
169+
170+ // Alarm 触发时刷新 token
171+ chrome . alarms . onAlarm . addListener ( async ( alarm ) => {
172+ if ( alarm . name !== SSO_REFRESH_ALARM ) return ;
173+
174+ const token = await Sso . getAccessToken ( ) ;
175+ if ( ! token ) {
176+ // 用户已退出,清除 alarm
177+ chrome . alarms . clear ( SSO_REFRESH_ALARM ) ;
178+ return ;
179+ }
180+
181+ try {
182+ await Sso . refreshAccessToken ( ) ;
183+ // 刷新成功,重新调度下次刷新
184+ await scheduleTokenRefresh ( ) ;
185+ console . log ( "[EP] SSO token refreshed successfully" ) ;
186+ } catch ( err ) {
187+ // 刷新失败(refresh token 过期等),清除 alarm
188+ // 用户下次请求会收到 401,需重新登录
189+ console . warn ( "[EP] SSO token refresh failed:" , err . message ) ;
190+ chrome . alarms . clear ( SSO_REFRESH_ALARM ) ;
191+ }
192+ } ) ;
193+
194+ // 监听 storage 变化:登录/退出时重新调度
195+ chrome . storage . onChanged . addListener ( ( changes ) => {
196+ if ( changes [ Sso . SSO_KEYS . EXPIRES_AT ] ) {
197+ scheduleTokenRefresh ( ) ;
198+ }
199+ } ) ;
200+
127201 /* ─── Inline Enhance (AI 聊天网站原地增强) ─── */
128202
129203 /**
0 commit comments