feat: support bi-directional backchannel SAML SLO
This commit is contained in:
@@ -5,8 +5,11 @@ import passport from 'passport';
|
||||
import appConfig from '../config/app.js';
|
||||
import createAuthTokenByUserId from './create-auth-token-by-user-id.js';
|
||||
import SamlAuthProvider from '../models/saml-auth-provider.ee.js';
|
||||
import AccessToken from '../models/access-token.js';
|
||||
import findOrCreateUserBySamlIdentity from './find-or-create-user-by-saml-identity.ee.js';
|
||||
|
||||
const asyncNoop = async () => { };
|
||||
|
||||
export default function configurePassport(app) {
|
||||
app.use(
|
||||
passport.initialize({
|
||||
@@ -19,6 +22,10 @@ export default function configurePassport(app) {
|
||||
{
|
||||
passReqToCallback: true,
|
||||
getSamlOptions: async function (request, done) {
|
||||
// This is a workaround to avoid session logout which passport-saml enforces
|
||||
request.logout = asyncNoop;
|
||||
request.logOut = asyncNoop;
|
||||
|
||||
const { issuer } = request.params;
|
||||
const notFoundIssuer = new Error('Issuer cannot be found!');
|
||||
|
||||
@@ -35,7 +42,7 @@ export default function configurePassport(app) {
|
||||
return done(null, authProvider.config);
|
||||
},
|
||||
},
|
||||
async function (request, user, done) {
|
||||
async function signonVerify(request, user, done) {
|
||||
const { issuer } = request.params;
|
||||
const notFoundIssuer = new Error('Issuer cannot be found!');
|
||||
|
||||
@@ -53,10 +60,38 @@ export default function configurePassport(app) {
|
||||
user,
|
||||
authProvider
|
||||
);
|
||||
|
||||
request.samlSessionId = user.sessionIndex;
|
||||
|
||||
return done(null, foundUserWithIdentity);
|
||||
},
|
||||
function (request, user, done) {
|
||||
return done(null, null);
|
||||
async function logoutVerify(request, user, done) {
|
||||
const { issuer } = request.params;
|
||||
const notFoundIssuer = new Error('Issuer cannot be found!');
|
||||
|
||||
if (!issuer) return done(notFoundIssuer);
|
||||
|
||||
const authProvider = await SamlAuthProvider.query().findOne({
|
||||
issuer: request.params.issuer,
|
||||
});
|
||||
|
||||
if (!authProvider) {
|
||||
return done(notFoundIssuer);
|
||||
}
|
||||
|
||||
const foundUserWithIdentity = await findOrCreateUserBySamlIdentity(
|
||||
user,
|
||||
authProvider
|
||||
);
|
||||
|
||||
const accessToken = await AccessToken.query().findOne({
|
||||
revoked_at: null,
|
||||
saml_session_id: user.sessionIndex,
|
||||
}).throwIfNotFound();
|
||||
|
||||
await accessToken.revoke();
|
||||
|
||||
return done(null, foundUserWithIdentity);
|
||||
}
|
||||
)
|
||||
);
|
||||
@@ -73,17 +108,22 @@ export default function configurePassport(app) {
|
||||
'/login/saml/:issuer/callback',
|
||||
passport.authenticate('saml', {
|
||||
session: false,
|
||||
failureRedirect: '/',
|
||||
failureFlash: true,
|
||||
}),
|
||||
async (req, res) => {
|
||||
const token = await createAuthTokenByUserId(req.currentUser.id);
|
||||
async (request, response) => {
|
||||
const token = await createAuthTokenByUserId(request.currentUser.id, request.samlSessionId);
|
||||
|
||||
const redirectUrl = new URL(
|
||||
`/login/callback?token=${token}`,
|
||||
appConfig.webAppUrl
|
||||
).toString();
|
||||
res.redirect(redirectUrl);
|
||||
response.redirect(redirectUrl);
|
||||
}
|
||||
);
|
||||
|
||||
app.post(
|
||||
'/logout/saml/:issuer',
|
||||
passport.authenticate('saml', {
|
||||
session: false,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
Reference in New Issue
Block a user