Reactアプリを持っています。
Expressサーバーもあります。これを使用passport-saml
すると、会社のPingID SSOIdPに対して認証できます。
Reactアプリを取得して、どういうわけかExpressアプリを呼び出して認証したいと思います。
ExpressServerと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/",
...
}
おもちゃのCreateReactアプリの概要は次のとおりです。
// 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アプリに結果を戻すことができますか?
私には解決策がありますが、それはひどいハックのようです。しかし、それは機能し、私はこれを一線を越えて取得する必要があります。誰かが改善または代替アプローチを提案することができれば、私は感謝するでしょう。
Passport-SAML SSOを介してユーザーを検証できる基本的なExpressサーバー(4005でホストされている)から始めます。
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);
次に、Reactsrc
フォルダーに、必要な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
Expressサーバーのおかげでリンクがリダイレクトされます。Passport-SAML
認証を試みます。/assert
ルート上のIdP(PingID認証サーバー)からExpressサーバーへのPOST呼び出しです。それを改善する方法を見つけることができれば、またはJWTステージで拡張できる場合は、この回答に戻ります。
誰かが(a)これが便利だと思うか、(b)タイムマシンを発明して、3週間前に戻って投稿し、残りの毛包をもっと節約できるようになることを願っています。または(c)私がそれをすべきだった方法を教えてくれます。
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加