refactor: Changing OAuth 2.0 to OIDC

This commit is contained in:
OnEvent 2024-08-09 16:44:15 +08:00
parent 80568f2d87
commit a3cb66661d
No known key found for this signature in database
GPG Key ID: 3CDB9068A32B4927
6 changed files with 184 additions and 69 deletions

View File

@ -23,11 +23,11 @@ const config = {
version: '',
wechat_login: false,
wechat_qrcode: '',
oauth2: false,
oauth2_app_id: '',
oauth2_authorization_endpoint: '',
oauth2_token_endpoint: '',
oauth2_userinfo_endpoint: '',
oidc: false,
oidc_app_id: '',
oidc_authorization_endpoint: '',
oidc_token_endpoint: '',
oidc_userinfo_endpoint: '',
}
};

View File

@ -98,12 +98,12 @@ export async function onLarkOAuthClicked(lark_client_id) {
window.open(`https://open.feishu.cn/open-apis/authen/v1/index?redirect_uri=${redirect_uri}&app_id=${lark_client_id}&state=${state}`);
}
export async function onOAuth2Clicked(auth_url, client_id, openInNewTab = false) {
export async function onOidcClicked(auth_url, client_id, openInNewTab = false) {
const state = await getOAuthState();
if (!state) return;
const redirect_uri = `${window.location.origin}/oauth/oidc`;
const response_type = "code";
const scope = "profile email";
const scope = "openid profile email";
const url = `${auth_url}?client_id=${client_id}&redirect_uri=${redirect_uri}&response_type=${response_type}&scope=${scope}&state=${state}`;
if (openInNewTab) {
window.open(url);

View File

@ -0,0 +1,94 @@
import { Link, useNavigate, useSearchParams } from 'react-router-dom';
import React, { useEffect, useState } from 'react';
import { showError } from 'utils/common';
import useLogin from 'hooks/useLogin';
// material-ui
import { useTheme } from '@mui/material/styles';
import { Grid, Stack, Typography, useMediaQuery, CircularProgress } from '@mui/material';
// project imports
import AuthWrapper from '../AuthWrapper';
import AuthCardWrapper from '../AuthCardWrapper';
import Logo from 'ui-component/Logo';
// assets
// ================================|| AUTH3 - LOGIN ||================================ //
const OidcOAuth = () => {
const theme = useTheme();
const matchDownSM = useMediaQuery(theme.breakpoints.down('md'));
const [searchParams] = useSearchParams();
const [prompt, setPrompt] = useState('处理中...');
const { oidcLogin } = useLogin();
let navigate = useNavigate();
const sendCode = async (code, state, count) => {
const { success, message } = await oidcLogin(code, state);
if (!success) {
if (message) {
showError(message);
}
if (count === 0) {
setPrompt(`操作失败,重定向至登录界面中...`);
await new Promise((resolve) => setTimeout(resolve, 2000));
navigate('/login');
return;
}
count++;
setPrompt(`出现错误,第 ${count} 次重试中...`);
await new Promise((resolve) => setTimeout(resolve, 2000));
await sendCode(code, state, count);
}
};
useEffect(() => {
let code = searchParams.get('code');
let state = searchParams.get('state');
sendCode(code, state, 0).then();
}, []);
return (
<AuthWrapper>
<Grid container direction="column" justifyContent="flex-end">
<Grid item xs={12}>
<Grid container justifyContent="center" alignItems="center" sx={{ minHeight: 'calc(100vh - 136px)' }}>
<Grid item sx={{ m: { xs: 1, sm: 3 }, mb: 0 }}>
<AuthCardWrapper>
<Grid container spacing={2} alignItems="center" justifyContent="center">
<Grid item sx={{ mb: 3 }}>
<Link to="#">
<Logo />
</Link>
</Grid>
<Grid item xs={12}>
<Grid container direction={matchDownSM ? 'column-reverse' : 'row'} alignItems="center" justifyContent="center">
<Grid item>
<Stack alignItems="center" justifyContent="center" spacing={1}>
<Typography color={theme.palette.primary.main} gutterBottom variant={matchDownSM ? 'h3' : 'h2'}>
OIDC 登录
</Typography>
</Stack>
</Grid>
</Grid>
</Grid>
<Grid item xs={12} container direction="column" justifyContent="center" alignItems="center" style={{ height: '200px' }}>
<CircularProgress />
<Typography variant="h3" paddingTop={'20px'}>
{prompt}
</Typography>
</Grid>
</Grid>
</AuthCardWrapper>
</Grid>
</Grid>
</Grid>
</Grid>
</AuthWrapper>
);
};
export default OidcOAuth;

View File

@ -36,7 +36,8 @@ import VisibilityOff from '@mui/icons-material/VisibilityOff';
import Github from 'assets/images/icons/github.svg';
import Wechat from 'assets/images/icons/wechat.svg';
import Lark from 'assets/images/icons/lark.svg';
import { onGitHubOAuthClicked, onLarkOAuthClicked, onOAuth2Clicked } from 'utils/common';
import OIDC from 'assets/images/icons/oidc.svg';
import { onGitHubOAuthClicked, onLarkOAuthClicked, onOidcClicked } from 'utils/common';
// ============================|| FIREBASE - LOGIN ||============================ //
@ -50,7 +51,7 @@ const LoginForm = ({ ...others }) => {
// const [checked, setChecked] = useState(true);
let tripartiteLogin = false;
if (siteInfo.github_oauth || siteInfo.wechat_login || siteInfo.lark_client_id || siteInfo.oauth2) {
if (siteInfo.github_oauth || siteInfo.wechat_login || siteInfo.lark_client_id || siteInfo.oidc) {
tripartiteLogin = true;
}
@ -145,13 +146,13 @@ const LoginForm = ({ ...others }) => {
</AnimateButton>
</Grid>
)}
{siteInfo.oauth2 && (
{siteInfo.oidc && (
<Grid item xs={12}>
<AnimateButton>
<Button
disableElevation
fullWidth
onClick={() => onOAuth2Clicked(siteInfo.oauth2_authorization_endpoint,siteInfo.oauth2_app_id)}
onClick={() => onOidcClicked(siteInfo.oidc_authorization_endpoint,siteInfo.oidc_app_id)}
size="large"
variant="outlined"
sx={{
@ -161,9 +162,9 @@ const LoginForm = ({ ...others }) => {
}}
>
<Box sx={{ mr: { xs: 1, sm: 2, width: 20 }, display: 'flex', alignItems: 'center' }}>
<img src={Wechat} alt="Lark" width={25} height={25} style={{ marginRight: matchDownSM ? 8 : 16 }} />
<img src={OIDC} alt="Lark" width={25} height={25} style={{ marginRight: matchDownSM ? 8 : 16 }} />
</Box>
使用 OAuth 2.0 登录
使用 OIDC 登录
</Button>
</AnimateButton>
</Grid>

View File

@ -20,7 +20,7 @@ import SubCard from 'ui-component/cards/SubCard';
import { IconBrandWechat, IconBrandGithub, IconMail } from '@tabler/icons-react';
import Label from 'ui-component/Label';
import { API } from 'utils/api';
import { showError, showSuccess } from 'utils/common';
import { onOidcClicked, showError, showSuccess } from 'utils/common';
import { onGitHubOAuthClicked, onLarkOAuthClicked, copy } from 'utils/common';
import * as Yup from 'yup';
import WechatModal from 'views/Authentication/AuthForms/WechatModal';
@ -28,6 +28,7 @@ import { useSelector } from 'react-redux';
import EmailModal from './component/EmailModal';
import Turnstile from 'react-turnstile';
import { ReactComponent as Lark } from 'assets/images/icons/lark.svg';
import { ReactComponent as OIDC } from 'assets/images/icons/oidc.svg';
const validationSchema = Yup.object().shape({
username: Yup.string().required('用户名 不能为空').min(3, '用户名 不能小于 3 个字符'),
@ -123,6 +124,15 @@ export default function Profile() {
loadUser().then();
}, [status]);
function getOidcId(){
if (!inputs.oidc_id) return '';
let oidc_id = inputs.oidc_id;
if (inputs.oidc_id.length > 8) {
oidc_id = inputs.oidc_id.slice(0, 6) + '...' + inputs.oidc_id.slice(-6);
}
return oidc_id;
}
return (
<>
<UserCard>
@ -141,6 +151,9 @@ export default function Profile() {
<Label variant="ghost" color={inputs.lark_id ? 'primary' : 'default'}>
<SvgIcon component={Lark} inheritViewBox="0 0 24 24" /> {inputs.lark_id || '未绑定'}
</Label>
<Label variant="ghost" color={inputs.oidc_id ? 'primary' : 'default'}>
<SvgIcon component={OIDC} inheritViewBox="0 0 24 24" /> {getOidcId() || '未绑定'}
</Label>
</Stack>
<SubCard title="个人信息">
<Grid container spacing={2}>
@ -216,6 +229,13 @@ export default function Profile() {
</Button>
</Grid>
)}
{status.oidc && !inputs.oidc_id && (
<Grid xs={12} md={4}>
<Button variant="contained" onClick={() => onOidcClicked(status.oidc_authorization_endpoint,status.oidc_app_id,true)}>
绑定 OIDC 账号
</Button>
</Grid>
)}
<Grid xs={12} md={4}>
<Button
variant="contained"

View File

@ -33,12 +33,12 @@ const SystemSetting = () => {
GitHubClientSecret: '',
LarkClientId: '',
LarkClientSecret: '',
OAuth2Enabled: '',
OAuth2AppId: '',
OAuth2AppSecret: '',
OAuth2AuthorizationEndpoint: '',
OAuth2TokenEndpoint: '',
OAuth2UserinfoEndpoint: '',
OidcEnabled: '',
OidcAppId: '',
OidcAppSecret: '',
OidcAuthorizationEndpoint: '',
OidcTokenEndpoint: '',
OidcUserinfoEndpoint: '',
Notice: '',
SMTPServer: '',
SMTPPort: '',
@ -100,7 +100,7 @@ const SystemSetting = () => {
case 'TurnstileCheckEnabled':
case 'EmailDomainRestrictionEnabled':
case 'RegisterEnabled':
case 'OAuth2Enabled':
case 'OidcEnabled':
value = inputs[key] === 'true' ? 'false' : 'true';
break;
default:
@ -150,11 +150,11 @@ const SystemSetting = () => {
name === 'MessagePusherToken' ||
name === 'LarkClientId' ||
name === 'LarkClientSecret' ||
name === 'OAuth2AppId' ||
name === 'OAuth2AppSecret' ||
name === 'OAuth2AuthorizationEndpoint' ||
name === 'OAuth2TokenEndpoint' ||
name === 'OAuth2UserinfoEndpoint'
name === 'OidcAppId' ||
name === 'OidcAppSecret' ||
name === 'OidcAuthorizationEndpoint' ||
name === 'OidcTokenEndpoint' ||
name === 'OidcUserinfoEndpoint'
)
{
setInputs((inputs) => ({ ...inputs, [name]: value }));
@ -238,29 +238,29 @@ const SystemSetting = () => {
}
};
const submitOAuth2 = async () => {
const OAuth2Config = {
OAuth2AppId: inputs.OAuth2AppId,
OAuth2AppSecret: inputs.OAuth2AppSecret,
OAuth2AuthorizationEndpoint: inputs.OAuth2AuthorizationEndpoint,
OAuth2TokenEndpoint: inputs.OAuth2TokenEndpoint,
OAuth2UserinfoEndpoint: inputs.OAuth2UserinfoEndpoint
const submitOidc = async () => {
const OidcConfig = {
OidcAppId: inputs.OidcAppId,
OidcAppSecret: inputs.OidcAppSecret,
OidcAuthorizationEndpoint: inputs.OidcAuthorizationEndpoint,
OidcTokenEndpoint: inputs.OidcTokenEndpoint,
OidcUserinfoEndpoint: inputs.OidcUserinfoEndpoint
};
console.log(OAuth2Config);
if (originInputs['OAuth2AppId'] !== inputs.OAuth2AppId) {
await updateOption('OAuth2AppId', inputs.OAuth2AppId);
console.log(OidcConfig);
if (originInputs['OidcAppId'] !== inputs.OidcAppId) {
await updateOption('OidcAppId', inputs.OidcAppId);
}
if (originInputs['OAuth2AppSecret'] !== inputs.OAuth2AppSecret && inputs.OAuth2AppSecret !== '') {
await updateOption('OAuth2AppSecret', inputs.OAuth2AppSecret);
if (originInputs['OidcAppSecret'] !== inputs.OidcAppSecret && inputs.OidcAppSecret !== '') {
await updateOption('OidcAppSecret', inputs.OidcAppSecret);
}
if (originInputs['OAuth2AuthorizationEndpoint'] !== inputs.OAuth2AuthorizationEndpoint) {
await updateOption('OAuth2AuthorizationEndpoint', inputs.OAuth2AuthorizationEndpoint);
if (originInputs['OidcAuthorizationEndpoint'] !== inputs.OidcAuthorizationEndpoint) {
await updateOption('OidcAuthorizationEndpoint', inputs.OidcAuthorizationEndpoint);
}
if (originInputs['OAuth2TokenEndpoint'] !== inputs.OAuth2TokenEndpoint) {
await updateOption('OAuth2TokenEndpoint', inputs.OAuth2TokenEndpoint);
if (originInputs['OidcTokenEndpoint'] !== inputs.OidcTokenEndpoint) {
await updateOption('OidcTokenEndpoint', inputs.OidcTokenEndpoint);
}
if (originInputs['OAuth2UserinfoEndpoint'] !== inputs.OAuth2UserinfoEndpoint) {
await updateOption('OAuth2UserinfoEndpoint', inputs.OAuth2UserinfoEndpoint);
if (originInputs['OidcUserinfoEndpoint'] !== inputs.OidcUserinfoEndpoint) {
await updateOption('OidcUserinfoEndpoint', inputs.OidcUserinfoEndpoint);
}
};
@ -332,8 +332,8 @@ const SystemSetting = () => {
</Grid>
<Grid xs={12} md={3}>
<FormControlLabel
label="允许通过 OAuth 2.0 登录 & 注册"
control={<Checkbox checked={inputs.OAuth2Enabled === 'true'} onChange={handleInputChange} name="OAuth2Enabled" />}
label="允许通过 Oidc 登录 & 注册"
control={<Checkbox checked={inputs.OidcEnabled === 'true'} onChange={handleInputChange} name="OidcEnabled" />}
/>
</Grid>
<Grid xs={12} md={3}>
@ -663,10 +663,10 @@ const SystemSetting = () => {
</SubCard>
<SubCard
title="配置第三方 OAuth 2.0"
title="配置 OIDC"
subTitle={
<span>
用以支持通过第三方 OAuth2 登录例如 OktaAuth0 或自建的兼容 OAuth2.0 协议的 IdP
用以支持通过 OIDC 登录例如 OktaAuth0 等兼容 OIDC 协议的 IdP
</span>
}
>
@ -679,11 +679,11 @@ const SystemSetting = () => {
</Grid>
<Grid xs={ 12 } md={ 6 }>
<FormControl fullWidth>
<InputLabel htmlFor="OAuth2AppId">App ID</InputLabel>
<InputLabel htmlFor="OidcAppId">App ID</InputLabel>
<OutlinedInput
id="OAuth2AppId"
name="OAuth2AppId"
value={ inputs.OAuth2AppId || '' }
id="OidcAppId"
name="OidcAppId"
value={ inputs.OidcAppId || '' }
onChange={ handleInputChange }
label="App ID"
placeholder="输入 OAuth 2.0 的 App ID"
@ -693,11 +693,11 @@ const SystemSetting = () => {
</Grid>
<Grid xs={ 12 } md={ 6 }>
<FormControl fullWidth>
<InputLabel htmlFor="OAuth2AppSecret">App Secret</InputLabel>
<InputLabel htmlFor="OidcAppSecret">App Secret</InputLabel>
<OutlinedInput
id="OAuth2AppSecret"
name="OAuth2AppSecret"
value={ inputs.OAuth2AppSecret || '' }
id="OidcAppSecret"
name="OidcAppSecret"
value={ inputs.OidcAppSecret || '' }
onChange={ handleInputChange }
label="App Secret"
placeholder="敏感信息不会发送到前端显示"
@ -707,11 +707,11 @@ const SystemSetting = () => {
</Grid>
<Grid xs={ 12 } md={ 6 }>
<FormControl fullWidth>
<InputLabel htmlFor="OAuth2AuthorizationEndpoint">授权地址</InputLabel>
<InputLabel htmlFor="OidcAuthorizationEndpoint">授权地址</InputLabel>
<OutlinedInput
id="OAuth2AuthorizationEndpoint"
name="OAuth2AuthorizationEndpoint"
value={ inputs.OAuth2AuthorizationEndpoint || '' }
id="OidcAuthorizationEndpoint"
name="OidcAuthorizationEndpoint"
value={ inputs.OidcAuthorizationEndpoint || '' }
onChange={ handleInputChange }
label="授权地址"
placeholder="输入 OAuth 2.0 的 授权地址"
@ -721,11 +721,11 @@ const SystemSetting = () => {
</Grid>
<Grid xs={ 12 } md={ 6 }>
<FormControl fullWidth>
<InputLabel htmlFor="OAuth2TokenEndpoint">认证地址</InputLabel>
<InputLabel htmlFor="OidcTokenEndpoint">认证地址</InputLabel>
<OutlinedInput
id="OAuth2TokenEndpoint"
name="OAuth2TokenEndpoint"
value={ inputs.OAuth2TokenEndpoint || '' }
id="OidcTokenEndpoint"
name="OidcTokenEndpoint"
value={ inputs.OidcTokenEndpoint || '' }
onChange={ handleInputChange }
label="认证地址"
placeholder="输入 OAuth 2.0 的 认证地址"
@ -735,11 +735,11 @@ const SystemSetting = () => {
</Grid>
<Grid xs={ 12 } md={ 6 }>
<FormControl fullWidth>
<InputLabel htmlFor="OAuth2UserinfoEndpoint">用户地址</InputLabel>
<InputLabel htmlFor="OidcUserinfoEndpoint">用户地址</InputLabel>
<OutlinedInput
id="OAuth2UserinfoEndpoint"
name="OAuth2UserinfoEndpoint"
value={ inputs.OAuth2UserinfoEndpoint || '' }
id="OidcUserinfoEndpoint"
name="OidcUserinfoEndpoint"
value={ inputs.OidcUserinfoEndpoint || '' }
onChange={ handleInputChange }
label="认证地址"
placeholder="输入 OAuth 2.0 的 认证地址"
@ -748,7 +748,7 @@ const SystemSetting = () => {
</FormControl>
</Grid>
<Grid xs={ 12 }>
<Button variant="contained" onClick={ submitOAuth2 }>
<Button variant="contained" onClick={ submitOidc }>
保存第三方 OAuth 2.0 设置
</Button>
</Grid>