diff --git a/packages/plugins/@nocobase/plugin-oidc/src/client/OIDCButton.tsx b/packages/plugins/@nocobase/plugin-oidc/src/client/OIDCButton.tsx index a73a07646..e16097827 100644 --- a/packages/plugins/@nocobase/plugin-oidc/src/client/OIDCButton.tsx +++ b/packages/plugins/@nocobase/plugin-oidc/src/client/OIDCButton.tsx @@ -1,76 +1,57 @@ import { LoginOutlined } from '@ant-design/icons'; -import { Authenticator, css, useAPIClient, useRedirect } from '@nocobase/client'; -import { useMemoizedFn } from 'ahooks'; -import { Button, Space } from 'antd'; -import React, { useEffect, useState } from 'react'; +import { Authenticator, css, useAPIClient, useCurrentUserContext, useRedirect } from '@nocobase/client'; +import { Button, Space, message } from 'antd'; +import React, { useEffect } from 'react'; import { useOidcTranslation } from './locale'; +import { useLocation } from 'react-router-dom'; export interface OIDCProvider { clientId: string; title: string; } -export const OIDCButton = (props: { authenticator: Authenticator }) => { +export const OIDCButton = ({ authenticator }: { authenticator: Authenticator }) => { const { t } = useOidcTranslation(); - const [windowHandler, setWindowHandler] = useState(); const api = useAPIClient(); const redirect = useRedirect(); + const location = useLocation(); + const { refreshAsync: refresh } = useCurrentUserContext(); - /** - * 打开登录弹出框 - */ - const handleOpen = async (name: string) => { + const login = async () => { const response = await api.request({ method: 'post', url: 'oidc:getAuthUrl', headers: { - 'X-Authenticator': name, + 'X-Authenticator': authenticator.name, }, }); const authUrl = response?.data?.data; - const { width, height } = screen; - - const win = window.open( - authUrl, - '_blank', - `width=800,height=600,left=${(width - 800) / 2},top=${ - (height - 600) / 2 - },toolbar=no,menubar=no,location=no,status=no`, - ); - - setWindowHandler(win); + window.location.replace(authUrl); }; - /** - * 从弹出窗口,发消息回来进行登录 - */ - const handleOIDCLogin = useMemoizedFn(async (event: MessageEvent) => { - const { state } = event.data; - const search = new URLSearchParams(state); - const authenticator = search.get('name'); - try { - await api.auth.signIn(event.data, authenticator); - redirect(); - } catch (err) { - console.error(err); + useEffect(() => { + const params = new URLSearchParams(location.search); + const token = params.get('token'); + const name = params.get('authenticator'); + const error = params.get('error'); + if (name !== authenticator.name) { + return; + } + if (error) { + message.error(t(error)); + return; + } + if (token) { + api.auth.setToken(token); + api.auth.setAuthenticator(name); + refresh() + .then(() => redirect()) + .catch((err) => console.log(err)); + return; } }); - /** - * 监听弹出窗口的消息 - */ - useEffect(() => { - if (!windowHandler) return; - - const channel = new BroadcastChannel('nocobase-oidc-response'); - channel.onmessage = handleOIDCLogin; - return () => { - channel.close(); - }; - }, [windowHandler, handleOIDCLogin]); - - const authenticator = props.authenticator; return ( { display: flex; `} > - diff --git a/packages/plugins/@nocobase/plugin-oidc/src/server/__tests__/oidc.test.ts b/packages/plugins/@nocobase/plugin-oidc/src/server/__tests__/oidc.test.ts index 673ed22bf..00c483024 100644 --- a/packages/plugins/@nocobase/plugin-oidc/src/server/__tests__/oidc.test.ts +++ b/packages/plugins/@nocobase/plugin-oidc/src/server/__tests__/oidc.test.ts @@ -72,12 +72,7 @@ describe('oidc', () => { const res = await agent .set('X-Authenticator', 'oidc-auth') .set('Cookie', ['nocobase_oidc=token']) - .resource('auth') - .signIn() - .send({ - code: '', - state: 'token=token&name=oidc-auth', - }); + .get('/auth:signIn?state=token%3Dtoken&name=oidc-auth'); expect(res.body.data.user).toBeDefined(); expect(res.body.data.user.nickname).toBe('user1'); diff --git a/packages/plugins/@nocobase/plugin-oidc/src/server/actions/redirect.ts b/packages/plugins/@nocobase/plugin-oidc/src/server/actions/redirect.ts index 3f962f18c..74667a880 100644 --- a/packages/plugins/@nocobase/plugin-oidc/src/server/actions/redirect.ts +++ b/packages/plugins/@nocobase/plugin-oidc/src/server/actions/redirect.ts @@ -1,29 +1,18 @@ -import { Context } from '@nocobase/actions'; - -export const redirect = async (ctx: Context, next) => { - const { params } = ctx.action; - - const template = ` - - - - - - - - - - - - - `; - - ctx.body = template; - ctx.withoutDataWrapping = true; +import { Context, Next } from '@nocobase/actions'; +import { OIDCAuth } from '../oidc-auth'; +export const redirect = async (ctx: Context, next: Next) => { + const { + params: { state }, + } = ctx.action; + const search = new URLSearchParams(state); + const authenticator = search.get('name'); + const auth = (await ctx.app.authManager.get(authenticator, ctx)) as OIDCAuth; + try { + const { token } = await auth.signIn(); + ctx.redirect(`/signin?authenticator=${authenticator}&token=${token}`); + } catch (error) { + ctx.redirect(`/signin?authenticator=${authenticator}&error=${error.message}`); + } await next(); }; diff --git a/packages/plugins/@nocobase/plugin-oidc/src/server/oidc-auth.ts b/packages/plugins/@nocobase/plugin-oidc/src/server/oidc-auth.ts index 009102f13..cbbd1c359 100644 --- a/packages/plugins/@nocobase/plugin-oidc/src/server/oidc-auth.ts +++ b/packages/plugins/@nocobase/plugin-oidc/src/server/oidc-auth.ts @@ -49,9 +49,7 @@ export class OIDCAuth extends BaseAuth { async validate() { const ctx = this.ctx; - const { - params: { values }, - } = ctx.action; + const { params: values } = ctx.action; const token = ctx.cookies.get(cookieName); const search = new URLSearchParams(values.state); if (search.get('token') !== token) { diff --git a/packages/plugins/@nocobase/plugin-saml/src/client/SAMLButton.tsx b/packages/plugins/@nocobase/plugin-saml/src/client/SAMLButton.tsx index 63dca36c0..9b102ef05 100644 --- a/packages/plugins/@nocobase/plugin-saml/src/client/SAMLButton.tsx +++ b/packages/plugins/@nocobase/plugin-saml/src/client/SAMLButton.tsx @@ -1,69 +1,52 @@ import { LoginOutlined } from '@ant-design/icons'; -import { Authenticator, css, useAPIClient, useRedirect } from '@nocobase/client'; -import { Button, Space } from 'antd'; -import React, { useCallback, useEffect, useState } from 'react'; +import { Authenticator, css, useAPIClient, useCurrentUserContext, useRedirect } from '@nocobase/client'; +import { Button, Space, message } from 'antd'; +import React, { useEffect } from 'react'; import { useSamlTranslation } from './locale'; +import { useLocation } from 'react-router-dom'; -export const SAMLButton = (props: { authenticator: Authenticator }) => { +export const SAMLButton = ({ authenticator }: { authenticator: Authenticator }) => { const { t } = useSamlTranslation(); - const [windowHandler, setWindowHandler] = useState(); const api = useAPIClient(); const redirect = useRedirect(); + const location = useLocation(); + const { refreshAsync: refresh } = useCurrentUserContext(); - /** - * 打开登录弹出框 - */ - const handleOpen = async (name: string) => { + const login = async () => { const response = await api.request({ method: 'post', url: 'saml:getAuthUrl', headers: { - 'X-Authenticator': name, + 'X-Authenticator': authenticator.name, }, }); const authUrl = response?.data?.data; - const { width, height } = screen; - - const win = window.open( - authUrl, - '_blank', - `width=800,height=600,left=${(width - 800) / 2},top=${ - (height - 600) / 2 - },toolbar=no,menubar=no,location=no,status=no`, - ); - - setWindowHandler(win); + window.location.replace(authUrl); }; - const handleSAMLLogin = useCallback( - async (event: MessageEvent) => { - try { - await api.auth.signIn(event.data, event.data?.authenticator); - redirect(); - } catch (err) { - console.error(err); - } finally { - windowHandler.close(); - setWindowHandler(undefined); - } - }, - [api, redirect, windowHandler], - ); - - /** - * 监听弹出窗口的消息 - */ useEffect(() => { - if (!windowHandler) return; + const params = new URLSearchParams(location.search); + const token = params.get('token'); + const name = params.get('authenticator'); + const error = params.get('error'); + if (name !== authenticator.name) { + return; + } + if (error) { + message.error(error); + return; + } + if (token) { + api.auth.setToken(token); + api.auth.setAuthenticator(name); + refresh() + .then(() => redirect()) + .catch((err) => console.log(err)); + return; + } + }); - window.addEventListener('message', handleSAMLLogin); - return () => { - window.removeEventListener('message', handleSAMLLogin); - }; - }, [windowHandler, handleSAMLLogin]); - - const authenticator = props.authenticator; return ( { display: flex; `} > - diff --git a/packages/plugins/@nocobase/plugin-saml/src/server/__tests__/saml.test.ts b/packages/plugins/@nocobase/plugin-saml/src/server/__tests__/saml.test.ts index fd9de80bc..2e614cf3d 100644 --- a/packages/plugins/@nocobase/plugin-saml/src/server/__tests__/saml.test.ts +++ b/packages/plugins/@nocobase/plugin-saml/src/server/__tests__/saml.test.ts @@ -61,15 +61,9 @@ describe('saml', () => { loggedOut: false, }); - const res = await agent - .set('X-Authenticator', 'saml-auth') - .resource('auth') - .signIn() - .send({ - samlResponse: { - SAMLResponse: '', - }, - }); + const res = await agent.set('X-Authenticator', 'saml-auth').resource('auth').signIn().send({ + samlResponse: {}, + }); expect(res.body.data.user).toBeDefined(); expect(res.body.data.user.nickname).toBe('Test Nocobase'); diff --git a/packages/plugins/@nocobase/plugin-saml/src/server/actions/redirect.ts b/packages/plugins/@nocobase/plugin-saml/src/server/actions/redirect.ts index 0e8c3eed3..e2e88c74b 100644 --- a/packages/plugins/@nocobase/plugin-saml/src/server/actions/redirect.ts +++ b/packages/plugins/@nocobase/plugin-saml/src/server/actions/redirect.ts @@ -1,30 +1,14 @@ -import { Context } from '@nocobase/actions'; - -export const redirect = async (ctx: Context, next) => { - const { params } = ctx.action; - - const template = ` - - - - - - - - - - - - - `; - - ctx.body = template; - ctx.withoutDataWrapping = true; +import { Context, Next } from '@nocobase/actions'; +import { SAMLAuth } from '../saml-auth'; +export const redirect = async (ctx: Context, next: Next) => { + const { authenticator } = ctx.action.params || {}; + const auth = (await ctx.app.authManager.get(authenticator, ctx)) as SAMLAuth; + try { + const { token } = await auth.signIn(); + ctx.redirect(`/signin?authenticator=${authenticator}&token=${token}`); + } catch (error) { + ctx.redirect(`/signin?authenticator=${authenticator}&error=${error.message}`); + } await next(); }; diff --git a/packages/plugins/@nocobase/plugin-saml/src/server/saml-auth.ts b/packages/plugins/@nocobase/plugin-saml/src/server/saml-auth.ts index 39367799a..58f2ad02b 100644 --- a/packages/plugins/@nocobase/plugin-saml/src/server/saml-auth.ts +++ b/packages/plugins/@nocobase/plugin-saml/src/server/saml-auth.ts @@ -35,9 +35,7 @@ export class SAMLAuth extends BaseAuth { async validate() { const ctx = this.ctx; const { - params: { - values: { samlResponse }, - }, + params: { values: samlResponse }, } = ctx.action; const saml = new SAML(this.getOptions());