feat: support bi-directional backchannel SAML SLO

This commit is contained in:
Ali BARIN
2024-05-03 08:28:53 +00:00
parent 40d0fe0db6
commit 3da5e13ecd
6 changed files with 137 additions and 14 deletions

View File

@@ -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,
}),
);
}