mirror of
https://github.com/certd/certd.git
synced 2026-04-26 05:37:25 +08:00
perf: 支持OIDC单点登录
This commit is contained in:
@@ -84,4 +84,6 @@ export default {
|
|||||||
phoneNumber: "Phone Number",
|
phoneNumber: "Phone Number",
|
||||||
changePassword: "Change Password",
|
changePassword: "Change Password",
|
||||||
updateProfile: "Update Profile",
|
updateProfile: "Update Profile",
|
||||||
|
oauthLoginTitle: "Other ways of login",
|
||||||
|
oauthOnlyLoginTitle: "Login",
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -743,6 +743,8 @@ export default {
|
|||||||
paymentSetting: "Payment Settings",
|
paymentSetting: "Payment Settings",
|
||||||
captchaSetting: "Captcha Setting",
|
captchaSetting: "Captcha Setting",
|
||||||
pipelineSetting: "Pipeline Settings",
|
pipelineSetting: "Pipeline Settings",
|
||||||
|
oauthSetting: "OAuth2 Settings",
|
||||||
|
|
||||||
showRunStrategy: "Show RunStrategy",
|
showRunStrategy: "Show RunStrategy",
|
||||||
showRunStrategyHelper: "Allow modify the run strategy of the task",
|
showRunStrategyHelper: "Allow modify the run strategy of the task",
|
||||||
|
|
||||||
@@ -770,6 +772,14 @@ export default {
|
|||||||
oauthCallback: "Callback URL",
|
oauthCallback: "Callback URL",
|
||||||
oauthCallbackHelper: "Copy this URL to the callback address of the OAuth2 login provider",
|
oauthCallbackHelper: "Copy this URL to the callback address of the OAuth2 login provider",
|
||||||
oauthCallbackCopy: "Copy Callback URL",
|
oauthCallbackCopy: "Copy Callback URL",
|
||||||
|
oauthAutoRegister: "Auto Register User",
|
||||||
|
oauthAutoRegisterCheckedText: "Auto Register",
|
||||||
|
oauthAutoRegisterUnCheckedText: "User Select",
|
||||||
|
oauthAutoRegisterHelper: "Whether to auto register user when login",
|
||||||
|
oauthAutoRedirect: "Auto Redirect to OAuth2 Login",
|
||||||
|
oauthAutoRedirectHelper: "Whether to auto redirect to OAuth2 login when login (using the first enabled OAuth2 login type)",
|
||||||
|
oauthOnly: "OAuth2 Login Only",
|
||||||
|
oauthOnlyHelper: "Whether to only allow OAuth2 login, disable password login",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
modal: {
|
modal: {
|
||||||
|
|||||||
@@ -85,4 +85,7 @@ export default {
|
|||||||
phoneNumber: "手机号",
|
phoneNumber: "手机号",
|
||||||
changePassword: "修改密码",
|
changePassword: "修改密码",
|
||||||
updateProfile: "修改个人信息",
|
updateProfile: "修改个人信息",
|
||||||
|
|
||||||
|
oauthLoginTitle: "其他登录方式",
|
||||||
|
oauthOnlyLoginTitle: "登录",
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -743,6 +743,7 @@ export default {
|
|||||||
paymentSetting: "支付设置",
|
paymentSetting: "支付设置",
|
||||||
captchaSetting: "验证码设置",
|
captchaSetting: "验证码设置",
|
||||||
pipelineSetting: "流水线设置",
|
pipelineSetting: "流水线设置",
|
||||||
|
oauthSetting: "第三方登录",
|
||||||
|
|
||||||
showRunStrategy: "显示运行策略选择",
|
showRunStrategy: "显示运行策略选择",
|
||||||
showRunStrategyHelper: "任务设置中是否允许选择运行策略",
|
showRunStrategyHelper: "任务设置中是否允许选择运行策略",
|
||||||
@@ -771,6 +772,14 @@ export default {
|
|||||||
oauthCallback: "回调地址",
|
oauthCallback: "回调地址",
|
||||||
oauthCallbackHelper: "复制回调地址,配置到对应提供商的回调地址中",
|
oauthCallbackHelper: "复制回调地址,配置到对应提供商的回调地址中",
|
||||||
oauthCallbackCopy: "复制回调地址",
|
oauthCallbackCopy: "复制回调地址",
|
||||||
|
oauthAutoRegister: "自动注册用户",
|
||||||
|
oauthAutoRegisterHelper: "当第三方账户未绑定本站账号时,是否自动注册用户,默认由用户选择",
|
||||||
|
oauthAutoRegisterCheckedText: "自动注册",
|
||||||
|
oauthAutoRegisterUnCheckedText: "用户选择",
|
||||||
|
oauthAutoRedirect: "自动跳转第三方登录",
|
||||||
|
oauthAutoRedirectHelper: "是否自动跳转第三方登录(使用第一个已启用的第三方登录类型)",
|
||||||
|
oauthOnly: "仅使用第三方登录",
|
||||||
|
oauthOnlyHelper: "是否仅使用第三方登录,关闭密码登录(注意:请务必在测试第三方登录功能正常后再开启)",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
modal: {
|
modal: {
|
||||||
|
|||||||
@@ -62,6 +62,13 @@ export type SysPublicSetting = {
|
|||||||
|
|
||||||
// 第三方OAuth配置
|
// 第三方OAuth配置
|
||||||
oauthEnabled?: boolean;
|
oauthEnabled?: boolean;
|
||||||
|
// 是否自动注册用户
|
||||||
|
oauthAutoRegister?: boolean;
|
||||||
|
// 是否自动跳转第三方登录
|
||||||
|
oauthAutoRedirect?: boolean;
|
||||||
|
// 是否仅允许使用第三方登录
|
||||||
|
oauthOnly?: boolean;
|
||||||
|
// 第三方OAuth登录提供者配置
|
||||||
oauthProviders?: Record<
|
oauthProviders?: Record<
|
||||||
string,
|
string,
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -100,3 +100,10 @@ export async function loginByTwoFactor(data: any) {
|
|||||||
data,
|
data,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function OauthProviders() {
|
||||||
|
return await request({
|
||||||
|
url: "/oauth/providers",
|
||||||
|
method: "post",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import { mitter } from "/src/utils/util.mitt";
|
|||||||
import { resetAllStores, useAccessStore } from "/@/vben/stores";
|
import { resetAllStores, useAccessStore } from "/@/vben/stores";
|
||||||
|
|
||||||
import { useUserStore as vbenUserStore } from "/@/vben/stores/modules/user";
|
import { useUserStore as vbenUserStore } from "/@/vben/stores/modules/user";
|
||||||
|
import { request } from "/@/api/service";
|
||||||
|
|
||||||
interface UserState {
|
interface UserState {
|
||||||
userInfo: Nullable<UserInfoRes>;
|
userInfo: Nullable<UserInfoRes>;
|
||||||
@@ -116,15 +117,38 @@ export const useUserStore = defineStore({
|
|||||||
* @description: logout
|
* @description: logout
|
||||||
*/
|
*/
|
||||||
async logout(goLogin = true, from401 = false) {
|
async logout(goLogin = true, from401 = false) {
|
||||||
|
if (!from401 && this.getToken) {
|
||||||
|
try {
|
||||||
|
await UserApi.logout(); //主要是清空cookie
|
||||||
|
} catch (e) {
|
||||||
|
console.error("注销登录请求失败:", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.resetState();
|
this.resetState();
|
||||||
resetAllStores();
|
resetAllStores();
|
||||||
if (!from401) {
|
// 第三方登录注销
|
||||||
await UserApi.logout(); //主要是清空cookie
|
await this.oauthLogout();
|
||||||
}
|
|
||||||
goLogin && router.push("/login");
|
goLogin && router.push("/login");
|
||||||
mitter.emit("app.logout");
|
mitter.emit("app.logout");
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async oauthLogout() {
|
||||||
|
const providers = await UserApi.OauthProviders();
|
||||||
|
for (const provider of providers) {
|
||||||
|
if (provider.logoutUrl) {
|
||||||
|
try {
|
||||||
|
await request({
|
||||||
|
url: provider.logoutUrl,
|
||||||
|
method: "get",
|
||||||
|
withCredentials: true,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.error("注销第三方登录失败:", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
/**
|
/**
|
||||||
* @description: Confirm before logging out
|
* @description: Confirm before logging out
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -16,10 +16,10 @@
|
|||||||
<a-descriptions-item :label="t('authentication.email')">{{ userInfo.email }}</a-descriptions-item>
|
<a-descriptions-item :label="t('authentication.email')">{{ userInfo.email }}</a-descriptions-item>
|
||||||
<a-descriptions-item :label="t('authentication.phoneNumber')">{{ userInfo.phoneCode }}{{ userInfo.mobile }}</a-descriptions-item>
|
<a-descriptions-item :label="t('authentication.phoneNumber')">{{ userInfo.phoneCode }}{{ userInfo.mobile }}</a-descriptions-item>
|
||||||
<a-descriptions-item v-if="settingStore.sysPublic.oauthEnabled && settingStore.isPlus" label="第三方账号绑定">
|
<a-descriptions-item v-if="settingStore.sysPublic.oauthEnabled && settingStore.isPlus" label="第三方账号绑定">
|
||||||
<div v-for="item in computedOauthBounds" :key="item.name" class="flex items-center gap-2">
|
<div v-for="item in computedOauthBounds" :key="item.name" class="flex items-center gap-2 mb-2">
|
||||||
<fs-icon :icon="item.icon" class="mr-2 text-blue-500" />
|
<fs-icon :icon="item.icon" class="mr-2 text-blue-500" />
|
||||||
<span class="mr-2 w-36">{{ item.title }}</span>
|
<span class="mr-2 w-36">{{ item.title }}</span>
|
||||||
<a-button v-if="item.bound" type="link" danger @click="unbind(item.name)">解绑</a-button>
|
<a-button v-if="item.bound" type="primary" danger @click="unbind(item.name)">解绑</a-button>
|
||||||
<a-button v-else type="primary" @click="bind(item.name)">绑定</a-button>
|
<a-button v-else type="primary" @click="bind(item.name)">绑定</a-button>
|
||||||
</div>
|
</div>
|
||||||
</a-descriptions-item>
|
</a-descriptions-item>
|
||||||
|
|||||||
@@ -2,71 +2,74 @@
|
|||||||
<div class="main login-page">
|
<div class="main login-page">
|
||||||
<a-form v-if="!twoFactor.loginId" ref="formRef" class="user-layout-login" name="custom-validation" :model="formState" v-bind="layout" @finish="handleFinish" @finish-failed="handleFinishFailed">
|
<a-form v-if="!twoFactor.loginId" ref="formRef" class="user-layout-login" name="custom-validation" :model="formState" v-bind="layout" @finish="handleFinish" @finish-failed="handleFinishFailed">
|
||||||
<!-- <div class="login-title">登录</div>-->
|
<!-- <div class="login-title">登录</div>-->
|
||||||
<a-tabs v-model:active-key="formState.loginType" :tab-bar-style="{ textAlign: 'center', borderBottom: 'unset' }">
|
<template v-if="!isOauthOnly">
|
||||||
<a-tab-pane key="password" :tab="t('authentication.passwordTab')" :disabled="sysPublicSettings.passwordLoginEnabled !== true">
|
<a-tabs v-model:active-key="formState.loginType" :tab-bar-style="{ textAlign: 'center', borderBottom: 'unset' }">
|
||||||
<template v-if="formState.loginType === 'password'">
|
<a-tab-pane key="password" :tab="t('authentication.passwordTab')" :disabled="sysPublicSettings.passwordLoginEnabled !== true">
|
||||||
<!-- <div class="login-title">登录</div>-->
|
<template v-if="formState.loginType === 'password'">
|
||||||
<a-form-item required has-feedback name="username" :rules="rules.username">
|
<!-- <div class="login-title">登录</div>-->
|
||||||
<a-input v-model:value="formState.username" :placeholder="t('authentication.usernamePlaceholder')" autocomplete="off" @keydown.enter="handleFinish">
|
<a-form-item required has-feedback name="username" :rules="rules.username">
|
||||||
<template #prefix>
|
<a-input v-model:value="formState.username" :placeholder="t('authentication.usernamePlaceholder')" autocomplete="off" @keydown.enter="handleFinish">
|
||||||
<fs-icon icon="ion:phone-portrait-outline"></fs-icon>
|
<template #prefix>
|
||||||
</template>
|
<fs-icon icon="ion:phone-portrait-outline"></fs-icon>
|
||||||
</a-input>
|
</template>
|
||||||
</a-form-item>
|
</a-input>
|
||||||
<a-form-item has-feedback name="password" :rules="rules.password">
|
</a-form-item>
|
||||||
<a-input-password v-model:value="formState.password" :placeholder="t('authentication.passwordPlaceholder')" autocomplete="off" @keyup.enter="handleFinish">
|
<a-form-item has-feedback name="password" :rules="rules.password">
|
||||||
<template #prefix>
|
<a-input-password v-model:value="formState.password" :placeholder="t('authentication.passwordPlaceholder')" autocomplete="off" @keyup.enter="handleFinish">
|
||||||
<fs-icon icon="ion:lock-closed-outline"></fs-icon>
|
<template #prefix>
|
||||||
</template>
|
<fs-icon icon="ion:lock-closed-outline"></fs-icon>
|
||||||
</a-input-password>
|
</template>
|
||||||
</a-form-item>
|
</a-input-password>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
<a-form-item v-if="settingStore.sysPublic.captchaEnabled" has-feedback required name="captcha" :rules="rules.captcha">
|
<a-form-item v-if="settingStore.sysPublic.captchaEnabled" has-feedback required name="captcha" :rules="rules.captcha">
|
||||||
<CaptchaInput v-model:model-value="formState.captcha" @keydown.enter="handleFinish"></CaptchaInput>
|
<CaptchaInput v-model:model-value="formState.captcha" @keydown.enter="handleFinish"></CaptchaInput>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</template>
|
</template>
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
<a-tab-pane v-if="sysPublicSettings.smsLoginEnabled === true" key="sms" :tab="t('authentication.smsTab')">
|
<a-tab-pane v-if="sysPublicSettings.smsLoginEnabled === true" key="sms" :tab="t('authentication.smsTab')">
|
||||||
<template v-if="formState.loginType === 'sms'">
|
<template v-if="formState.loginType === 'sms'">
|
||||||
<a-form-item has-feedback name="mobile" :rules="rules.mobile">
|
<a-form-item has-feedback name="mobile" :rules="rules.mobile">
|
||||||
<a-input v-model:value="formState.mobile" :placeholder="t('authentication.mobilePlaceholder')" autocomplete="off">
|
<a-input v-model:value="formState.mobile" :placeholder="t('authentication.mobilePlaceholder')" autocomplete="off">
|
||||||
<template #prefix>
|
<template #prefix>
|
||||||
<fs-icon icon="ion:phone-portrait-outline"></fs-icon>
|
<fs-icon icon="ion:phone-portrait-outline"></fs-icon>
|
||||||
</template>
|
</template>
|
||||||
</a-input>
|
</a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
<a-form-item has-feedback name="smsCaptcha">
|
<a-form-item has-feedback name="smsCaptcha">
|
||||||
<CaptchaInput v-model:model-value="formState.smsCaptcha" @keydown.enter="handleFinish"></CaptchaInput>
|
<CaptchaInput v-model:model-value="formState.smsCaptcha" @keydown.enter="handleFinish"></CaptchaInput>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
<a-form-item name="smsCode" :rules="rules.smsCode">
|
<a-form-item name="smsCode" :rules="rules.smsCode">
|
||||||
<sms-code v-model:value="formState.smsCode" :captcha="formState.smsCaptcha" :mobile="formState.mobile" :phone-code="formState.phoneCode" @error="formState.smsCaptcha = null" />
|
<sms-code v-model:value="formState.smsCode" :captcha="formState.smsCaptcha" :mobile="formState.mobile" :phone-code="formState.phoneCode" @error="formState.smsCaptcha = null" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</template>
|
</template>
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
</a-tabs>
|
</a-tabs>
|
||||||
<a-form-item>
|
<a-form-item>
|
||||||
<a-button type="primary" size="large" html-type="button" :loading="loading" class="login-button" @click="handleFinish">
|
<a-button type="primary" size="large" html-type="button" :loading="loading" class="login-button" @click="handleFinish">
|
||||||
{{ queryBindCode ? t("authentication.bindButton") : t("authentication.loginButton") }}
|
{{ queryBindCode ? t("authentication.bindButton") : t("authentication.loginButton") }}
|
||||||
</a-button>
|
</a-button>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item>
|
||||||
|
<div class="mt-2 flex justify-between items-center">
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<language-toggle class="text-blue-500"></language-toggle>
|
||||||
|
<router-link v-if="!!settingStore.sysPublic.selfServicePasswordRetrievalEnabled && !queryBindCode" :to="{ name: 'forgotPassword' }">
|
||||||
|
{{ t("authentication.forgotPassword") }}
|
||||||
|
</router-link>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="mt-2 flex justify-between items-center">
|
<router-link v-if="hasRegisterTypeEnabled() && !queryBindCode" class="register" :to="{ name: 'register' }">
|
||||||
<div class="flex items-center gap-2">
|
{{ t("authentication.registerLink") }}
|
||||||
<language-toggle class="text-blue-500"></language-toggle>
|
|
||||||
<router-link v-if="!!settingStore.sysPublic.selfServicePasswordRetrievalEnabled && !queryBindCode" :to="{ name: 'forgotPassword' }">
|
|
||||||
{{ t("authentication.forgotPassword") }}
|
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
|
</a-form-item>
|
||||||
<router-link v-if="hasRegisterTypeEnabled() && !queryBindCode" class="register" :to="{ name: 'register' }">
|
</template>
|
||||||
{{ t("authentication.registerLink") }}
|
|
||||||
</router-link>
|
|
||||||
</div>
|
|
||||||
</a-form-item>
|
|
||||||
|
|
||||||
<div v-if="!queryBindCode && settingStore.sysPublic.oauthEnabled && settingStore.isPlus" class="w-full">
|
<div v-if="!queryBindCode && settingStore.sysPublic.oauthEnabled && settingStore.isPlus" class="w-full">
|
||||||
<oauth-footer></oauth-footer>
|
<oauth-footer :oauth-only="isOauthOnly"></oauth-footer>
|
||||||
</div>
|
</div>
|
||||||
</a-form>
|
</a-form>
|
||||||
<a-form v-else ref="twoFactorFormRef" class="user-layout-login" :model="twoFactor" v-bind="layout">
|
<a-form v-else ref="twoFactorFormRef" class="user-layout-login" :model="twoFactor" v-bind="layout">
|
||||||
@@ -89,7 +92,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, nextTick, reactive, ref, toRaw } from "vue";
|
import { computed, defineComponent, nextTick, reactive, ref, toRaw } from "vue";
|
||||||
import { useUserStore } from "/src/store/user";
|
import { useUserStore } from "/src/store/user";
|
||||||
import { useSettingStore } from "/@/store/settings";
|
import { useSettingStore } from "/@/store/settings";
|
||||||
import { utils } from "@fast-crud/fast-crud";
|
import { utils } from "@fast-crud/fast-crud";
|
||||||
@@ -110,6 +113,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
const queryBindCode = ref(route.query.bindCode as string | undefined);
|
const queryBindCode = ref(route.query.bindCode as string | undefined);
|
||||||
|
|
||||||
|
const queryOauthOnly = route.query.oauthOnly as string;
|
||||||
const urlLoginType = route.query.loginType as string | undefined;
|
const urlLoginType = route.query.loginType as string | undefined;
|
||||||
const verifyCodeInputRef = ref();
|
const verifyCodeInputRef = ref();
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
@@ -231,6 +235,12 @@ export default defineComponent({
|
|||||||
const captchaInputRef = ref();
|
const captchaInputRef = ref();
|
||||||
const captchaInputForSmsCode = ref();
|
const captchaInputForSmsCode = ref();
|
||||||
|
|
||||||
|
const isOauthOnly = computed(() => {
|
||||||
|
if (queryOauthOnly === "false" || queryOauthOnly === "0") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return sysPublicSettings.oauthOnly && settingStore.isPlus && sysPublicSettings.oauthEnabled;
|
||||||
|
});
|
||||||
return {
|
return {
|
||||||
t,
|
t,
|
||||||
loading,
|
loading,
|
||||||
@@ -238,6 +248,7 @@ export default defineComponent({
|
|||||||
formRef,
|
formRef,
|
||||||
rules,
|
rules,
|
||||||
layout,
|
layout,
|
||||||
|
isOauthOnly,
|
||||||
handleFinishFailed,
|
handleFinishFailed,
|
||||||
handleFinish,
|
handleFinish,
|
||||||
resetForm,
|
resetForm,
|
||||||
@@ -266,6 +277,11 @@ export default defineComponent({
|
|||||||
// font-size: 14px;
|
// font-size: 14px;
|
||||||
//}
|
//}
|
||||||
|
|
||||||
|
.fs-icon {
|
||||||
|
color: rgba(0, 0, 0, 0.45);
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
.login-title {
|
.login-title {
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@@ -319,11 +335,6 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.fs-icon {
|
|
||||||
color: rgba(0, 0, 0, 0.45);
|
|
||||||
margin-right: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-input-affix-wrapper {
|
.ant-input-affix-wrapper {
|
||||||
line-height: 1.8 !important;
|
line-height: 1.8 !important;
|
||||||
font-size: 14px !important;
|
font-size: 14px !important;
|
||||||
|
|||||||
@@ -53,8 +53,13 @@ async function handleOauthToken() {
|
|||||||
}
|
}
|
||||||
if (res.bindRequired) {
|
if (res.bindRequired) {
|
||||||
//需要绑定
|
//需要绑定
|
||||||
bindRequired.value = true;
|
|
||||||
bindCode.value = res.validationCode;
|
bindCode.value = res.validationCode;
|
||||||
|
//如果开启了自动注册,默认自动注册账号
|
||||||
|
if (settingStore.sysPublic.registerEnabled) {
|
||||||
|
autoRegister();
|
||||||
|
} else {
|
||||||
|
bindRequired.value = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,7 +69,7 @@ onMounted(async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (forType === "bind") {
|
if (forType === "bind") {
|
||||||
//绑定第三方账号
|
//从用户中心页面,进行第三方账号的绑定
|
||||||
await api.BindUser(validationCode);
|
await api.BindUser(validationCode);
|
||||||
notification.success({
|
notification.success({
|
||||||
message: "绑定成功",
|
message: "绑定成功",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="oauth-footer relative">
|
<div class="oauth-footer relative">
|
||||||
<div class="oauth-title">
|
<div class="oauth-title">
|
||||||
<div class="oauth-title-text">其他方式登录</div>
|
<div class="oauth-title-text">{{ computedTitle }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-center items-center gap-4">
|
<div class="flex justify-center items-center gap-4">
|
||||||
<template v-for="item in oauthProviderList" :key="item.type">
|
<template v-for="item in oauthProviderList" :key="item.type">
|
||||||
@@ -14,13 +14,31 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onMounted, ref } from "vue";
|
import { computed, onMounted, ref } from "vue";
|
||||||
import * as api from "./api";
|
import * as api from "./api";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
|
import { useSettingStore } from "/@/store/settings";
|
||||||
|
|
||||||
const oauthProviderList = ref([]);
|
const oauthProviderList = ref([]);
|
||||||
|
const props = defineProps<{
|
||||||
|
oauthOnly?: boolean;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const computedTitle = computed(() => {
|
||||||
|
return props.oauthOnly ? t("authentication.oauthOnlyLoginTitle") : t("authentication.oauthLoginTitle");
|
||||||
|
});
|
||||||
|
|
||||||
|
const settingStore = useSettingStore();
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
oauthProviderList.value = await api.GetOauthProviders();
|
oauthProviderList.value = await api.GetOauthProviders();
|
||||||
|
//如果开启了自动跳转登录
|
||||||
|
if (settingStore.sysPublic.oauthAutoRedirect) {
|
||||||
|
const firstOauth = oauthProviderList.value.find(item => item.addonId > 0);
|
||||||
|
if (firstOauth) {
|
||||||
|
goOauthLogin(firstOauth.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
async function goOauthLogin(type: string) {
|
async function goOauthLogin(type: string) {
|
||||||
@@ -82,6 +100,7 @@ async function goOauthLogin(type: string) {
|
|||||||
.fs-icon {
|
.fs-icon {
|
||||||
font-size: 36px;
|
font-size: 36px;
|
||||||
color: #006be6 !important;
|
color: #006be6 !important;
|
||||||
|
margin: 0px !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,9 @@
|
|||||||
<a-tab-pane key="register" :tab="t('certd.sys.setting.registerSetting')">
|
<a-tab-pane key="register" :tab="t('certd.sys.setting.registerSetting')">
|
||||||
<SettingRegister v-if="activeKey === 'register'" />
|
<SettingRegister v-if="activeKey === 'register'" />
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
|
<a-tab-pane key="oauth" :tab="t('certd.sys.setting.oauthSetting')">
|
||||||
|
<SettingOauth v-if="activeKey === 'oauth'" />
|
||||||
|
</a-tab-pane>
|
||||||
<a-tab-pane v-if="settingsStore.isComm" key="payment" :tab="t('certd.sys.setting.paymentSetting')">
|
<a-tab-pane v-if="settingsStore.isComm" key="payment" :tab="t('certd.sys.setting.paymentSetting')">
|
||||||
<SettingPayment v-if="activeKey === 'payment'" />
|
<SettingPayment v-if="activeKey === 'payment'" />
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
@@ -35,6 +38,7 @@ import SettingPayment from "/@/views/sys/settings/tabs/payment.vue";
|
|||||||
import SettingSafe from "/@/views/sys/settings/tabs/safe.vue";
|
import SettingSafe from "/@/views/sys/settings/tabs/safe.vue";
|
||||||
import SettingCaptcha from "/@/views/sys/settings/tabs/captcha.vue";
|
import SettingCaptcha from "/@/views/sys/settings/tabs/captcha.vue";
|
||||||
import SettingPipeline from "/@/views/sys/settings/tabs/pipeline.vue";
|
import SettingPipeline from "/@/views/sys/settings/tabs/pipeline.vue";
|
||||||
|
import SettingOauth from "/@/views/sys/settings/tabs/oauth.vue";
|
||||||
import { useRoute, useRouter } from "vue-router";
|
import { useRoute, useRouter } from "vue-router";
|
||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
import { useSettingStore } from "/@/store/settings";
|
import { useSettingStore } from "/@/store/settings";
|
||||||
@@ -47,9 +51,7 @@ const settingsStore = useSettingStore();
|
|||||||
const activeKey = ref("base");
|
const activeKey = ref("base");
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
if (route.query.tab) {
|
activeKey.value = (route.query.tab as string) || "base";
|
||||||
activeKey.value = (route.query.tab as string) || "base";
|
|
||||||
}
|
|
||||||
|
|
||||||
function onChange(value: string) {
|
function onChange(value: string) {
|
||||||
// activeKey.value = value;
|
// activeKey.value = value;
|
||||||
|
|||||||
@@ -0,0 +1,150 @@
|
|||||||
|
<template>
|
||||||
|
<div class="sys-settings-form sys-settings-oauth">
|
||||||
|
<a-form :model="formState" name="register" :label-col="{ span: 8 }" :wrapper-col="{ span: 16 }" autocomplete="off" @finish="onFinish">
|
||||||
|
<a-form-item :label="t('certd.sys.setting.enableOauth')" :name="['public', 'oauthEnabled']">
|
||||||
|
<div class="flex-o">
|
||||||
|
<a-switch v-model:checked="formState.public.oauthEnabled" :disabled="!settingsStore.isPlus" :title="t('certd.plusFeature')" />
|
||||||
|
<vip-button class="ml-5" mode="button"></vip-button>
|
||||||
|
</div>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item v-if="formState.public.oauthEnabled" :label="t('certd.sys.setting.oauthProviders')" :name="['public', 'oauthProviders']">
|
||||||
|
<div class="flex flex-wrap">
|
||||||
|
<table class="w-full table-auto border-collapse border border-gray-400">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="border border-gray-300 px-4 py-2 w-1/3">{{ t("certd.sys.setting.oauthType") }}</th>
|
||||||
|
<th class="border border-gray-300 px-4 py-2 w-1/3">{{ t("certd.sys.setting.oauthCallback") }}</th>
|
||||||
|
<th class="border border-gray-300 px-4 py-2 w-1/3">{{ t("certd.sys.setting.oauthConfig") }}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr v-for="(item, key) of oauthProviders" :key="key">
|
||||||
|
<td class="border border-gray-300 px-4 py-2">
|
||||||
|
<div class="flex items-center" :title="item.desc">
|
||||||
|
<fs-icon :icon="item.icon" class="mr-2 text-blue-600" />
|
||||||
|
{{ item.title }}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="border border-gray-300 px-4 py-2 overflow-ellipsis" :title="t('certd.sys.setting.oauthCallbackHelper')">
|
||||||
|
<fs-copyable :model-value="buildCallbackUrl(item.name)">
|
||||||
|
{{ t("certd.sys.setting.oauthCallbackCopy") }}
|
||||||
|
</fs-copyable>
|
||||||
|
</td>
|
||||||
|
<td class="border border-gray-300 px-4 py-2">
|
||||||
|
<AddonSelector v-model:model-value="item.addonId" addon-type="oauth" from="sys" :type="item.name" :placeholder="t('certd.sys.setting.oauthProviderSelectorPlaceholder')" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item v-if="formState.public.oauthEnabled" :label="t('certd.sys.setting.oauthOnly')" :name="['public', 'oauthOnly']">
|
||||||
|
<div class="flex-o">
|
||||||
|
<a-switch v-model:checked="formState.public.oauthOnly" :disabled="!settingsStore.isPlus" :title="t('certd.plusFeature')" />
|
||||||
|
</div>
|
||||||
|
<div class="helper">{{ t("certd.sys.setting.oauthOnlyHelper") }}</div>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item v-if="formState.public.oauthEnabled" :label="t('certd.sys.setting.oauthAutoRedirect')" :name="['public', 'oauthAutoRedirect']">
|
||||||
|
<div class="flex-o">
|
||||||
|
<a-switch v-model:checked="formState.public.oauthAutoRedirect" :disabled="!settingsStore.isPlus" :title="t('certd.plusFeature')" />
|
||||||
|
</div>
|
||||||
|
<div class="helper">{{ t("certd.sys.setting.oauthAutoRedirectHelper") }}</div>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item v-if="formState.public.oauthEnabled" :label="t('certd.sys.setting.oauthAutoRegister')" :name="['public', 'oauthAutoRegister']">
|
||||||
|
<div class="flex-o">
|
||||||
|
<a-switch
|
||||||
|
v-model:checked="formState.public.oauthAutoRegister"
|
||||||
|
:checked-children="t('certd.sys.setting.oauthAutoRegisterCheckedText')"
|
||||||
|
:un-checked-children="t('certd.sys.setting.oauthAutoRegisterUnCheckedText')"
|
||||||
|
:disabled="!settingsStore.isPlus"
|
||||||
|
:title="t('certd.plusFeature')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="helper">{{ t("certd.sys.setting.oauthAutoRegisterHelper") }}</div>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label=" " :colon="false" :wrapper-col="{ span: 16 }">
|
||||||
|
<a-button :loading="saveLoading" type="primary" html-type="submit">{{ t("certd.saveButton") }}</a-button>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="tsx">
|
||||||
|
import { notification } from "ant-design-vue";
|
||||||
|
import { merge } from "lodash-es";
|
||||||
|
import { reactive, ref, Ref } from "vue";
|
||||||
|
import AddonSelector from "../../../certd/addon/addon-selector/index.vue";
|
||||||
|
import { useSettingStore } from "/@/store/settings";
|
||||||
|
import * as api from "/@/views/sys/settings/api";
|
||||||
|
import { SysSettings } from "/@/views/sys/settings/api";
|
||||||
|
import { useI18n } from "/src/locales";
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: "SettingOauth",
|
||||||
|
});
|
||||||
|
|
||||||
|
const formState = reactive<Partial<SysSettings>>({
|
||||||
|
public: {},
|
||||||
|
private: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
const oauthProviders = ref([]);
|
||||||
|
async function loadOauthProviders() {
|
||||||
|
oauthProviders.value = await api.GetOauthProviders();
|
||||||
|
}
|
||||||
|
|
||||||
|
function fillOauthProviders(form: any) {
|
||||||
|
const providers: any = {};
|
||||||
|
for (const item of oauthProviders.value) {
|
||||||
|
const type = item.name;
|
||||||
|
providers[type] = {
|
||||||
|
type: type,
|
||||||
|
title: item.title,
|
||||||
|
icon: item.icon,
|
||||||
|
addonId: item.addonId || null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
form.public.oauthProviders = providers;
|
||||||
|
return providers;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadSysSettings() {
|
||||||
|
const data: any = await api.SysSettingsGet();
|
||||||
|
merge(formState, data);
|
||||||
|
|
||||||
|
await loadOauthProviders();
|
||||||
|
}
|
||||||
|
|
||||||
|
const saveLoading = ref(false);
|
||||||
|
loadSysSettings();
|
||||||
|
const settingsStore = useSettingStore();
|
||||||
|
const onFinish = async (form: any) => {
|
||||||
|
try {
|
||||||
|
saveLoading.value = true;
|
||||||
|
fillOauthProviders(form);
|
||||||
|
await api.SysSettingsSave(form);
|
||||||
|
await settingsStore.loadSysSettings();
|
||||||
|
notification.success({
|
||||||
|
message: t("certd.saveSuccess"),
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
saveLoading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function buildCallbackUrl(type: string) {
|
||||||
|
return `${window.location.origin}/api/oauth/callback/${type}`;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="less">
|
||||||
|
.sys-settings-oauth {
|
||||||
|
width: 1000px !important;
|
||||||
|
|
||||||
|
.addon-selector {
|
||||||
|
.inner {
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -55,45 +55,6 @@
|
|||||||
</a-form-item>
|
</a-form-item>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<a-form-item :label="t('certd.sys.setting.enableOauth')" :name="['public', 'oauthEnabled']">
|
|
||||||
<div class="flex-o">
|
|
||||||
<a-switch v-model:checked="formState.public.oauthEnabled" :disabled="!settingsStore.isPlus" :title="t('certd.plusFeature')" />
|
|
||||||
<vip-button class="ml-5" mode="button"></vip-button>
|
|
||||||
</div>
|
|
||||||
</a-form-item>
|
|
||||||
<a-form-item v-if="formState.public.oauthEnabled" :label="t('certd.sys.setting.oauthProviders')" :name="['public', 'oauthProviders']">
|
|
||||||
<div class="flex flex-wrap">
|
|
||||||
<table class="w-full table-auto border-collapse border border-gray-400">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th class="border border-gray-300 px-4 py-2 w-1/3">{{ t("certd.sys.setting.oauthType") }}</th>
|
|
||||||
<th class="border border-gray-300 px-4 py-2 w-1/3">{{ t("certd.sys.setting.oauthCallback") }}</th>
|
|
||||||
<th class="border border-gray-300 px-4 py-2 w-1/3">{{ t("certd.sys.setting.oauthConfig") }}</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr v-for="(item, key) of oauthProviders" :key="key">
|
|
||||||
<td class="border border-gray-300 px-4 py-2">
|
|
||||||
<div class="flex items-center" :title="item.desc">
|
|
||||||
<fs-icon :icon="item.icon" class="mr-2 text-blue-600" />
|
|
||||||
{{ item.title }}
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td class="border border-gray-300 px-4 py-2 overflow-ellipsis" :title="t('certd.sys.setting.oauthCallbackHelper')">
|
|
||||||
<fs-copyable :model-value="buildCallbackUrl(item.name)">
|
|
||||||
{{ t("certd.sys.setting.oauthCallbackCopy") }}
|
|
||||||
</fs-copyable>
|
|
||||||
</td>
|
|
||||||
<td class="border border-gray-300 px-4 py-2">
|
|
||||||
<AddonSelector v-model:model-value="item.addonId" addon-type="oauth" from="sys" :type="item.name" :placeholder="t('certd.sys.setting.oauthProviderSelectorPlaceholder')" />
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</a-form-item>
|
|
||||||
|
|
||||||
<a-form-item label=" " :colon="false" :wrapper-col="{ span: 16 }">
|
<a-form-item label=" " :colon="false" :wrapper-col="{ span: 16 }">
|
||||||
<a-button :loading="saveLoading" type="primary" html-type="submit">{{ t("certd.saveButton") }}</a-button>
|
<a-button :loading="saveLoading" type="primary" html-type="submit">{{ t("certd.saveButton") }}</a-button>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
@@ -105,7 +66,6 @@
|
|||||||
import { notification } from "ant-design-vue";
|
import { notification } from "ant-design-vue";
|
||||||
import { merge } from "lodash-es";
|
import { merge } from "lodash-es";
|
||||||
import { reactive, ref, Ref } from "vue";
|
import { reactive, ref, Ref } from "vue";
|
||||||
import AddonSelector from "../../../certd/addon/addon-selector/index.vue";
|
|
||||||
import { useSettingStore } from "/@/store/settings";
|
import { useSettingStore } from "/@/store/settings";
|
||||||
import * as api from "/@/views/sys/settings/api";
|
import * as api from "/@/views/sys/settings/api";
|
||||||
import { SysSettings } from "/@/views/sys/settings/api";
|
import { SysSettings } from "/@/views/sys/settings/api";
|
||||||
@@ -196,26 +156,6 @@ async function loadTypeDefine(type: string) {
|
|||||||
smsTypeDefineInputs.value = inputs;
|
smsTypeDefineInputs.value = inputs;
|
||||||
}
|
}
|
||||||
|
|
||||||
const oauthProviders = ref([]);
|
|
||||||
async function loadOauthProviders() {
|
|
||||||
oauthProviders.value = await api.GetOauthProviders();
|
|
||||||
}
|
|
||||||
|
|
||||||
function fillOauthProviders(form: any) {
|
|
||||||
const providers: any = {};
|
|
||||||
for (const item of oauthProviders.value) {
|
|
||||||
const type = item.name;
|
|
||||||
providers[type] = {
|
|
||||||
type: type,
|
|
||||||
title: item.title,
|
|
||||||
icon: item.icon,
|
|
||||||
addonId: item.addonId || null,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
form.public.oauthProviders = providers;
|
|
||||||
return providers;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function loadSysSettings() {
|
async function loadSysSettings() {
|
||||||
const data: any = await api.SysSettingsGet();
|
const data: any = await api.SysSettingsGet();
|
||||||
merge(formState, data);
|
merge(formState, data);
|
||||||
@@ -230,7 +170,6 @@ async function loadSysSettings() {
|
|||||||
if (!settingsStore.isComm) {
|
if (!settingsStore.isComm) {
|
||||||
formState.public.smsLoginEnabled = false;
|
formState.public.smsLoginEnabled = false;
|
||||||
}
|
}
|
||||||
await loadOauthProviders();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const saveLoading = ref(false);
|
const saveLoading = ref(false);
|
||||||
@@ -239,7 +178,6 @@ const settingsStore = useSettingStore();
|
|||||||
const onFinish = async (form: any) => {
|
const onFinish = async (form: any) => {
|
||||||
try {
|
try {
|
||||||
saveLoading.value = true;
|
saveLoading.value = true;
|
||||||
fillOauthProviders(form);
|
|
||||||
await api.SysSettingsSave(form);
|
await api.SysSettingsSave(form);
|
||||||
await settingsStore.loadSysSettings();
|
await settingsStore.loadSysSettings();
|
||||||
notification.success({
|
notification.success({
|
||||||
@@ -249,10 +187,6 @@ const onFinish = async (form: any) => {
|
|||||||
saveLoading.value = false;
|
saveLoading.value = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function buildCallbackUrl(type: string) {
|
|
||||||
return `${window.location.origin}/api/oauth/callback/${type}`;
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
<style lang="less">
|
<style lang="less">
|
||||||
.sys-settings-register {
|
.sys-settings-register {
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
import { addonRegistry, AddonService, BaseController, Constants, SysInstallInfo, SysSettingsService } from "@certd/lib-server";
|
|
||||||
import { ALL, Body, Controller, Get, Inject, Param, Post, Provide, Query } from "@midwayjs/core";
|
|
||||||
import { AddonGetterService } from "../../../modules/pipeline/service/addon-getter-service.js";
|
|
||||||
import { IOauthProvider } from "../../../plugins/plugin-oauth/api.js";
|
|
||||||
import { LoginService } from "../../../modules/login/service/login-service.js";
|
|
||||||
import { CodeService } from "../../../modules/basic/service/code-service.js";
|
|
||||||
import { UserService } from "../../../modules/sys/authority/service/user-service.js";
|
|
||||||
import { UserEntity } from "../../../modules/sys/authority/entity/user.js";
|
|
||||||
import { logger, simpleNanoId, utils } from "@certd/basic";
|
import { logger, simpleNanoId, utils } from "@certd/basic";
|
||||||
import { OauthBoundService } from "../../../modules/login/service/oauth-bound-service.js";
|
import { addonRegistry, AddonService, BaseController, Constants, SysInstallInfo, SysSettingsService } from "@certd/lib-server";
|
||||||
import { OauthBoundEntity } from "../../../modules/login/entity/oauth-bound.js";
|
|
||||||
import { checkPlus } from "@certd/plus-core";
|
import { checkPlus } from "@certd/plus-core";
|
||||||
|
import { ALL, Body, Controller, Get, Inject, Param, Post, Provide, Query } from "@midwayjs/core";
|
||||||
|
import { CodeService } from "../../../modules/basic/service/code-service.js";
|
||||||
|
import { OauthBoundEntity } from "../../../modules/login/entity/oauth-bound.js";
|
||||||
|
import { LoginService } from "../../../modules/login/service/login-service.js";
|
||||||
|
import { OauthBoundService } from "../../../modules/login/service/oauth-bound-service.js";
|
||||||
|
import { AddonGetterService } from "../../../modules/pipeline/service/addon-getter-service.js";
|
||||||
|
import { UserEntity } from "../../../modules/sys/authority/entity/user.js";
|
||||||
|
import { UserService } from "../../../modules/sys/authority/service/user-service.js";
|
||||||
|
import { IOauthProvider } from "../../../plugins/plugin-oauth/api.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
@@ -121,6 +121,14 @@ export class ConnectController extends BaseController {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Post('/getLogoutUrl', { summary: Constants.per.guest })
|
||||||
|
public async logout(@Body(ALL) body: any) {
|
||||||
|
checkPlus()
|
||||||
|
const addon = await this.getOauthProvider(body.type);
|
||||||
|
const { logoutUrl } = await addon.buildLogoutUrl(body);
|
||||||
|
return this.ok({ logoutUrl });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Post('/token', { summary: Constants.per.guest })
|
@Post('/token', { summary: Constants.per.guest })
|
||||||
public async token(@Body(ALL) body: { validationCode: string, type: string }) {
|
public async token(@Body(ALL) body: { validationCode: string, type: string }) {
|
||||||
@@ -241,6 +249,12 @@ export class ConnectController extends BaseController {
|
|||||||
if (addonEntity) {
|
if (addonEntity) {
|
||||||
provider.addonId = conf.addonId;
|
provider.addonId = conf.addonId;
|
||||||
provider.addonTitle = addonEntity.name;
|
provider.addonTitle = addonEntity.name;
|
||||||
|
|
||||||
|
const addon = await this.addonGetterService.getAddonById(conf.addonId,true,0);
|
||||||
|
const {logoutUrl} = await addon.buildLogoutUrl();
|
||||||
|
if (logoutUrl){
|
||||||
|
provider.logoutUrl = logoutUrl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
list.push(provider);
|
list.push(provider);
|
||||||
|
|||||||
@@ -43,7 +43,15 @@ export type BuildLoginUrlReq = {
|
|||||||
from?:string;
|
from?:string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type BuildLogoutUrlReq = {
|
||||||
|
}
|
||||||
|
|
||||||
|
export type LogoutUrlReply = {
|
||||||
|
logoutUrl?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface IOauthProvider {
|
export interface IOauthProvider {
|
||||||
buildLoginUrl: (params: BuildLoginUrlReq) => Promise<LoginUrlReply>;
|
buildLoginUrl: (params: BuildLoginUrlReq) => Promise<LoginUrlReply>;
|
||||||
onCallback: (params: OnCallbackReq) => Promise<OauthToken>;
|
onCallback: (params: OnCallbackReq) => Promise<OauthToken>;
|
||||||
|
buildLogoutUrl: (params: BuildLogoutUrlReq) => Promise<LogoutUrlReply>;
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { AddonInput, BaseAddon, IsAddon } from "@certd/lib-server";
|
import { AddonInput, BaseAddon, IsAddon } from "@certd/lib-server";
|
||||||
import { BuildLoginUrlReq, IOauthProvider, OnCallbackReq } from "../api.js";
|
import { BuildLoginUrlReq, BuildLogoutUrlReq, IOauthProvider, OnCallbackReq } from "../api.js";
|
||||||
|
|
||||||
@IsAddon({
|
@IsAddon({
|
||||||
addonType: "oauth",
|
addonType: "oauth",
|
||||||
@@ -129,4 +129,12 @@ export class OidcOauthProvider extends BaseAddon implements IOauthProvider {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
async buildLogoutUrl(params: BuildLogoutUrlReq) {
|
||||||
|
const { config } = await this.getClient()
|
||||||
|
let logoutUrl = config.serverMetadata().end_session_endpoint
|
||||||
|
return {
|
||||||
|
logoutUrl: logoutUrl,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { AddonInput, BaseAddon, IsAddon } from "@certd/lib-server";
|
import { AddonInput, BaseAddon, IsAddon } from "@certd/lib-server";
|
||||||
import { BuildLoginUrlReq, IOauthProvider, OnCallbackReq } from "../api.js";
|
import { BuildLoginUrlReq, BuildLogoutUrlReq, IOauthProvider, OnCallbackReq } from "../api.js";
|
||||||
|
|
||||||
@IsAddon({
|
@IsAddon({
|
||||||
addonType: "oauth",
|
addonType: "oauth",
|
||||||
@@ -125,4 +125,9 @@ export class WxOauthProvider extends BaseAddon implements IOauthProvider {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
async buildLogoutUrl(params: BuildLogoutUrlReq) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user