如何使用护照对 React 应用程序进行身份验证?
How do I authenticate a React app using passport?
我有一个 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=...
的路由,将其用作后续任何 bearer:
身份验证,并重定向到主站点。
流程如下:
- 用户加载首页,并点击
<StartBtn/>
;
由于 setupProxy.js
,- Link 被重定向到 Express 服务器;
- Express 服务器尝试
Passport-SAML
身份验证;
- 身份验证过程的结果是在
/assert
路由上从 IdP(PingID 身份验证服务器)到 Express 服务器的 POST 调用。
- 结果要么成功要么失败,但在这两种情况下都会重定向到 React 应用程序。
- 如果成功,用户详细信息将作为 JWT 返回;或
- 如果失败,返回错误。
如果我能找到改进它的方法,或者在 JWT 阶段进行扩展,我会回到这个答案。
我希望有人 (a) 发现这个有用,或者 (b) 发明了一个时间机器,回到 3 周前发布这个,这样我就可以保存更多剩余的毛囊。或者 (c) 告诉我应该怎么做。
我有一个 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=...
的路由,将其用作后续任何 bearer:
身份验证,并重定向到主站点。
流程如下:
- 用户加载首页,并点击
<StartBtn/>
;
由于 - Link 被重定向到 Express 服务器;
- Express 服务器尝试
Passport-SAML
身份验证; - 身份验证过程的结果是在
/assert
路由上从 IdP(PingID 身份验证服务器)到 Express 服务器的 POST 调用。 - 结果要么成功要么失败,但在这两种情况下都会重定向到 React 应用程序。
- 如果成功,用户详细信息将作为 JWT 返回;或
- 如果失败,返回错误。
setupProxy.js
,如果我能找到改进它的方法,或者在 JWT 阶段进行扩展,我会回到这个答案。
我希望有人 (a) 发现这个有用,或者 (b) 发明了一个时间机器,回到 3 周前发布这个,这样我就可以保存更多剩余的毛囊。或者 (c) 告诉我应该怎么做。