如何避免使用护照身份验证进行循环登录

时间:2021-05-11 19:05:31

标签: node.js passport.js saml-2.0

我正在通过带有 nodejs/express 后端的 SAML2 上的通行证设置单点登录。我最终陷入了某种循环..

流程如下:

当我访问我的应用时,它会调用 whoami 路由,定义如下。在第一次通过时,这应该(并且确实)返回 401(未经授权)。 当我的应用返回 401(未经授权)时,我们会将应用重定向到 login。 下面定义的 login 路由通过 RelayState 记住我们的原始 URL(以便我们可以返回到它)并在重定向回应用程序之前运行 passport.authenticate(....)。我承认不知道我们如何或为什么重定向回此处的应用程序 - 删除它并不能解决问题,但是,实际上,它确实会转到 SSO 提供程序(在 config.saml.options目的)。 SSO 提供者发挥了它的魔力并返回到 /login/callback 路由,定义如下。此请求包含所有需要的信息。 ......但是......然后我的应用程序重新启动该过程......创建一个循环: WhoAmI -> 登录 -> LoginCallback -> WhoAmI -> 登录 -> LoginCallback

我的理解是,在某些时候,后端应该在本地保存用户,随后的 whoami 调用将返回所需的信息。

如何停止这种循环处理。

我的passport.js文件:

import fs from 'fs';
import passport from 'passport';
import { Strategy } from 'passport-saml';
import config from './config.js';

const savedUsers = [];
passport.serializeUser((expressUser, done) => {
    done(null, expressUser)
});
passport.deserializeUser((expressUser, done) => {
    done(null, expressUser)
});

passport.use(
    new Strategy(
        {
            issuer: config.saml.issuer,
            protocol: 'https://',
            path: '/auth/login/callback',
            entryPoint: config.saml.entryPoint,
            cert: fs.readFileSync(config.saml.cert, 'utf-8'),
            authnContext: ["urn:federation:authentication:windows"],
            identifierFormat: null,  //if omitted the identifier format defaults to urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
        },
        (expressUser, done) => {
            if (savedUsers.includes(expressUser)) {
                savedUsers.push(expressUser)
            }
            return done(null, expressUser)
        }
    )
)

我的 SAML 身份验证的路由定义是:

import express from 'express';
import config from '../config/config.js';

import '../config/passport.js'
import session from 'express-session'
import passport from 'passport'

const samlRouter = express.Router();

samlRouter.use(session(config.session));
samlRouter.use(passport.initialize());
samlRouter.use(passport.session());

samlRouter.use((req, res, next) => {
    console.log("Use");
    res.header('Access-Control-Allow-Origin', req.header('origin'));
    res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-Width, Content-Type, Accept, Authorization');
    res.header('Access-Control-Allow-Credentials', 'true'); //SAML

    if (req.method == 'OPTIONS') {
        res.header('Access-Control-Allow-Methods', 'PUT, POST, GET');
        return res.status(200).json({})
    }
    next();
})

samlRouter.get('/whoami', (req, res, next) => {
    console.log('whoami');
    if (!req.isAuthenticated()) {
        return res.status(401).json({
            message: 'Unauthorized',
        });
    }
    return res.status(200).json({ user: req.user });
});


samlRouter.get('/login',

    (req, res, next) => {
        console.log('login');
        req.query.RelayState = req.query.origURL;
        passport.authenticate('saml', config.saml.options)(req, res, next)
    },
    (req, res, next) => {
        return res.redirect(`${config.app.location}:3000`);
    }
); 


samlRouter.post('/login/callback',
(req, res, next) => {
        console.log('login/callback');
        passport.authenticate('saml', config.saml.options)(req, res, next)
    },
    (req, res, next) => {
        if(req.user) {
            console.log('req.user.nameID :>> ', req.user?.nameID);   **// THIS OUTPUTS THE CORRECT INFO**
        } else {
            console.log('req.user was falsey');
        }
        
        console.log(`redirect to Relay state ${req.body.RelayState}`);
        return res.redirect(`${req.body.RelayState}`);
    });


export default samlRouter;

我获取用户的应用代码是这样的:

export function getUser() {  
    console.log('Getting user...');
    const RedirectToLogin = () => {
        console.log("Redirecting to xxxx/login...")
        window.location.assign(`https://xxxx/login?origURL=${encodeURIComponent(window.location.href)}`)
    }

    return axios({
        method: 'GET',
        url: 'https://xxxx/whoami',
        withCredentials: true
    })
        .then(resp => {    // WE NEVER GET HERE...
            if (resp.data.user) {
                console.log(`User Authenticated as ${resp.data.user.nameID}`);
                return Promise.resolve(resp.data.user.nameID)
            }
            else {
                console.warn("Unauthenticated - redirecting to login")
                RedirectToLogin()
            }
        })
        .catch(err => {
            console.log('err :>> ', err);
            console.warn("Unauthenticated -- redirecting to login")
            RedirectToLogin()
        })
}

0 个答案:

没有答案