我有一个 React 应用程序。
我还有一个 Express 服务器,使用 passport-saml
我可以根据公司的 PingID SSO IdP 进行身份验证。
我想要获取 React 应用程序,以某种方式调用 Express 应用程序,进行身份验证。
Express Server 和 React 应用程序在同一主机上运行。
这是我所拥有的:
// Express App - rendering code pulled out
const express = require('express');
var passport = require('passport');
var Strategy = require('passport-saml').Strategy;
var fs = require('fs')
const bodyParser = require('body-parser');
const cookieParser = require('cookie-parser');
const expressSession = require('express-session');
const app = express();
const port = process.env.PORT || 4005;
passport.use('saml2', new Strategy({
path: 'http://MYSERVER:4005/assert',
entryPoint: 'https://sso.connect.pingidentity.com/sso/idp/SSO.saml2?XXXXXXXX',
issuer: 'MYAPP',
audience: 'MYAPP',
},
function(profile, cb) {
return cb(null, profile);
}));
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(obj, done) {
done(null, obj);
});
app.use(cookieParser());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(expressSession({
secret: '123xyz',
resave: true,
saveUninitialized: true
}));
// Initialize Passport and restore authentication state, if any, from the session.
app.use(passport.initialize());
app.use(passport.session());
app.get('/login/idp', () =>{
passport.authenticate('saml2')
console.log('Authentication called');
});
app.get('/login', () =>{
console.log('Authentication failed, try again');
});
app.post('/assert',
passport.authenticate('saml2', { failureRedirect: '/login' }),
function(req, res) {
console.log('Authentication succeeded');
console.log(req.user)
res.redirect('/');
});
app.listen(port, () => console.log(`Listening on port ${port}`));
在我的 React 应用程序的 package.json
中,我有:
{
...
"proxy": "http://localhost:4005/",
...
}
Create React App 玩具的轮廓是:
// Create React App
import React, { useState } from 'react';
import './App.css';
function App() {
const handleLogin = async e => {
e.preventDefault();
const response = await fetch('/login/idp', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
}
});
};
return (
<div className="App">
<form onSubmit={handleLogin}>
<button type="submit">Login</button>
</form>
</div>
);
}
export default App;
我可以愉快地点击按钮,控制台显示 Express 服务器的 GET 被触发,但没有任何内容返回到 React 客户端。
代理是可行的方法吗?我有正确的方法吗?如果是这样,我如何将结果从 Express 应用程序返回到 React 应用程序?
最佳答案
我有一个解决方案,但它看起来像是一个糟糕的黑客。然而,它确实有效,我需要把它过线。如果有人可以提出改进或替代方法,我将不胜感激。
我们从一个基本的 Express 服务器(托管在 4005)开始,它可以通过 Passport-SAML SSO 验证用户:
const express = require('express');
const jwt = require('jsonwebtoken')
const passport = require('passport');
const Strategy = require('passport-saml').Strategy;
require('dotenv').config()
const signature = process.env.SIGNATURE
const expiresIn = process.env.EXPIRESIN
// Simplification: actually there's a db look-up here
// based on req.user in order to get just the id
// but you get the idea
const createToken = user =>
jwt.sign({ user.email }, signature, { expiresIn: expiresIn })
passport.use('saml2', new Strategy({
path: 'http://localhost:4005/assert',
entryPoint: 'https://sso.connect.pingidentity.com/sso/idp/SSO.saml2?idpid=XXXX_YOURVALUEHERE_XXXX',
issuer: 'XXXX_YOURIDHERE_XXXX',
audience: 'XXXX_YOURIDHERE_XXXX',
},
function(profile, cb) {
return cb(null, profile);
}));
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(obj, done) {
done(null, obj);
});
// Create a new Express application.
var app = express();
app.use(require('cookie-parser')());
app.use(require('body-parser').urlencoded({ extended: true }));
// Initialize Passport and restore authentication state, if any, from the
// session.
app.use(passport.initialize());
app.get('/login/idp', passport.authenticate('saml2'));
app.post('/assert',
passport.authenticate('saml2',
{ failureRedirect: `http://localhost:3000/?error=unauthenticated` } ),
function(req, res) {
const token = createToken(req.user)
res.redirect(`http://localhost:3000/signinOK?token=${token}`);
});
app.listen(4005);
然后在React中src
文件夹中,添加所需的setupProxy.js:
const { createProxyMiddleware } = require('http-proxy-middleware');
module.exports = function(app) {
app.use(
'/login',
createProxyMiddleware({
target: 'http://localhost:4005',
headers: {
"Connection": "keep-alive"
}
})
);
};
然后在 React 应用程序(托管在端口 3000)中,我们为首页创建一个简单的按钮组件:
import React from 'react'
import { Button } from '@material-ui/core'
function StartBtn() {
return (
<Button type="submit" variant="contained" color="primary" >
<a href="/login/idp">Login</a>
</Button>
)
}
export default StartBtn
此时,我们粘贴 <StartBtn />
在首页上,并设置一条响应 http://localhost:3000/signinOK?token=...
的路线通过获取 token ,并将其用作任何后续 bearer:
中的值身份验证,并重定向到主站点。
流程如下:
- 用户加载首页,然后单击
<StartBtn/>
; - 链接已重定向,感谢
setupProxy.js
到 Express 服务器; - Express 服务器尝试
Passport-SAML
身份验证; - 身份验证过程的结果是从 IdP(PingID 身份验证服务器)到
/assert
上的 Express 服务器的 POST 调用。路线。 - 结果要么成功,要么失败,但在这两种情况下都会重定向到 React 应用。
- 如果成功,用户详细信息将以 JWT 形式返回;或
- 如果失败,则会返回错误。
如果我能找到改进它的方法,或者在 JWT 阶段进行扩展,我会回到这个答案。
我希望有人(a)发现这很有用,或者(b)发明了一台时间机器,回去并在三周前发布了这篇文章,这样我就可以保存更多剩余的毛囊。或者 (c) 告诉我应该如何做。
关于node.js - 如何使用 Passport 验证 React 应用程序?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61431902/