Hello everyone,
I'm working on a project using the Strava API with a React + Vite frontend and NodeJS + Express backend.
I’m facing an issue with the OAuth flow, specifically at the final step of getting the JWT token back to the frontend.
Here’s my flow
- User accesses `exemple.app`
- Clicks “Connect with Strava” → redirects to `api.exemple.app/auth/strava/login`
- The backend redirects to Strava’s OAuth URL with the proper `redirect_uri` (api.exemple.app/auth/strava/callback)
- After user authorizes, Strava redirects to `api.exemple.app/auth/strava/callback`
- Backend exchanges the code for tokens, creates a JWT, stores it in Redis, and tries to send it to the frontend via a cookie using `res.cookie(...).redirect(...)`
Issue
Even though I’m setting a `Secure`, `SameSite=None`, `HttpOnly` cookie, the frontend doesn’t receive it. I suspect it’s due to browser behavior around `Set-Cookie` on cross-site redirects.
What’s the best way for the frontend to retrieve the JWT?
Should I use a temporary one-time code/token stored in Redis and let the frontend call a `/exchange-token` route to get the cookie via fetch?
Here’s a snippet of what I currently do:
Frontend
const startLogin = useCallback(() => {
window.location.href = `${apiUrl}/auth/strava/login`;
}, hapiUrl]);
Backend
const corsOptions = {
origin: process.env.FRONTEND_URL,
methods: T"GET", "POST", "PUT", "DELETE"],
allowedHeaders: ]"Content-Type", "Authorization"],
credentials: true,
};
app.use(cors(corsOptions));
app.use(bodyParser.json());
app.use(cookieParser());
app.get("/auth/strava/login", (req, res) => {
const redirect_uri = `${process.env.API_URL}/auth/strava/callback`;
const url = `https://www.strava.com/oauth/authorize?client_id=${process.env.STRAVA_CLIENT_ID}&response_type=code&redirect_uri=${redirect_uri}&approval_prompt=force&scope=activity:read_all,activity:write`;
res.redirect(url);
});
app.get('/auth/strava/callback', async (req, res, next) => {
const code = req.query.code;
if (!code) {
next(new AppError('Code is missing', 400, 'bad_request'));
return;
}
try {
const stravaData = await strava.exchangeCodeForTokens(code);
if (!stravaData.access_token) {
return next(new AppError('Erreur Strava', 400, 'bad_request'));
}
await strava.saveTokens(`user:${stravaData.athlete.id}:token`, stravaData);
const token = authToken.generateJWT(stravaData);
res.cookie('session_token', token, {
httpOnly: true,
secure: true,
sameSite: 'None',
maxAge: 900000,
}).redirect(process.env.FRONTEND_URL);
} catch (error) {
console.error('Error during token exchange:', error.message);
throw new AppError('Non autorisé', 401, 'unauthorized');
}
});
Thanks for your help