fix(auth): redirect URL after signing in by SSO sucessfully (#3387)
* fix: fix T-2900 * fix: saml
This commit is contained in:
parent
88dd3dd569
commit
dc71a77d8c
@ -6,17 +6,17 @@ import { getSubAppName } from '@nocobase/sdk';
|
|||||||
import { Authenticator } from '@nocobase/plugin-auth/client';
|
import { Authenticator } from '@nocobase/plugin-auth/client';
|
||||||
|
|
||||||
export const SigninPage = (props: { authenticator: Authenticator }) => {
|
export const SigninPage = (props: { authenticator: Authenticator }) => {
|
||||||
const location = useLocation();
|
|
||||||
|
|
||||||
const authenticator = props.authenticator;
|
const authenticator = props.authenticator;
|
||||||
|
const location = useLocation();
|
||||||
|
const params = new URLSearchParams(location.search);
|
||||||
|
const redirect = params.get('redirect');
|
||||||
|
|
||||||
const app = getSubAppName() || 'main';
|
const app = getSubAppName() || 'main';
|
||||||
const login = async () => {
|
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(() => {
|
useEffect(() => {
|
||||||
const params = new URLSearchParams(location.search);
|
|
||||||
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) {
|
||||||
|
@ -9,7 +9,7 @@ export class SamlPlugin extends Plugin {
|
|||||||
const auth = this.app.pm.get(AuthPlugin);
|
const auth = this.app.pm.get(AuthPlugin);
|
||||||
auth.registerType(authType, {
|
auth.registerType(authType, {
|
||||||
components: {
|
components: {
|
||||||
SignInForm: SigninPage,
|
SignInButton: SigninPage,
|
||||||
AdminSettingsForm: Options,
|
AdminSettingsForm: Options,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -2,10 +2,12 @@ import { Context, Next } from '@nocobase/actions';
|
|||||||
import { CASAuth } from '../auth';
|
import { CASAuth } from '../auth';
|
||||||
|
|
||||||
export const login = async (ctx: Context, next: Next) => {
|
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 auth = (await ctx.app.authManager.get(authenticator, ctx)) as CASAuth;
|
||||||
const { casUrl, serviceUrl } = auth.getOptions();
|
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}`);
|
ctx.redirect(`${casUrl}/login?service=${service}`);
|
||||||
next();
|
next();
|
||||||
};
|
};
|
||||||
|
@ -3,7 +3,7 @@ import { AppSupervisor } from '@nocobase/server';
|
|||||||
import { CASAuth } from '../auth';
|
import { CASAuth } from '../auth';
|
||||||
|
|
||||||
export const service = async (ctx: Context, next: Next) => {
|
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 = '';
|
let prefix = '';
|
||||||
if (appName && appName !== 'main') {
|
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;
|
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}/admin?authenticator=${authenticator}&token=${token}`);
|
ctx.redirect(`${prefix}${redirect}?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}&redirect=${redirect}`);
|
||||||
}
|
}
|
||||||
return next();
|
return next();
|
||||||
};
|
};
|
||||||
|
@ -15,6 +15,8 @@ export const OIDCButton = ({ authenticator }: { authenticator: Authenticator })
|
|||||||
const { t } = useOidcTranslation();
|
const { t } = useOidcTranslation();
|
||||||
const api = useAPIClient();
|
const api = useAPIClient();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
|
const params = new URLSearchParams(location.search);
|
||||||
|
const redirect = params.get('redirect');
|
||||||
|
|
||||||
const login = async () => {
|
const login = async () => {
|
||||||
const response = await api.request({
|
const response = await api.request({
|
||||||
@ -23,6 +25,9 @@ export const OIDCButton = ({ authenticator }: { authenticator: Authenticator })
|
|||||||
headers: {
|
headers: {
|
||||||
'X-Authenticator': authenticator.name,
|
'X-Authenticator': authenticator.name,
|
||||||
},
|
},
|
||||||
|
data: {
|
||||||
|
redirect,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const authUrl = response?.data?.data;
|
const authUrl = response?.data?.data;
|
||||||
@ -30,7 +35,6 @@ export const OIDCButton = ({ authenticator }: { authenticator: Authenticator })
|
|||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const params = new URLSearchParams(location.search);
|
|
||||||
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) {
|
||||||
|
@ -4,6 +4,7 @@ import { nanoid } from 'nanoid';
|
|||||||
import { cookieName } from '../../constants';
|
import { cookieName } from '../../constants';
|
||||||
|
|
||||||
export const getAuthUrl = async (ctx: Context, next: Next) => {
|
export const getAuthUrl = async (ctx: Context, next: Next) => {
|
||||||
|
const { redirect = '' } = ctx.action.params.values || {};
|
||||||
const app = ctx.app.name;
|
const app = ctx.app.name;
|
||||||
const auth = ctx.auth as OIDCAuth;
|
const auth = ctx.auth as OIDCAuth;
|
||||||
const client = await auth.createOIDCClient();
|
const client = await auth.createOIDCClient();
|
||||||
@ -16,8 +17,8 @@ export const getAuthUrl = async (ctx: Context, next: Next) => {
|
|||||||
ctx.body = client.authorizationUrl({
|
ctx.body = client.authorizationUrl({
|
||||||
response_type: 'code',
|
response_type: 'code',
|
||||||
scope: scope || 'openid email profile',
|
scope: scope || 'openid email profile',
|
||||||
redirect_uri: auth.getRedirectUri(),
|
redirect_uri: `${auth.getRedirectUri()}`,
|
||||||
state: encodeURIComponent(`token=${token}&name=${ctx.headers['x-authenticator']}&app=${app}`),
|
state: encodeURIComponent(`token=${token}&name=${ctx.headers['x-authenticator']}&app=${app}&redirect=${redirect}`),
|
||||||
});
|
});
|
||||||
|
|
||||||
return next();
|
return next();
|
||||||
|
@ -9,6 +9,7 @@ export const redirect = async (ctx: Context, next: Next) => {
|
|||||||
const search = new URLSearchParams(decodeURIComponent(state));
|
const search = new URLSearchParams(decodeURIComponent(state));
|
||||||
const authenticator = search.get('name');
|
const authenticator = search.get('name');
|
||||||
const appName = search.get('app');
|
const appName = search.get('app');
|
||||||
|
const redirect = search.get('redirect') || '/admin';
|
||||||
let prefix = '';
|
let prefix = '';
|
||||||
if (appName && appName !== 'main') {
|
if (appName && appName !== 'main') {
|
||||||
const appSupervisor = AppSupervisor.getInstance();
|
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;
|
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}/admin?authenticator=${authenticator}&token=${token}`);
|
ctx.redirect(`${prefix}${redirect}?authenticator=${authenticator}&token=${token}`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
ctx.logger.error('OIDC auth error', { 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();
|
await next();
|
||||||
};
|
};
|
||||||
|
@ -10,6 +10,8 @@ export const SAMLButton = ({ authenticator }: { authenticator: Authenticator })
|
|||||||
const { t } = useSamlTranslation();
|
const { t } = useSamlTranslation();
|
||||||
const api = useAPIClient();
|
const api = useAPIClient();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
|
const params = new URLSearchParams(location.search);
|
||||||
|
const redirect = params.get('redirect');
|
||||||
|
|
||||||
const login = async () => {
|
const login = async () => {
|
||||||
const response = await api.request({
|
const response = await api.request({
|
||||||
@ -18,6 +20,9 @@ export const SAMLButton = ({ authenticator }: { authenticator: Authenticator })
|
|||||||
headers: {
|
headers: {
|
||||||
'X-Authenticator': authenticator.name,
|
'X-Authenticator': authenticator.name,
|
||||||
},
|
},
|
||||||
|
data: {
|
||||||
|
redirect,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const authUrl = response?.data?.data;
|
const authUrl = response?.data?.data;
|
||||||
@ -25,7 +30,6 @@ export const SAMLButton = ({ authenticator }: { authenticator: Authenticator })
|
|||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const params = new URLSearchParams(location.search);
|
|
||||||
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) {
|
||||||
|
@ -3,11 +3,12 @@ import { SAML } from '@node-saml/node-saml';
|
|||||||
import { SAMLAuth } from '../saml-auth';
|
import { SAMLAuth } from '../saml-auth';
|
||||||
|
|
||||||
export const getAuthUrl = async (ctx: Context, next: Next) => {
|
export const getAuthUrl = async (ctx: Context, next: Next) => {
|
||||||
|
const { redirect = '' } = ctx.action.params.values || {};
|
||||||
const auth = ctx.auth as SAMLAuth;
|
const auth = ctx.auth as SAMLAuth;
|
||||||
const options = auth.getOptions();
|
const options = auth.getOptions();
|
||||||
const saml = new SAML(options);
|
const saml = new SAML(options);
|
||||||
|
|
||||||
ctx.body = await saml.getAuthorizeUrlAsync('', '', {});
|
ctx.body = await saml.getAuthorizeUrlAsync(redirect, '', {});
|
||||||
|
|
||||||
return next();
|
return next();
|
||||||
};
|
};
|
||||||
|
@ -4,6 +4,7 @@ import { AppSupervisor } from '@nocobase/server';
|
|||||||
|
|
||||||
export const redirect = async (ctx: Context, next: Next) => {
|
export const redirect = async (ctx: Context, next: Next) => {
|
||||||
const { authenticator, __appName: appName } = ctx.action.params || {};
|
const { authenticator, __appName: appName } = ctx.action.params || {};
|
||||||
|
const { RelayState: redirect = '/admin' } = ctx.action.params.values || {};
|
||||||
let prefix = '';
|
let prefix = '';
|
||||||
if (appName && appName !== 'main') {
|
if (appName && appName !== 'main') {
|
||||||
const appSupervisor = AppSupervisor.getInstance();
|
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;
|
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}/admin?authenticator=${authenticator}&token=${token}`);
|
ctx.redirect(`${prefix}${redirect}?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}&redirect=${redirect}`);
|
||||||
}
|
}
|
||||||
await next();
|
await next();
|
||||||
};
|
};
|
||||||
|
@ -18,19 +18,19 @@ export class SAMLAuth extends BaseAuth {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getOptions() {
|
getOptions(): SamlConfig {
|
||||||
const ctx = this.ctx;
|
const ctx = this.ctx;
|
||||||
const { ssoUrl, certificate, idpIssuer, http }: SAMLOptions = this.options?.saml || {};
|
const { ssoUrl, certificate, idpIssuer, http }: SAMLOptions = this.options?.saml || {};
|
||||||
const name = this.authenticator.get('name');
|
const name = this.authenticator.get('name');
|
||||||
const protocol = http ? 'http' : 'https';
|
const protocol = http ? 'http' : 'https';
|
||||||
return {
|
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,
|
entryPoint: ssoUrl,
|
||||||
issuer: name,
|
issuer: name,
|
||||||
cert: certificate,
|
cert: certificate,
|
||||||
idpIssuer,
|
idpIssuer,
|
||||||
wantAssertionsSigned: false,
|
wantAssertionsSigned: false,
|
||||||
} as SamlConfig;
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async validate() {
|
async validate() {
|
||||||
|
Loading…
Reference in New Issue
Block a user