passport saml -- Error: Cannot set headers after they are sent to the client

76 views Asked by At

After upgrading from Passport 0.5.2 to 0.6.0, our application behaves weird. I tried to isolate the code that is broken. Unfortunally, I wasn't able to reproduce it in my sample. But it should give an idea what our larger app is doing. Why might the await not "await"... I see the next midddleware called before the one with the async/await finishes. So of course we get the error "Error: Cannot set headers after they are sent to the client" because it continues on and then we try to redirect.

const express = require('express');
const app = express();
const session = require('express-session');
const port = 8080;
const passport = require('passport');
const SamlStrategy = require('@node-saml/passport-saml').Strategy;
const bodyParser = require("body-parser");
const cookieParser = require('cookie-parser');

app.use(bodyParser.json({ type: ['text/plain', 'application/json'] }));
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());

app.use(session({
    secret: 'keyboard cat',
    resave: false,
    saveUninitialized: true,
    cookie: { secure: true }
}))
app.use(passport.initialize());
app.use(passport.session());

const samlStrategy = new SamlStrategy(
    {
        "path": "/login/callback",
        "entryPoint": "https://xxxx.oktapreview.com/app/xxxxx1/XXX/sso/saml",
        "issuer": "xxx",
        "cert": "XXXXXXXX"
    },
    (profile, done) => {
        return done(null, profile);
    }
);
passport.use(samlStrategy);

passport.serializeUser((user, done) => {
    done(null, user);
});

passport.deserializeUser((user, done) => {
    done(null, user);
});


app.get('/login', (req, res, next) => {
    const destination = '/';

    passport.authenticate('saml', {
        additionalParams: { 'RelayState': JSON.stringify({ destination }) },
        failureRedirect: '/login'
    })(req, res, next);
});

app.use('/login/callback',
    passport.authenticate(
        'saml', {
        failureRedirect: '/login'
    }
),
async (request, response, next) => {
    console.log("in second")

    const {
        user: { firstName, emailAddress, emailaddress, nameID },
        body: { RelayState: relayState = '{}' } = {}
    } = request;
    const email = emailAddress || emailaddress || nameID;
    const user = {
        firstName,
        email,
        nameID
    };

    // store a new user object
    request.session.user = user;


    try {
        console.log("in try");
        await new Promise((resolve, reject) => setTimeout(resolve, 13000));
        console.log("after promise");
    } catch (error) {
        console.log("in catch", error);
        // swallow it
    }

    const destination = '/';
    console.log("before redirect");
    response.redirect(destination);
    console.log('after redirect');
},
(    request, response, next) => {
        console.log("don't come down here, you redirected!");
        next();
    }
);
app.get('/', (req, res) => {
    res.send('Hello World!')
});

app.listen(port, () => {
    console.log(`Example app listening on port ${port}`)
});
0

There are 0 answers