oauth
(cherry picked from commit 8933b95e6148670f60ca6c7bd7568d9f19774cc8)
This commit is contained in:
@@ -4,6 +4,18 @@ export function oauthLinuxDoLogin(code) {
|
||||
return http.post('/oauth/linuxDo/login',{code})
|
||||
}
|
||||
|
||||
export function oauthGithubLogin(code) {
|
||||
return http.post('/oauth/github/login',{code})
|
||||
}
|
||||
|
||||
export function oauthGitlabLogin(code) {
|
||||
return http.post('/oauth/gitlab/login',{code})
|
||||
}
|
||||
|
||||
export function oauthGoogleLogin(code) {
|
||||
return http.post('/oauth/google/login',{code})
|
||||
}
|
||||
|
||||
export function oauthBindUser(form) {
|
||||
return http.put('/oauth/bindUser', form)
|
||||
}
|
||||
|
||||
@@ -59,6 +59,26 @@ const routes = [
|
||||
name: 'login',
|
||||
component: () => import('@/views/login/index.vue')
|
||||
},
|
||||
{
|
||||
path: '/login/github',
|
||||
name: 'loginGithub',
|
||||
component: () => import('@/views/login/index.vue')
|
||||
},
|
||||
{
|
||||
path: '/login/gitlab',
|
||||
name: 'loginGitlab',
|
||||
component: () => import('@/views/login/index.vue')
|
||||
},
|
||||
{
|
||||
path: '/login/google',
|
||||
name: 'loginGoogle',
|
||||
component: () => import('@/views/login/index.vue')
|
||||
},
|
||||
{
|
||||
path: '/login/linuxdo',
|
||||
name: 'loginLinuxdo',
|
||||
component: () => import('@/views/login/index.vue')
|
||||
},
|
||||
{
|
||||
path: '/test',
|
||||
name: 'test',
|
||||
@@ -100,16 +120,16 @@ router.beforeEach((to, from, next) => {
|
||||
|
||||
const token = localStorage.getItem('token')
|
||||
|
||||
if (!token && to.name !== 'login') {
|
||||
if (!token && !to.path.startsWith('/login')) {
|
||||
return next({name: 'login'})
|
||||
}
|
||||
|
||||
if (!token && to.name === 'login') {
|
||||
if (!token && to.path.startsWith('/login')) {
|
||||
loadBackground(next)
|
||||
return
|
||||
}
|
||||
|
||||
if (token && to.name === 'login') {
|
||||
if (token && to.path.startsWith('/login')) {
|
||||
return next(from.path)
|
||||
}
|
||||
|
||||
|
||||
@@ -44,8 +44,10 @@
|
||||
<el-button class="btn" type="primary" @click="submit" :loading="loginLoading"
|
||||
>{{ $t('loginBtn') }}
|
||||
</el-button>
|
||||
<el-button class="btn" v-if="settingStore.settings.linuxdoSwitch" style="margin-top: 10px" @click="linuxDoLogin">
|
||||
<el-avatar src="/image/linuxdo.webp" :size="18" style="margin-right: 10px" />LinuxDo
|
||||
<el-button v-for="p in oauthProviders" :key="p.key" class="btn" style="margin-top: 10px" @click="oauthLogin(p.key)">
|
||||
<el-avatar v-if="p.iconType === 'image'" :src="p.icon" :size="18" style="margin-right: 10px" />
|
||||
<Icon v-else :icon="p.icon" width="18" height="18" style="margin-right: 10px" />
|
||||
{{ p.label }}
|
||||
</el-button>
|
||||
</div>
|
||||
<div v-show="show !== 'login'">
|
||||
@@ -94,8 +96,10 @@
|
||||
<el-button class="btn" style="margin: 0" type="primary" @click="submitRegister" :loading="registerLoading"
|
||||
>{{ $t('regBtn') }}
|
||||
</el-button>
|
||||
<el-button v-if="settingStore.settings.linuxdoSwitch" class="btn" style="margin-top: 10px" @click="linuxDoLogin">
|
||||
<el-avatar src="/image/linuxdo.webp" :size="18" style="margin-right: 10px" />LinuxDo
|
||||
<el-button v-for="p in oauthProviders" :key="p.key" class="btn" style="margin-top: 10px" @click="oauthLogin(p.key)">
|
||||
<el-avatar v-if="p.iconType === 'image'" :src="p.icon" :size="18" style="margin-right: 10px" />
|
||||
<Icon v-else :icon="p.icon" width="18" height="18" style="margin-right: 10px" />
|
||||
{{ p.label }}
|
||||
</el-button>
|
||||
</div>
|
||||
<template v-if="settingStore.settings.register === 0">
|
||||
@@ -148,6 +152,7 @@
|
||||
|
||||
<script setup>
|
||||
import router from "@/router";
|
||||
import {useRoute} from "vue-router";
|
||||
import {computed, nextTick, reactive, ref} from "vue";
|
||||
import {login} from "@/request/login.js";
|
||||
import {register} from "@/request/login.js";
|
||||
@@ -162,19 +167,35 @@ import {cvtR2Url} from "@/utils/convert.js";
|
||||
import {loginUserInfo} from "@/request/my.js";
|
||||
import {permsToRouter} from "@/perm/perm.js";
|
||||
import {useI18n} from "vue-i18n";
|
||||
import {oauthBindUser, oauthLinuxDoLogin} from "@/request/ouath.js";
|
||||
import {oauthBindUser, oauthLinuxDoLogin, oauthGithubLogin, oauthGitlabLogin, oauthGoogleLogin} from "@/request/ouath.js";
|
||||
|
||||
const {t} = useI18n();
|
||||
const accountStore = useAccountStore();
|
||||
const userStore = useUserStore();
|
||||
const uiStore = useUiStore();
|
||||
const settingStore = useSettingStore();
|
||||
const route = useRoute();
|
||||
const loginLoading = ref(false)
|
||||
const bindLoading = ref(false)
|
||||
const oauthLoading = ref(false);
|
||||
const showBindForm = ref(false);
|
||||
const show = ref('login')
|
||||
|
||||
const oauthProvider = computed(() => {
|
||||
const match = route.path.match(/^\/login\/(.+)/)
|
||||
return match ? match[1] : null
|
||||
})
|
||||
|
||||
const oauthProviders = computed(() => {
|
||||
const allProviders = [
|
||||
{ key: 'linuxdo', label: 'LinuxDo', icon: '/image/linuxdo.webp', iconType: 'image' },
|
||||
{ key: 'github', label: 'GitHub', icon: 'mingcute:github-fill', iconType: 'iconify' },
|
||||
{ key: 'gitlab', label: 'GitLab', icon: 'mingcute:gitlab-fill', iconType: 'iconify' },
|
||||
{ key: 'google', label: 'Google', icon: 'mingcute:google-fill', iconType: 'iconify' },
|
||||
]
|
||||
return allProviders.filter(p => settingStore.settings[p.key + 'Switch'])
|
||||
})
|
||||
|
||||
const bindForm = reactive({
|
||||
email: '',
|
||||
oauthUserId: '',
|
||||
@@ -261,16 +282,30 @@ const getEmailName = (email) => {
|
||||
return email.split('@')[0]
|
||||
}
|
||||
|
||||
function linuxDoLogin() {
|
||||
const clientId = settingStore.settings.linuxdoClientId
|
||||
const redirectUri = encodeURIComponent(settingStore.settings.linuxdoCallbackUrl)
|
||||
window.location.href =
|
||||
`https://connect.linux.do/oauth2/authorize?client_id=${clientId}&redirect_uri=${redirectUri}&response_type=code&scope=openid+profile+email`
|
||||
function oauthLogin(provider) {
|
||||
const clientId = settingStore.settings[provider + 'ClientId']
|
||||
const redirectUri = encodeURIComponent(window.location.origin + '/login/' + provider)
|
||||
const authorizeUrls = {
|
||||
linuxdo: `https://connect.linux.do/oauth2/authorize?client_id=${clientId}&redirect_uri=${redirectUri}&response_type=code&scope=openid+profile+email`,
|
||||
github: `https://github.com/login/oauth/authorize?client_id=${clientId}&redirect_uri=${redirectUri}&scope=user:email`,
|
||||
gitlab: `https://gitlab.com/oauth/authorize?client_id=${clientId}&redirect_uri=${redirectUri}&response_type=code&scope=read_user`,
|
||||
google: `https://accounts.google.com/o/oauth2/v2/auth?client_id=${clientId}&redirect_uri=${redirectUri}&response_type=code&scope=openid+profile+email`,
|
||||
}
|
||||
window.location.href = authorizeUrls[provider]
|
||||
}
|
||||
|
||||
linuxDoGetUser();
|
||||
const loginFns = {
|
||||
linuxdo: oauthLinuxDoLogin,
|
||||
github: oauthGithubLogin,
|
||||
gitlab: oauthGitlabLogin,
|
||||
google: oauthGoogleLogin,
|
||||
}
|
||||
|
||||
async function linuxDoGetUser() {
|
||||
oauthGetUser();
|
||||
|
||||
async function oauthGetUser() {
|
||||
|
||||
if (!oauthProvider.value) return
|
||||
|
||||
const params = new URLSearchParams(window.location.search)
|
||||
const code = params.get('code')
|
||||
@@ -278,7 +313,7 @@ async function linuxDoGetUser() {
|
||||
if (code) {
|
||||
|
||||
oauthLoading.value = true
|
||||
oauthLinuxDoLogin(code).then(data => {
|
||||
loginFns[oauthProvider.value](code).then(data => {
|
||||
|
||||
bindForm.oauthUserId = data.userInfo.oauthUserId;
|
||||
|
||||
|
||||
@@ -7,6 +7,21 @@ app.post('/oauth/linuxDo/login', async (c) => {
|
||||
return c.json(result.ok(loginInfo))
|
||||
});
|
||||
|
||||
app.post('/oauth/github/login', async (c) => {
|
||||
const loginInfo = await oauthService.githubLogin(c, await c.req.json());
|
||||
return c.json(result.ok(loginInfo))
|
||||
});
|
||||
|
||||
app.post('/oauth/gitlab/login', async (c) => {
|
||||
const loginInfo = await oauthService.gitlabLogin(c, await c.req.json());
|
||||
return c.json(result.ok(loginInfo))
|
||||
});
|
||||
|
||||
app.post('/oauth/google/login', async (c) => {
|
||||
const loginInfo = await oauthService.googleLogin(c, await c.req.json());
|
||||
return c.json(result.ok(loginInfo))
|
||||
});
|
||||
|
||||
app.put('/oauth/bindUser', async (c) => {
|
||||
const loginInfo = await oauthService.bindUser(c, await c.req.json());
|
||||
return c.json(result.ok(loginInfo))
|
||||
|
||||
@@ -11,6 +11,7 @@ export const oauth = sqliteTable('oauth', {
|
||||
trustLevel: integer('trust_level'),
|
||||
silenced: integer('silenced'),
|
||||
createTime: text('create_time').default(sql`CURRENT_TIMESTAMP`).notNull(),
|
||||
platform: text('platform'),
|
||||
userId: integer('user_id').default(0).notNull()
|
||||
});
|
||||
|
||||
|
||||
@@ -34,9 +34,6 @@ const oauthService = {
|
||||
|
||||
const { code } = params;
|
||||
|
||||
let token = '';
|
||||
let userInfo = {}
|
||||
|
||||
const reqParams = new URLSearchParams()
|
||||
reqParams.append('client_id', c.env.linuxdo_client_id)
|
||||
reqParams.append('client_secret', c.env.linuxdo_client_secret)
|
||||
@@ -54,7 +51,7 @@ const oauthService = {
|
||||
throw new BizError(tokenRes.statusText)
|
||||
}
|
||||
|
||||
token = await tokenRes.json()
|
||||
const token = await tokenRes.json()
|
||||
|
||||
const userRes = await fetch('https://connect.linux.do/api/user', {
|
||||
headers: {
|
||||
@@ -66,23 +63,166 @@ const oauthService = {
|
||||
throw new BizError(userRes.statusText)
|
||||
}
|
||||
|
||||
userInfo = await userRes.json();
|
||||
const userInfo = await userRes.json();
|
||||
|
||||
userInfo.oauthUserId = String(userInfo.id);
|
||||
userInfo.active = userInfo.active ? 0 : 1;
|
||||
userInfo.silenced = userInfo.silenced ? 0 : 1;
|
||||
userInfo.trustLevel = userInfo.trust_level;
|
||||
userInfo.avatar = userInfo.avatar_url;
|
||||
userInfo.platform = 'linuxdo';
|
||||
|
||||
const oauthRow = await this.saveUser(c, userInfo);
|
||||
return await this.saveAndLogin(c, userInfo)
|
||||
},
|
||||
|
||||
async githubLogin(c, params) {
|
||||
|
||||
const { code } = params;
|
||||
|
||||
const tokenRes = await fetch("https://github.com/login/oauth/access_token", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Accept": "application/json"
|
||||
},
|
||||
body: JSON.stringify({
|
||||
client_id: c.env.github_client_id,
|
||||
client_secret: c.env.github_client_secret,
|
||||
code: code,
|
||||
redirect_uri: c.env.github_callback_url
|
||||
})
|
||||
});
|
||||
|
||||
if (!tokenRes.ok) {
|
||||
throw new BizError(tokenRes.statusText);
|
||||
}
|
||||
|
||||
const token = await tokenRes.json();
|
||||
|
||||
if (token.error) {
|
||||
throw new BizError(token.error_description || token.error);
|
||||
}
|
||||
|
||||
const userRes = await fetch('https://api.github.com/user', {
|
||||
headers: {
|
||||
Authorization: 'Bearer ' + token.access_token,
|
||||
'User-Agent': 'cloud-mail'
|
||||
}
|
||||
});
|
||||
|
||||
if (!userRes.ok) {
|
||||
throw new BizError(userRes.statusText);
|
||||
}
|
||||
|
||||
const userInfo = await userRes.json();
|
||||
|
||||
userInfo.oauthUserId = String(userInfo.id);
|
||||
userInfo.username = userInfo.login;
|
||||
userInfo.avatar = userInfo.avatar_url;
|
||||
userInfo.platform = 'github';
|
||||
|
||||
return await this.saveAndLogin(c, userInfo);
|
||||
},
|
||||
|
||||
async gitlabLogin(c, params) {
|
||||
|
||||
const { code } = params;
|
||||
|
||||
const reqParams = new URLSearchParams()
|
||||
reqParams.append('client_id', c.env.gitlab_client_id)
|
||||
reqParams.append('client_secret', c.env.gitlab_client_secret)
|
||||
reqParams.append('code', code)
|
||||
reqParams.append('redirect_uri', c.env.gitlab_callback_url)
|
||||
reqParams.append('grant_type', 'authorization_code')
|
||||
|
||||
const tokenRes = await fetch("https://gitlab.com/oauth/token", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
||||
body: reqParams.toString()
|
||||
});
|
||||
|
||||
if (!tokenRes.ok) {
|
||||
throw new BizError(tokenRes.statusText);
|
||||
}
|
||||
|
||||
const token = await tokenRes.json();
|
||||
|
||||
const userRes = await fetch('https://gitlab.com/api/v4/user', {
|
||||
headers: {
|
||||
Authorization: 'Bearer ' + token.access_token
|
||||
}
|
||||
});
|
||||
|
||||
if (!userRes.ok) {
|
||||
throw new BizError(userRes.statusText);
|
||||
}
|
||||
|
||||
const userInfo = await userRes.json();
|
||||
|
||||
userInfo.oauthUserId = String(userInfo.id);
|
||||
userInfo.username = userInfo.username;
|
||||
userInfo.name = userInfo.name;
|
||||
userInfo.avatar = userInfo.avatar_url;
|
||||
userInfo.platform = 'gitlab';
|
||||
|
||||
return await this.saveAndLogin(c, userInfo);
|
||||
},
|
||||
|
||||
async googleLogin(c, params) {
|
||||
|
||||
const { code } = params;
|
||||
|
||||
const reqParams = new URLSearchParams()
|
||||
reqParams.append('client_id', c.env.google_client_id)
|
||||
reqParams.append('client_secret', c.env.google_client_secret)
|
||||
reqParams.append('code', code)
|
||||
reqParams.append('redirect_uri', c.env.google_callback_url)
|
||||
reqParams.append('grant_type', 'authorization_code')
|
||||
|
||||
const tokenRes = await fetch("https://oauth2.googleapis.com/token", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
||||
body: reqParams.toString()
|
||||
});
|
||||
|
||||
if (!tokenRes.ok) {
|
||||
throw new BizError(tokenRes.statusText);
|
||||
}
|
||||
|
||||
const token = await tokenRes.json();
|
||||
|
||||
const userRes = await fetch('https://openidconnect.googleapis.com/v1/userinfo', {
|
||||
headers: {
|
||||
Authorization: 'Bearer ' + token.access_token
|
||||
}
|
||||
});
|
||||
|
||||
if (!userRes.ok) {
|
||||
throw new BizError(userRes.statusText);
|
||||
}
|
||||
|
||||
const userInfo = await userRes.json();
|
||||
|
||||
userInfo.oauthUserId = String(userInfo.sub);
|
||||
userInfo.username = userInfo.email;
|
||||
userInfo.name = userInfo.name;
|
||||
userInfo.avatar = userInfo.picture;
|
||||
userInfo.platform = 'google';
|
||||
|
||||
return await this.saveAndLogin(c, userInfo);
|
||||
},
|
||||
|
||||
async saveAndLogin(c, userInfo) {
|
||||
|
||||
const oauthRow = await this.saveUser(c, userInfo);
|
||||
const userRow = await userService.selectByIdIncludeDel(c, oauthRow.userId);
|
||||
|
||||
if (!userRow) {
|
||||
return { userInfo: oauthRow, token: null }
|
||||
return { userInfo: oauthRow, token: null };
|
||||
}
|
||||
|
||||
const JwtToken = await loginService.login(c, { email: userRow.email, password: null }, true);
|
||||
return { userInfo: oauthRow, token: JwtToken }
|
||||
return { userInfo: oauthRow, token: JwtToken };
|
||||
},
|
||||
|
||||
async saveUser(c, userInfo) {
|
||||
|
||||
@@ -74,6 +74,18 @@ const settingService = {
|
||||
setting.linuxdoCallbackUrl = c.env.linuxdo_callback_url;
|
||||
setting.linuxdoSwitch = linuxdoSwitch;
|
||||
|
||||
setting.githubClientId = c.env.github_client_id;
|
||||
setting.githubCallbackUrl = c.env.github_callback_url;
|
||||
setting.githubSwitch = this.parseBoolean(c.env.github_switch);
|
||||
|
||||
setting.gitlabClientId = c.env.gitlab_client_id;
|
||||
setting.gitlabCallbackUrl = c.env.gitlab_callback_url;
|
||||
setting.gitlabSwitch = this.parseBoolean(c.env.gitlab_switch);
|
||||
|
||||
setting.googleClientId = c.env.google_client_id;
|
||||
setting.googleCallbackUrl = c.env.google_callback_url;
|
||||
setting.googleSwitch = this.parseBoolean(c.env.google_switch);
|
||||
|
||||
setting.emailPrefixFilter = setting.emailPrefixFilter.split(",").filter(Boolean);
|
||||
|
||||
c.set?.('setting', setting);
|
||||
@@ -231,11 +243,26 @@ const settingService = {
|
||||
linuxdoClientId: settingRow.linuxdoClientId,
|
||||
linuxdoCallbackUrl: settingRow.linuxdoCallbackUrl,
|
||||
linuxdoSwitch: settingRow.linuxdoSwitch,
|
||||
githubClientId: settingRow.githubClientId,
|
||||
githubCallbackUrl: settingRow.githubCallbackUrl,
|
||||
githubSwitch: settingRow.githubSwitch,
|
||||
gitlabClientId: settingRow.gitlabClientId,
|
||||
gitlabCallbackUrl: settingRow.gitlabCallbackUrl,
|
||||
gitlabSwitch: settingRow.gitlabSwitch,
|
||||
googleClientId: settingRow.googleClientId,
|
||||
googleCallbackUrl: settingRow.googleCallbackUrl,
|
||||
googleSwitch: settingRow.googleSwitch,
|
||||
minEmailPrefix: settingRow.minEmailPrefix,
|
||||
projectLink: settingRow.projectLink
|
||||
};
|
||||
},
|
||||
|
||||
parseBoolean(value) {
|
||||
if (typeof value === 'string' && value === 'true') return true;
|
||||
if (value === true) return true;
|
||||
return false;
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
export default settingService;
|
||||
|
||||
Reference in New Issue
Block a user