Search code examples
expressherokusafariangular-ui-routerstripe-connect

Node Express redirect not working in Safari in Production


This route redirect is doing exactly what I want in Chrome and Firefox, but for some reason, it is not working in Safari in the production environment:

var express = require('express');
var router = express.Router();
var pool = require('../modules/pg-pool');

router.get('/', async (req, res) => {
    var client = await pool.connect();
    try {
        var stripeConnectState = req.query.state;
        var stripeConnectVendorIdResult = await client.query('SELECT vendor_id ' +
            'FROM users_vendors ' +
            'WHERE stripe_connect_state=$1;',
            [stripeConnectState])
        client.release();
        if (stripeConnectVendorIdResult.rows[0] && stripeConnectVendorIdResult.rows[0].vendor_id) {
            var stripeConnectVendorId = stripeConnectVendorIdResult.rows[0].vendor_id;
        }
        if (stripeConnectVendorId) {
            var redirectUrl = [req.protocol, '://', req.get('Host'), '/#/account/vendor/details/', stripeConnectVendorId, '?', req.originalUrl.split("?").pop()].join('');
            res.redirect(redirectUrl);
        } else {
            console.log('There was no vendor id to match the stripe state received');
            res.sendStatus(403);
        }
    } catch (e) {
        console.log('Error vendor id GET SQL query task', e);
        res.sendStatus(500);
    }
});

module.exports = router;

The craziest part to me is that when I I tested locally in Safari, it worked! For some reason, when I deploy to Heroku, it no longer works in production for Safari.

When I test locally, it is redirecting to

http://localhost:5000/#/account/vendor/details/7?state=XXXXXXXXXXXXXXXXXXXXXXXX&scope=read_write&code=ac_XXXXXXXXXXXXXXXXXX

But in production, it is redirecting to the base url

https://www.fairlywed.com/

without the incredibly important query parameters sent along by Stripe. This almost seems like I have a race condition on my hands that never triggers in Chrome or Firefox, but always triggers in Safari.

I'm also wondering if my use of angular-ui-router or the fact that stripe is directing to my site might be involved, but none of those really make a whole lot of sense to me. I might just be grasping at straws at this point.


Solution

  • I found a band-aid fix, but I'm still not sure why this is happening. On production, the req.protocol was coming in as http which is handled by another part of my code:

    function redirectChecker (req, res, next) {
        if (env === 'production') {
            if (req.headers['x-forwarded-proto'] !== 'https') {
                var urlAfterConversion = ['https://', req.get('Host'), req.url].join('');
                return res.redirect(urlAfterConversion);
            }
        }
        return next();
    };
    

    For some reason on Safari, that redirect doesn't work (maybe Safari doesn't accept a redirect followed by a redirect?)

    So it failed and redirected to my landing page. I'm still not sure why it solved the problem, but this code is working:

    if (stripeConnectVendorId) {
        var env = process.env.NODE_ENV || 'development';
        var redirectUrl = [req.protocol, '://', req.get('Host'), '/#/account/vendor/details/', stripeConnectVendorId, '?', req.originalUrl.split("?").pop()].join('');
        if (env === 'production') {
           redirectUrl = 'https:' + redirectUrl.split(':')[1];
        }
        res.redirect(redirectUrl);
    } else {
        console.log('There was no vendor id to match the stripe state received');
        res.sendStatus(403);
    }