feat(auth): add global auth token provider (#2824)

This commit is contained in:
YANG QIA 2023-10-13 05:12:26 -05:00 committed by GitHub
parent 2d593175b9
commit 6f09d7ad06
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 67 additions and 79 deletions

View File

@ -0,0 +1,41 @@
import {
OptionsComponentProvider,
SettingsCenterProvider,
SigninPageProvider,
SignupPageProvider,
} from '@nocobase/client';
import React, { FC } from 'react';
import { Authenticator } from './settings/Authenticator';
import SigninPage from './basic/SigninPage';
import { presetAuthType } from '../preset';
import SignupPage from './basic/SignupPage';
import { useAuthTranslation } from './locale';
import { Options } from './basic/Options';
export const AuthPluginProvider: FC = (props) => {
const { t } = useAuthTranslation();
return (
<SettingsCenterProvider
settings={{
auth: {
title: t('Authentication'),
icon: 'LoginOutlined',
tabs: {
authenticators: {
title: t('Authenticators'),
component: () => <Authenticator />,
},
},
},
}}
>
<OptionsComponentProvider authType={presetAuthType} component={Options}>
<SigninPageProvider authType={presetAuthType} tabTitle={t('Sign in via password')} component={SigninPage}>
<SignupPageProvider authType={presetAuthType} component={SignupPage}>
{props.children}
</SignupPageProvider>
</SigninPageProvider>
</OptionsComponentProvider>
</SettingsCenterProvider>
);
};

View File

@ -1,41 +1,19 @@
import { import { useAPIClient } from '@nocobase/client';
OptionsComponentProvider, import React, { useEffect } from 'react';
SettingsCenterProvider, import { useLocation } from 'react-router-dom';
SigninPageProvider,
SignupPageProvider,
} from '@nocobase/client';
import React, { FC } from 'react';
import { Authenticator } from './settings/Authenticator';
import SigninPage from './basic/SigninPage';
import { presetAuthType } from '../preset';
import SignupPage from './basic/SignupPage';
import { useAuthTranslation } from './locale';
import { Options } from './basic/Options';
export const AuthProvider: FC = (props) => { export const AuthProvider: React.FC = (props) => {
const { t } = useAuthTranslation(); const location = useLocation();
return ( const api = useAPIClient();
<SettingsCenterProvider
settings={{ useEffect(() => {
auth: { const params = new URLSearchParams(location.search);
title: t('Authentication'), const authenticator = params.get('authenticator');
icon: 'LoginOutlined', const token = params.get('token');
tabs: { if (token) {
authenticators: { api.auth.setToken(token);
title: t('Authenticators'), api.auth.setAuthenticator(authenticator);
component: () => <Authenticator />, }
}, });
}, return <>{props.children}</>;
},
}}
>
<OptionsComponentProvider authType={presetAuthType} component={Options}>
<SigninPageProvider authType={presetAuthType} tabTitle={t('Sign in via password')} component={SigninPage}>
<SignupPageProvider authType={presetAuthType} component={SignupPage}>
{props.children}
</SignupPageProvider>
</SigninPageProvider>
</OptionsComponentProvider>
</SettingsCenterProvider>
);
}; };

View File

@ -1,9 +1,11 @@
import { Plugin } from '@nocobase/client'; import { Plugin } from '@nocobase/client';
import { AuthPluginProvider } from './AuthPluginProvider';
import { AuthProvider } from './AuthProvider'; import { AuthProvider } from './AuthProvider';
export class AuthPlugin extends Plugin { export class AuthPlugin extends Plugin {
async load() { async load() {
this.app.use(AuthProvider); this.app.providers.unshift([AuthProvider, {}]);
this.app.use(AuthPluginProvider);
} }
} }

View File

@ -1,4 +1,4 @@
import { Authenticator, useAPIClient, useRedirect, useCurrentUserContext } from '@nocobase/client'; import { Authenticator, useRedirect } from '@nocobase/client';
import React, { useEffect } from 'react'; import React, { useEffect } from 'react';
import { LoginOutlined } from '@ant-design/icons'; import { LoginOutlined } from '@ant-design/icons';
import { Button, Space, message } from 'antd'; import { Button, Space, message } from 'antd';
@ -6,10 +6,8 @@ import { useLocation } from 'react-router-dom';
import { getSubAppName } from '@nocobase/sdk'; import { getSubAppName } from '@nocobase/sdk';
export const SigninPage = (props: { authenticator: Authenticator }) => { export const SigninPage = (props: { authenticator: Authenticator }) => {
const api = useAPIClient();
const redirect = useRedirect(); const redirect = useRedirect();
const location = useLocation(); const location = useLocation();
const { refreshAsync: refresh } = useCurrentUserContext();
const authenticator = props.authenticator; const authenticator = props.authenticator;
@ -21,7 +19,6 @@ export const SigninPage = (props: { authenticator: Authenticator }) => {
useEffect(() => { useEffect(() => {
const params = new URLSearchParams(location.search); const params = new URLSearchParams(location.search);
const token = params.get('token');
const name = params.get('authenticator'); const name = params.get('authenticator');
const error = params.get('error'); const error = params.get('error');
if (name !== authenticator.name) { if (name !== authenticator.name) {
@ -31,14 +28,6 @@ export const SigninPage = (props: { authenticator: Authenticator }) => {
message.error(error); message.error(error);
return; return;
} }
if (token) {
api.auth.setToken(token);
api.auth.setAuthenticator(name);
refresh()
.then(() => redirect())
.catch((err) => console.log(err));
return;
}
}); });
return ( return (

View File

@ -16,7 +16,7 @@ export const service = async (ctx: Context, next: Next) => {
const auth = (await ctx.app.authManager.get(authenticator, ctx)) as CASAuth; const auth = (await ctx.app.authManager.get(authenticator, ctx)) as CASAuth;
try { try {
const { token } = await auth.signIn(); const { token } = await auth.signIn();
ctx.redirect(`${prefix}/signin?authenticator=${authenticator}&token=${token}`); ctx.redirect(`${prefix}/admin?authenticator=${authenticator}&token=${token}`);
} catch (error) { } catch (error) {
ctx.redirect(`${prefix}/signin?authenticator=${authenticator}&error=${error.message}`); ctx.redirect(`${prefix}/signin?authenticator=${authenticator}&error=${error.message}`);
} }

View File

@ -1,5 +1,5 @@
import { LoginOutlined } from '@ant-design/icons'; import { LoginOutlined } from '@ant-design/icons';
import { Authenticator, css, useAPIClient, useCurrentUserContext, useRedirect } from '@nocobase/client'; import { Authenticator, css, useAPIClient } from '@nocobase/client';
import { Button, Space, message } from 'antd'; import { Button, Space, message } from 'antd';
import React, { useEffect } from 'react'; import React, { useEffect } from 'react';
import { useOidcTranslation } from './locale'; import { useOidcTranslation } from './locale';
@ -13,9 +13,7 @@ export interface OIDCProvider {
export const OIDCButton = ({ authenticator }: { authenticator: Authenticator }) => { export const OIDCButton = ({ authenticator }: { authenticator: Authenticator }) => {
const { t } = useOidcTranslation(); const { t } = useOidcTranslation();
const api = useAPIClient(); const api = useAPIClient();
const redirect = useRedirect();
const location = useLocation(); const location = useLocation();
const { refreshAsync: refresh } = useCurrentUserContext();
const login = async () => { const login = async () => {
const response = await api.request({ const response = await api.request({
@ -32,7 +30,6 @@ export const OIDCButton = ({ authenticator }: { authenticator: Authenticator })
useEffect(() => { useEffect(() => {
const params = new URLSearchParams(location.search); const params = new URLSearchParams(location.search);
const token = params.get('token');
const name = params.get('authenticator'); const name = params.get('authenticator');
const error = params.get('error'); const error = params.get('error');
if (name !== authenticator.name) { if (name !== authenticator.name) {
@ -42,14 +39,6 @@ export const OIDCButton = ({ authenticator }: { authenticator: Authenticator })
message.error(t(error)); message.error(t(error));
return; return;
} }
if (token) {
api.auth.setToken(token);
api.auth.setAuthenticator(name);
refresh()
.then(() => redirect())
.catch((err) => console.log(err));
return;
}
}); });
return ( return (

View File

@ -19,7 +19,7 @@ export const redirect = async (ctx: Context, next: Next) => {
const auth = (await ctx.app.authManager.get(authenticator, ctx)) as OIDCAuth; const auth = (await ctx.app.authManager.get(authenticator, ctx)) as OIDCAuth;
try { try {
const { token } = await auth.signIn(); const { token } = await auth.signIn();
ctx.redirect(`${prefix}/signin?authenticator=${authenticator}&token=${token}`); ctx.redirect(`${prefix}/admin?authenticator=${authenticator}&token=${token}`);
} catch (error) { } catch (error) {
ctx.redirect(`${prefix}/signin?authenticator=${authenticator}&error=${error.message}`); ctx.redirect(`${prefix}/signin?authenticator=${authenticator}&error=${error.message}`);
} }

View File

@ -1,5 +1,5 @@
import { LoginOutlined } from '@ant-design/icons'; import { LoginOutlined } from '@ant-design/icons';
import { Authenticator, css, useAPIClient, useCurrentUserContext, useRedirect } from '@nocobase/client'; import { Authenticator, css, useAPIClient } from '@nocobase/client';
import { Button, Space, message } from 'antd'; import { Button, Space, message } from 'antd';
import React, { useEffect } from 'react'; import React, { useEffect } from 'react';
import { useSamlTranslation } from './locale'; import { useSamlTranslation } from './locale';
@ -8,9 +8,7 @@ import { useLocation } from 'react-router-dom';
export const SAMLButton = ({ authenticator }: { authenticator: Authenticator }) => { export const SAMLButton = ({ authenticator }: { authenticator: Authenticator }) => {
const { t } = useSamlTranslation(); const { t } = useSamlTranslation();
const api = useAPIClient(); const api = useAPIClient();
const redirect = useRedirect();
const location = useLocation(); const location = useLocation();
const { refreshAsync: refresh } = useCurrentUserContext();
const login = async () => { const login = async () => {
const response = await api.request({ const response = await api.request({
@ -27,7 +25,6 @@ export const SAMLButton = ({ authenticator }: { authenticator: Authenticator })
useEffect(() => { useEffect(() => {
const params = new URLSearchParams(location.search); const params = new URLSearchParams(location.search);
const token = params.get('token');
const name = params.get('authenticator'); const name = params.get('authenticator');
const error = params.get('error'); const error = params.get('error');
if (name !== authenticator.name) { if (name !== authenticator.name) {
@ -37,14 +34,6 @@ export const SAMLButton = ({ authenticator }: { authenticator: Authenticator })
message.error(error); message.error(error);
return; return;
} }
if (token) {
api.auth.setToken(token);
api.auth.setAuthenticator(name);
refresh()
.then(() => redirect())
.catch((err) => console.log(err));
return;
}
}); });
return ( return (

View File

@ -14,7 +14,7 @@ export const redirect = async (ctx: Context, next: Next) => {
const auth = (await ctx.app.authManager.get(authenticator, ctx)) as SAMLAuth; const auth = (await ctx.app.authManager.get(authenticator, ctx)) as SAMLAuth;
try { try {
const { token } = await auth.signIn(); const { token } = await auth.signIn();
ctx.redirect(`${prefix}/signin?authenticator=${authenticator}&token=${token}`); ctx.redirect(`${prefix}/admin?authenticator=${authenticator}&token=${token}`);
} catch (error) { } catch (error) {
ctx.redirect(`${prefix}/signin?authenticator=${authenticator}&error=${error.message}`); ctx.redirect(`${prefix}/signin?authenticator=${authenticator}&error=${error.message}`);
} }