feat: add oidc support (#1725)
Some checks are pending
CI / Unit tests (push) Waiting to run
CI / commit_lint (push) Waiting to run

* feat: add the ui for configuring the third-party standard OAuth2.0/OIDC.

- update SystemSetting.js
- add setup ui
- add configuration

* feat: add the ui for "allow the OAuth 2.0 to login"

- update SystemSetting.js

* feat: add OAuth 2.0 web ui and its process functions

- update common.js
- update AuthLogin.js
- update config.js

* fix: missing "Userinfo" endpoint configuration entry, used by OAuth clients to request user information from the IdP.

- update config.js
- update SystemSetting.js

* feat: updated the icons for Lark and OIDC to match the style of the icons for WeChat, EMail, GitHub.

- update lark.svg
- new oidc.svg

* refactor: Changing OAuth 2.0 to OIDC

* feat: add OIDC login method

* feat: Add support for OIDC login to the backend

* fix: Change the AppId and AppSecret on the Web UI to the standard usage: ClientId, ClientSecret.

* feat: Support quick configuration of OIDC through Well-Known Discovery Endpoint

* feat: Standardize terminology, add well-known configuration

- Change the AppId and AppSecret on the Server End to the standard usage: ClientId, ClientSecret.
- add Well-Known configuration to store in database, no actual use in server end but store and display in web ui only
This commit is contained in:
OnEvent
2024-09-21 23:03:20 +08:00
committed by GitHub
parent 649ecbf29c
commit 99c8c77504
16 changed files with 659 additions and 26 deletions

View File

@@ -33,6 +33,13 @@ const SystemSetting = () => {
GitHubClientSecret: '',
LarkClientId: '',
LarkClientSecret: '',
OidcEnabled: '',
OidcWellKnown: '',
OidcClientId: '',
OidcClientSecret: '',
OidcAuthorizationEndpoint: '',
OidcTokenEndpoint: '',
OidcUserinfoEndpoint: '',
Notice: '',
SMTPServer: '',
SMTPPort: '',
@@ -94,6 +101,7 @@ const SystemSetting = () => {
case 'TurnstileCheckEnabled':
case 'EmailDomainRestrictionEnabled':
case 'RegisterEnabled':
case 'OidcEnabled':
value = inputs[key] === 'true' ? 'false' : 'true';
break;
default:
@@ -142,8 +150,15 @@ const SystemSetting = () => {
name === 'MessagePusherAddress' ||
name === 'MessagePusherToken' ||
name === 'LarkClientId' ||
name === 'LarkClientSecret'
) {
name === 'LarkClientSecret' ||
name === 'OidcClientId' ||
name === 'OidcClientSecret' ||
name === 'OidcWellKnown' ||
name === 'OidcAuthorizationEndpoint' ||
name === 'OidcTokenEndpoint' ||
name === 'OidcUserinfoEndpoint'
)
{
setInputs((inputs) => ({ ...inputs, [name]: value }));
} else {
await updateOption(name, value);
@@ -225,6 +240,43 @@ const SystemSetting = () => {
}
};
const submitOidc = async () => {
if (inputs.OidcWellKnown !== '') {
if (!inputs.OidcWellKnown.startsWith('http://') && !inputs.OidcWellKnown.startsWith('https://')) {
showError('Well-Known URL 必须以 http:// 或 https:// 开头');
return;
}
try {
const res = await API.get(inputs.OidcWellKnown);
inputs.OidcAuthorizationEndpoint = res.data['authorization_endpoint'];
inputs.OidcTokenEndpoint = res.data['token_endpoint'];
inputs.OidcUserinfoEndpoint = res.data['userinfo_endpoint'];
showSuccess('获取 OIDC 配置成功!');
} catch (err) {
showError("获取 OIDC 配置失败,请检查网络状况和 Well-Known URL 是否正确");
}
}
if (originInputs['OidcWellKnown'] !== inputs.OidcWellKnown) {
await updateOption('OidcWellKnown', inputs.OidcWellKnown);
}
if (originInputs['OidcClientId'] !== inputs.OidcClientId) {
await updateOption('OidcClientId', inputs.OidcClientId);
}
if (originInputs['OidcClientSecret'] !== inputs.OidcClientSecret && inputs.OidcClientSecret !== '') {
await updateOption('OidcClientSecret', inputs.OidcClientSecret);
}
if (originInputs['OidcAuthorizationEndpoint'] !== inputs.OidcAuthorizationEndpoint) {
await updateOption('OidcAuthorizationEndpoint', inputs.OidcAuthorizationEndpoint);
}
if (originInputs['OidcTokenEndpoint'] !== inputs.OidcTokenEndpoint) {
await updateOption('OidcTokenEndpoint', inputs.OidcTokenEndpoint);
}
if (originInputs['OidcUserinfoEndpoint'] !== inputs.OidcUserinfoEndpoint) {
await updateOption('OidcUserinfoEndpoint', inputs.OidcUserinfoEndpoint);
}
};
return (
<>
<Stack spacing={2}>
@@ -291,6 +343,12 @@ const SystemSetting = () => {
control={<Checkbox checked={inputs.GitHubOAuthEnabled === 'true'} onChange={handleInputChange} name="GitHubOAuthEnabled" />}
/>
</Grid>
<Grid xs={12} md={3}>
<FormControlLabel
label="允许通过 OIDC 登录 & 注册"
control={<Checkbox checked={inputs.OidcEnabled === 'true'} onChange={handleInputChange} name="OidcEnabled" />}
/>
</Grid>
<Grid xs={12} md={3}>
<FormControlLabel
label="允许通过微信登录 & 注册"
@@ -616,6 +674,117 @@ const SystemSetting = () => {
</Grid>
</Grid>
</SubCard>
<SubCard
title="配置 OIDC"
subTitle={
<span>
用以支持通过 OIDC 登录例如 OktaAuth0 等兼容 OIDC 协议的 IdP
</span>
}
>
<Grid container spacing={ { xs: 3, sm: 2, md: 4 } }>
<Grid xs={ 12 } md={ 12 }>
<Alert severity="info" sx={ { wordWrap: 'break-word' } }>
主页链接填 <code>{ inputs.ServerAddress }</code>
重定向 URL <code>{ `${ inputs.ServerAddress }/oauth/oidc` }</code>
</Alert> <br />
<Alert severity="info" sx={ { wordWrap: 'break-word' } }>
若你的 OIDC Provider 支持 Discovery Endpoint你可以仅填写 OIDC Well-Known URL系统会自动获取 OIDC 配置
</Alert>
</Grid>
<Grid xs={ 12 } md={ 6 }>
<FormControl fullWidth>
<InputLabel htmlFor="OidcClientId">Client ID</InputLabel>
<OutlinedInput
id="OidcClientId"
name="OidcClientId"
value={ inputs.OidcClientId || '' }
onChange={ handleInputChange }
label="Client ID"
placeholder="输入 OIDC 的 Client ID"
disabled={ loading }
/>
</FormControl>
</Grid>
<Grid xs={ 12 } md={ 6 }>
<FormControl fullWidth>
<InputLabel htmlFor="OidcClientSecret">Client Secret</InputLabel>
<OutlinedInput
id="OidcClientSecret"
name="OidcClientSecret"
value={ inputs.OidcClientSecret || '' }
onChange={ handleInputChange }
label="Client Secret"
placeholder="敏感信息不会发送到前端显示"
disabled={ loading }
/>
</FormControl>
</Grid>
<Grid xs={ 12 } md={ 6 }>
<FormControl fullWidth>
<InputLabel htmlFor="OidcWellKnown">Well-Known URL</InputLabel>
<OutlinedInput
id="OidcWellKnown"
name="OidcWellKnown"
value={ inputs.OidcWellKnown || '' }
onChange={ handleInputChange }
label="Well-Known URL"
placeholder="请输入 OIDC 的 Well-Known URL"
disabled={ loading }
/>
</FormControl>
</Grid>
<Grid xs={ 12 } md={ 6 }>
<FormControl fullWidth>
<InputLabel htmlFor="OidcAuthorizationEndpoint">Authorization Endpoint</InputLabel>
<OutlinedInput
id="OidcAuthorizationEndpoint"
name="OidcAuthorizationEndpoint"
value={ inputs.OidcAuthorizationEndpoint || '' }
onChange={ handleInputChange }
label="Authorization Endpoint"
placeholder="输入 OIDC 的 Authorization Endpoint"
disabled={ loading }
/>
</FormControl>
</Grid>
<Grid xs={ 12 } md={ 6 }>
<FormControl fullWidth>
<InputLabel htmlFor="OidcTokenEndpoint">Token Endpoint</InputLabel>
<OutlinedInput
id="OidcTokenEndpoint"
name="OidcTokenEndpoint"
value={ inputs.OidcTokenEndpoint || '' }
onChange={ handleInputChange }
label="Token Endpoint"
placeholder="输入 OIDC 的 Token Endpoint"
disabled={ loading }
/>
</FormControl>
</Grid>
<Grid xs={ 12 } md={ 6 }>
<FormControl fullWidth>
<InputLabel htmlFor="OidcUserinfoEndpoint">Userinfo Endpoint</InputLabel>
<OutlinedInput
id="OidcUserinfoEndpoint"
name="OidcUserinfoEndpoint"
value={ inputs.OidcUserinfoEndpoint || '' }
onChange={ handleInputChange }
label="Userinfo Endpoint"
placeholder="输入 OIDC 的 Userinfo Endpoint"
disabled={ loading }
/>
</FormControl>
</Grid>
<Grid xs={ 12 }>
<Button variant="contained" onClick={ submitOidc }>
保存 OIDC 设置
</Button>
</Grid>
</Grid>
</SubCard>
<SubCard
title="配置 Message Pusher"
subTitle={