fix(auth): redirect URL after signing in by SSO sucessfully (#3387)

* fix: fix T-2900

* fix: saml
This commit is contained in:
YANG QIA 2024-01-14 22:26:13 +08:00 committed by GitHub
parent 88dd3dd569
commit dc71a77d8c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 36 additions and 22 deletions

View File

@ -6,17 +6,17 @@ import { getSubAppName } from '@nocobase/sdk';
import { Authenticator } from '@nocobase/plugin-auth/client';
export const SigninPage = (props: { authenticator: Authenticator }) => {
const location = useLocation();
const authenticator = props.authenticator;
const location = useLocation();
const params = new URLSearchParams(location.search);
const redirect = params.get('redirect');
const app = getSubAppName() || 'main';
const login = async () => {
window.location.replace(`/api/cas:login?authenticator=${authenticator.name}&__appName=${app}`);
window.location.replace(`/api/cas:login?authenticator=${authenticator.name}&__appName=${app}&redirect=${redirect}`);
};
useEffect(() => {
const params = new URLSearchParams(location.search);
const name = params.get('authenticator');
const error = params.get('error');
if (name !== authenticator.name) {

View File

@ -9,7 +9,7 @@ export class SamlPlugin extends Plugin {
const auth = this.app.pm.get(AuthPlugin);
auth.registerType(authType, {
components: {
SignInForm: SigninPage,
SignInButton: SigninPage,
AdminSettingsForm: Options,
},
});

View File

@ -2,10 +2,12 @@ import { Context, Next } from '@nocobase/actions';
import { CASAuth } from '../auth';
export const login = async (ctx: Context, next: Next) => {
const { authenticator } = ctx.action.params;
const { authenticator, redirect = '' } = ctx.action.params;
const auth = (await ctx.app.authManager.get(authenticator, ctx)) as CASAuth;
const { casUrl, serviceUrl } = auth.getOptions();
const service = encodeURIComponent(`${serviceUrl}?authenticator=${authenticator}&__appName=${ctx.app.name}`);
const service = encodeURIComponent(
`${serviceUrl}?authenticator=${authenticator}&__appName=${ctx.app.name}&redirect=${redirect}`,
);
ctx.redirect(`${casUrl}/login?service=${service}`);
next();
};

View File

@ -3,7 +3,7 @@ import { AppSupervisor } from '@nocobase/server';
import { CASAuth } from '../auth';
export const service = async (ctx: Context, next: Next) => {
const { authenticator, __appName: appName } = ctx.action.params;
const { authenticator, __appName: appName, redirect = '/admin' } = ctx.action.params;
let prefix = '';
if (appName && appName !== 'main') {
@ -16,9 +16,9 @@ export const service = async (ctx: Context, next: Next) => {
const auth = (await ctx.app.authManager.get(authenticator, ctx)) as CASAuth;
try {
const { token } = await auth.signIn();
ctx.redirect(`${prefix}/admin?authenticator=${authenticator}&token=${token}`);
ctx.redirect(`${prefix}${redirect}?authenticator=${authenticator}&token=${token}`);
} catch (error) {
ctx.redirect(`${prefix}/signin?authenticator=${authenticator}&error=${error.message}`);
ctx.redirect(`${prefix}/signin?authenticator=${authenticator}&error=${error.message}&redirect=${redirect}`);
}
return next();
};

View File

@ -15,6 +15,8 @@ export const OIDCButton = ({ authenticator }: { authenticator: Authenticator })
const { t } = useOidcTranslation();
const api = useAPIClient();
const location = useLocation();
const params = new URLSearchParams(location.search);
const redirect = params.get('redirect');
const login = async () => {
const response = await api.request({
@ -23,6 +25,9 @@ export const OIDCButton = ({ authenticator }: { authenticator: Authenticator })
headers: {
'X-Authenticator': authenticator.name,
},
data: {
redirect,
},
});
const authUrl = response?.data?.data;
@ -30,7 +35,6 @@ export const OIDCButton = ({ authenticator }: { authenticator: Authenticator })
};
useEffect(() => {
const params = new URLSearchParams(location.search);
const name = params.get('authenticator');
const error = params.get('error');
if (name !== authenticator.name) {

View File

@ -4,6 +4,7 @@ import { nanoid } from 'nanoid';
import { cookieName } from '../../constants';
export const getAuthUrl = async (ctx: Context, next: Next) => {
const { redirect = '' } = ctx.action.params.values || {};
const app = ctx.app.name;
const auth = ctx.auth as OIDCAuth;
const client = await auth.createOIDCClient();
@ -16,8 +17,8 @@ export const getAuthUrl = async (ctx: Context, next: Next) => {
ctx.body = client.authorizationUrl({
response_type: 'code',
scope: scope || 'openid email profile',
redirect_uri: auth.getRedirectUri(),
state: encodeURIComponent(`token=${token}&name=${ctx.headers['x-authenticator']}&app=${app}`),
redirect_uri: `${auth.getRedirectUri()}`,
state: encodeURIComponent(`token=${token}&name=${ctx.headers['x-authenticator']}&app=${app}&redirect=${redirect}`),
});
return next();

View File

@ -9,6 +9,7 @@ export const redirect = async (ctx: Context, next: Next) => {
const search = new URLSearchParams(decodeURIComponent(state));
const authenticator = search.get('name');
const appName = search.get('app');
const redirect = search.get('redirect') || '/admin';
let prefix = '';
if (appName && appName !== 'main') {
const appSupervisor = AppSupervisor.getInstance();
@ -19,10 +20,10 @@ export const redirect = async (ctx: Context, next: Next) => {
const auth = (await ctx.app.authManager.get(authenticator, ctx)) as OIDCAuth;
try {
const { token } = await auth.signIn();
ctx.redirect(`${prefix}/admin?authenticator=${authenticator}&token=${token}`);
ctx.redirect(`${prefix}${redirect}?authenticator=${authenticator}&token=${token}`);
} catch (error) {
ctx.logger.error('OIDC auth error', { error });
ctx.redirect(`${prefix}/signin?authenticator=${authenticator}&error=${error.message}`);
ctx.redirect(`${prefix}/signin?redirect=${redirect}&authenticator=${authenticator}&error=${error.message}`);
}
await next();
};

View File

@ -10,6 +10,8 @@ export const SAMLButton = ({ authenticator }: { authenticator: Authenticator })
const { t } = useSamlTranslation();
const api = useAPIClient();
const location = useLocation();
const params = new URLSearchParams(location.search);
const redirect = params.get('redirect');
const login = async () => {
const response = await api.request({
@ -18,6 +20,9 @@ export const SAMLButton = ({ authenticator }: { authenticator: Authenticator })
headers: {
'X-Authenticator': authenticator.name,
},
data: {
redirect,
},
});
const authUrl = response?.data?.data;
@ -25,7 +30,6 @@ export const SAMLButton = ({ authenticator }: { authenticator: Authenticator })
};
useEffect(() => {
const params = new URLSearchParams(location.search);
const name = params.get('authenticator');
const error = params.get('error');
if (name !== authenticator.name) {

View File

@ -3,11 +3,12 @@ import { SAML } from '@node-saml/node-saml';
import { SAMLAuth } from '../saml-auth';
export const getAuthUrl = async (ctx: Context, next: Next) => {
const { redirect = '' } = ctx.action.params.values || {};
const auth = ctx.auth as SAMLAuth;
const options = auth.getOptions();
const saml = new SAML(options);
ctx.body = await saml.getAuthorizeUrlAsync('', '', {});
ctx.body = await saml.getAuthorizeUrlAsync(redirect, '', {});
return next();
};

View File

@ -4,6 +4,7 @@ import { AppSupervisor } from '@nocobase/server';
export const redirect = async (ctx: Context, next: Next) => {
const { authenticator, __appName: appName } = ctx.action.params || {};
const { RelayState: redirect = '/admin' } = ctx.action.params.values || {};
let prefix = '';
if (appName && appName !== 'main') {
const appSupervisor = AppSupervisor.getInstance();
@ -14,9 +15,9 @@ export const redirect = async (ctx: Context, next: Next) => {
const auth = (await ctx.app.authManager.get(authenticator, ctx)) as SAMLAuth;
try {
const { token } = await auth.signIn();
ctx.redirect(`${prefix}/admin?authenticator=${authenticator}&token=${token}`);
ctx.redirect(`${prefix}${redirect}?authenticator=${authenticator}&token=${token}`);
} catch (error) {
ctx.redirect(`${prefix}/signin?authenticator=${authenticator}&error=${error.message}`);
ctx.redirect(`${prefix}/signin?authenticator=${authenticator}&error=${error.message}&redirect=${redirect}`);
}
await next();
};

View File

@ -18,19 +18,19 @@ export class SAMLAuth extends BaseAuth {
});
}
getOptions() {
getOptions(): SamlConfig {
const ctx = this.ctx;
const { ssoUrl, certificate, idpIssuer, http }: SAMLOptions = this.options?.saml || {};
const name = this.authenticator.get('name');
const protocol = http ? 'http' : 'https';
return {
callbackUrl: `${protocol}://${ctx.host}/api/saml:redirect?authenticator=${name}&app=${ctx.app.name}`,
callbackUrl: `${protocol}://${ctx.host}/api/saml:redirect?authenticator=${name}&__appName=${ctx.app.name}`,
entryPoint: ssoUrl,
issuer: name,
cert: certificate,
idpIssuer,
wantAssertionsSigned: false,
} as SamlConfig;
};
}
async validate() {