我有一个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应用程序?
答案 0 :(得分:0)
我有一个解决方案,但是看起来很糟糕。但是,它可以正常工作,我需要一遍就解决这个问题。如果有人可以提出改进或替代方法,我将不胜感激。
我们从基本的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=...
做出响应的Route,并将其用作后续{{1} }身份验证,然后重定向到主站点。
流程如下:
bearer:
; <StartBtn/>
将链接重定向到Express服务器; setupProxy.js
身份验证; Passport-SAML
路由从IdP(PingID身份验证服务器)到Express服务器的POST调用。如果我能找到改进的方法,或者在JWT阶段进行扩展,我将回到这个答案。
我希望有人(a)认为这很有用,或者(b)发明了一种时间机器,并在3周前回过头来发布,以便我可以节省更多剩余的毛囊。或者(c)告诉我应该怎么做。