ClassworksKVAdmin/src/composables/useOAuthCallback.js
2025-11-16 16:16:58 +08:00

142 lines
4.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import {onMounted, onUnmounted} from 'vue'
import {useRoute, useRouter} from 'vue-router'
import {useAccountStore} from '@/stores/account'
import {toast} from 'vue-sonner'
/**
* 处理OAuth回调
* 检查URL参数中是否有OAuth回调信息
*/
export function useOAuthCallback() {
const route = useRoute()
const router = useRouter()
const accountStore = useAccountStore()
const handleOAuthCallback = async () => {
const {token, provider, color, success, error} = route.query
// 新版参数access_token / refresh_token / expires_in / providerName / providerColor
const access_token = route.query.access_token || token
const refresh_token = route.query.refresh_token || null
const providerName = route.query.providerName || provider
const providerColor = route.query.providerColor || color
// 检查是否是OAuth回调
if (!success && !error) {
return
}
// 处理成功回调
if (success === 'true' && access_token) {
try {
// 保存到 Store同时写入 localStorage
accountStore.setTokens(access_token, refresh_token)
if (providerName) localStorage.setItem('auth_provider', providerName)
if (providerColor) localStorage.setItem('auth_provider_color', providerColor)
// 登录到store加载资料、设备
await accountStore.login(access_token)
// 显示成功提示
toast.success('登录成功', {
description: `已通过 ${provider} 登录`,
})
// 清除URL参数
router.replace({query: {}})
// 触发storage事件通知其他窗口
window.dispatchEvent(new StorageEvent('storage', {
key: 'auth_token',
newValue: access_token,
url: window.location.href
}))
if (refresh_token) {
window.dispatchEvent(new StorageEvent('storage', {
key: 'auth_refresh_token',
newValue: refresh_token,
url: window.location.href
}))
}
// 如果是在新窗口中打开的OAuth回调自动关闭窗口
if (window.opener) {
// 通知父窗口登录成功
window.opener.postMessage({
type: 'oauth_success',
token: access_token,
provider: providerName,
}, window.location.origin)
// 延迟关闭窗口,确保消息已发送
setTimeout(() => {
window.close()
}, 1000)
}
} catch (err) {
toast.error('登录失败', {
description: err.message || '处理登录信息时出错'
})
}
}
// 处理错误回调
if (success === 'false' || error) {
const errorMessages = {
'invalid_state': 'State验证失败可能存在安全风险',
'access_denied': '用户拒绝了授权请求',
'temporarily_unavailable': '服务暂时不可用,请稍后重试'
}
const errorMsg = errorMessages[error] || error || '登录过程中出现错误'
toast.error('登录失败', {
description: errorMsg
})
// 清除URL参数
router.replace({query: {}})
// 如果是在新窗口中打开的OAuth回调自动关闭窗口
if (window.opener) {
// 通知父窗口登录失败
window.opener.postMessage({
type: 'oauth_error',
error: errorMsg
}, window.location.origin)
// 延迟关闭窗口
setTimeout(() => {
window.close()
}, 1000)
}
}
}
onMounted(() => {
handleOAuthCallback()
})
// 监听storage事件处理其他标签页的登录
const handleStorageChange = (e) => {
if (e.key === 'auth_token' && e.newValue) {
// 其他标签页已登录,刷新当前页面的状态
accountStore.login(e.newValue)
}
if (e.key === 'auth_refresh_token' && e.newValue) {
accountStore.setTokens(accountStore.token, e.newValue)
}
}
onMounted(() => {
window.addEventListener('storage', handleStorageChange)
})
onUnmounted(() => {
window.removeEventListener('storage', handleStorageChange)
})
return {
handleOAuthCallback
}
}