You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PassportJS+ADFS认证遇CORS预检请求无效问题(附代码)

Hey there, let's break down why you're hitting that "Response for preflight requests are invalid" error and fix it step by step.

1. Core Issues Identified

  • Conflicting CORS Logic: You're using both the cors() middleware and a custom manual CORS handler, which creates inconsistent header responses for preflight (OPTIONS) requests.
  • Incorrect OAuth2 Flow Initiation: You're trying to start the ADFS authentication flow via an AJAX (axios) request—but this flow requires a full browser redirect to the ADFS authorization page. AJAX can't handle cross-domain redirects properly here, which triggers CORS blocks.
  • Misplaced Request Headers: The Access-Control-* headers you're sending from the client are meant to be returned by the server, not included in your outgoing request.

2. Server-Side Fixes

First, clean up your CORS configuration and align it with the OAuth2 flow requirements:

'use strict'; 
var app = require('express')(),
cookieParser = require('cookie-parser'),
jwt = require('jsonwebtoken'),
passport = require('passport'),
OAuth2Strategy = require('passport-oauth').OAuth2Strategy,
fs = require('fs');
var cors = require('cors');
var https = require('https');

console.warn('Not verifying HTTPS certificates');
https.globalAgent.options.rejectUnauthorized = false;

// Exported from ADFS
var adfsSigningPublicKey = fs.readFileSync('ADFS-Signing.cer','utf8');
var cert = convertCertificate(adfsSigningPublicKey);

function validateAccessToken(accessToken) {
var payload = null;
try {
payload = jwt.verify(accessToken, cert,{algorithms: ["HS256"], ignoreExpiration: true});
} catch(e) {
console.warn('Dropping unverified accessToken', e);
}
return payload;
}

function convertCertificate (cert) {
//Certificate must be in this specific format or else the function won't accept it
var beginCert = "-----BEGIN CERTIFICATE-----";
var endCert = "-----END CERTIFICATE-----";
cert = cert.replace("\n", "");
cert = cert.replace(beginCert, "");
cert = cert.replace(endCert, "");
var result = beginCert;
while (cert.length > 0) {
if (cert.length > 64) {
result += "\n" + cert.substring(0, 64);
cert = cert.substring(64, cert.length);
} else {
result += "\n" + cert;
cert = "";
}
}
if (result[result.length ] != "\n") result += "\n";
result += endCert + "\n";
return result;
}

// Configure passport to integrate with ADFS
var strategy = new OAuth2Strategy({
authorizationURL: 'https://sso.xxx.com/adfs/oauth2/authorize',
tokenURL: 'https://sso.xxx.com/adfs/oauth2/token',
clientID: 'xxxxxxxx-xxxx-xxxx-xxxx-0cxxx4489fa', 
clientSecret: 'shhh-its-a-secret', 
callbackURL: 'http://localhost:3000/getAToken'
}, function(accessToken, refreshToken, profile, done) {
if (refreshToken) {
console.log('Received but ignoring refreshToken (truncated)', refreshToken.substr(0, 25));
} else {
console.log('No refreshToken received');
}
console.log("done ** " + profile);
done(null, profile);
});

strategy.authorizationParams = function(options) {
return { resource: 'iggggggg' 
};
};

strategy.userProfile = function(accessToken, done) {
done(null, accessToken);
};

passport.use('provider', strategy);

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

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

// Configure express app - Clean up CORS config
app.use(cookieParser());
// Centralized CORS configuration to handle preflight requests automatically
const corsOptions = {
  origin: 'http://localhost:3000',
  credentials: true,
  methods: ['GET', 'POST', 'OPTIONS'],
  allowedHeaders: ['Content-Type', 'X-Requested-With']
};
app.use(cors(corsOptions));

app.use(passport.initialize());

// OAuth2 login route - designed for browser redirects, not AJAX
app.get('/login', passport.authenticate('provider'));

app.get('/getAToken', passport.authenticate('provider'), function(req, res) {
console.log("*********************************");
// Add httpOnly flag for better security (prevents XSS access to the cookie)
res.cookie('accessToken', req.user, { httpOnly: true, secure: false }); 
// Redirect back to your client app's origin after authentication
res.redirect('http://localhost:3000'); 
});

app.get('/', function (req, res) {
console.log('default is called');
req.user = validateAccessToken(req.cookies['accessToken']);
res.send( !req.user ? 'Log In' : 'Log Out' + ' ' + JSON.stringify(req.user, null, 2) + ' ');
});

app.listen(3000);
console.log('Express server started on port 3000');

Key server improvements:

  • Removed conflicting manual CORS code and replaced it with a properly configured cors() middleware that handles OPTIONS requests automatically.
  • Added httpOnly to the access token cookie to reduce XSS vulnerability risks.
  • Explicitly redirects back to your client's origin after the ADFS callback completes.

3. Client-Side Fixes

You can't initiate the OAuth2 flow via AJAX—instead, trigger a full browser navigation to the /login route. Here's the corrected client code:

// Replace the axios AJAX call with a browser redirect to start the auth flow
document.location.href = 'http://localhost:3000/login';

// On page load, check if the authentication cookie exists to confirm login status
window.addEventListener('load', () => {
  const isAuthenticated = document.cookie.includes('accessToken');
  if (isAuthenticated) {
    console.log('User is logged in - proceed with authenticated features');
  } else {
    console.log('User needs to log in');
  }
});

Key client changes:

  • Axios AJAX is replaced with a simple page redirect, which lets the OAuth2 flow work as designed (browser navigates to ADFS, then back to your callback URL).
  • Added a page load check to detect authentication status using the cookie.

4. Why This Works

  • The centralized cors() middleware now returns consistent, valid headers for preflight requests, eliminating the "invalid preflight response" error.
  • Using a browser redirect instead of AJAX bypasses CORS restrictions for cross-domain auth flows—top-level navigation to external auth providers is allowed by browsers, unlike AJAX requests.

内容的提问来源于stack exchange,提问作者user25010

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 06:41:33