无法使ADAM护照通行证

时间:2018-06-28 12:14:25

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

我已经在ExpressJS,NodePass应用程序上使用Express,Passport和Passport-Saml在ADFS上实现了SSO。我可以登录ADFS,并与SAML令牌一起正确发布回我的回调路由(即localhost:3000 / adfs / postResponse)。但是,当我到达回调路由时,似乎SAML令牌已被拒绝,因此我被发送回ADFS登录。然后重复此过程。

有人可以建议出什么问题吗?任何帮助将不胜感激。

更多详细信息:

此SAML请求已发送到ADFS:

<?xml version="1.0"?>
<samlp:AuthnRequest AssertionConsumerServiceURL="https://localhost:3000/adfs/postResponse"
    Destination="https://nonp-adfs.dsgapps.dk/adfs/ls" ID="_5e09625f5c3f5dbb0b6b"
    IssueInstant="2018-06-28T11:57:35.962Z"
    ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Version="2.0"
    xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol">
    <saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">acme_tools_com</saml:Issuer>
    <samlp:RequestedAuthnContext Comparison="exact" xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol">
        <saml:AuthnContextClassRef xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">http://schemas.microsoft.com/ws/2008/06/identity/authenticationmethod/password</saml:AuthnContextClassRef>
    </samlp:RequestedAuthnContext>
</samlp:AuthnRequest>

此SAML响应已从ADFS服务器发布到我的回调https://localhost:3000/adfs/postResponse

<samlp:Response Consent="urn:oasis:names:tc:SAML:2.0:consent:unspecified"
    Destination="https://localhost:3000/adfs/postResponse" ID="_e543e979-0d99-48fe-947f-1d1469da8c70"
    InResponseTo="_49ab1e1060c3d7849902" IssueInstant="2018-06-28T19:46:27.782Z" Version="2.0"
    xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol">
    <Issuer xmlns="urn:oasis:names:tc:SAML:2.0:assertion">http://nonp-adfs.dsgapps.dk/adfs/services/trust</Issuer>
    <samlp:Status><samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/></samlp:Status>
    <Assertion ID="_cf245f57-1380-47cd-a5d3-05b13e4d9416" IssueInstant="2018-06-28T19:46:27.782Z"
        Version="2.0" xmlns="urn:oasis:names:tc:SAML:2.0:assertion">
        <Issuer>http://nonp-adfs.dsgapps.dk/adfs/services/trust</Issuer>
        <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
            <ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/><ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
                <ds:Reference URI="#_cf245f57-1380-47cd-a5d3-05b13e4d9416">
                    <ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
                    <ds:DigestValue>r6voAsVq4yAJTn4BQLFsyaoiCK3b7KQbJ5jVqi53ceY=</ds:DigestValue>
                </ds:Reference>
            </ds:SignedInfo>
            <ds:SignatureValue>F55JA6jNp3qFfp7p/BSzQBRTtVPOlQvIfVNG3JiqjohVC7Et0+aiRVlHHvZNghPJxhmxhuAUbo2kOweN+lZKb+fqDgK51kZ/DrIVpkljmwP2gJYgOGpJti53wfH2qkdDsxNkR3e13mG7RKwBuA4gJ0NxUFshmxyun0HKefd10wjnFwHY6dELWFmTL1W5xd2ZF/98ahIaqEWAMCYsJewEg4ND8z4vG74miht3lWHfTJL6kQ0UGkTJVwGZy9L8zaY8AMDRujs8SlXvBx9nvUnvufpYqto4kd0O0USWMCOPipcF2sVYDOVzidRSRb79TK256Wg9EGiw1usVThfAJ8IBzQ==</ds:SignatureValue>
            <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
                <ds:X509Data>
                    <ds:X509Certificate>MIIC5DCCAcygAwIBAgIQWKHI7vunT6hIeNtPiejvbTANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDEyNBREZTIFNpZ25pbmcgLSBub25wLWFkZnMuZHNnYXBwcy5kazAeFw0xODA2MTUxNDQ4NTdaFw0xOTA2MTUxNDQ4NTdaMC4xLDAqBgNVBAMTI0FERlMgU2lnbmluZyAtIG5vbnAtYWRmcy5kc2dhcHBzLmRrMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyN0SSHVjJML1lmcHB8RBNLXnegEISB66Nc75xEpscFGNPKoQloLck6XLPYvhmiL8WiVHTzghiJpU/faViR7s+wksj3n4IXVfCxb6wMd78LiOfeE6yyED+C/EprwoRWGXncUK4lwfLDGOPbWVqaPy0u14rQR0mvn0BsIOiML1JJvAPtf8fhavNmce2aEeRltLY3N8aoLMw8/TMrG+wk1imUo+JScp3gOPqrDnQBGgcjdBY/EaC9mFfAUbhyly0vKl/gYkOv1HFhUMtH7NlLUmDsvOCt3Nrbf6aKmi+H1EAfwJR/POnMbsoC8sqf4PWk/kMtj1POOpZAnQOBE8u4NtPwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQDIdu6cr7LgoNdXpgtwd/Zt7sb1N6dJ/GgULuxBm7Bm1Mdsc9+Q0lDhxeGtay9AIvpbF67xvSzrCz3eL9xPuNV6BYmZpYFsyBPP4MROlkgq1MkqLDpkB/zkiKQqZiJG3RHl5e+WniFrAmNxuuUAtdhKbh1ADJKc1bxte6uiY0dN/Mfw6WnY3m3VOtae9xoqHNM2i4uhEbMvXV9Pmb8BVv4eIZLtOgo+vgkusp3FZa2PL4UWQIPNiEggIxhs7MfpaoADT4taGeavpHWKuxIGvDQzoe7GP2iDGzyH1kS24rSeJRYOiyBq1zPJHrSPeLFsef/7LapCaz5x5+T/eWPhyJKd</ds:X509Certificate>
                </ds:X509Data>
            </KeyInfo>
        </ds:Signature>
        <Subject>
            <SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><SubjectConfirmationData InResponseTo="_49ab1e1060c3d7849902"
                NotOnOrAfter="2018-06-28T19:51:27.782Z" Recipient="https://localhost:3000/adfs/postResponse"/></SubjectConfirmation>
        </Subject>
        <Conditions NotBefore="2018-06-28T19:46:27.781Z" NotOnOrAfter="2018-06-28T20:46:27.781Z">
            <AudienceRestriction>
                <Audience>acme_tools_com</Audience>
            </AudienceRestriction>
        </Conditions>
        <AuthnStatement AuthnInstant="2018-06-28T19:45:51.797Z">
            <AuthnContext>
                <AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</AuthnContextClassRef>
            </AuthnContext>
        </AuthnStatement>
    </Assertion>
</samlp:Response>

NB:可以使用SAML Chrome扩展程序检查SAML请求和响应。

我的NodeJS程序的最核心部分是:

const verifyFunction = function(profile, done) {
    console.log("Verifying"+ profile);
    return done(null,
        {
            upn: profile['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn'],
            // e.g. if you added a Group claim
            group: profile['http://schemas.xmlsoap.org/claims/Group']
        });
};

var strategy = new SamlStrategy(
    {
        entryPoint: 'https://nonp-adfs.dsgapps.dk/adfs/ls',
        issuer: 'acme_tools_com',
        callbackUrl: 'https://localhost:3000/adfs/postResponse',
        privateCert: fs.readFileSync(root + '/acme_tools_com.key', 'utf-8'),
        cert: fs.readFileSync(root + '/acme_tools_com.cert', 'utf-8'),
        authnContext: 'http://schemas.microsoft.com/ws/2008/06/identity/authenticationmethod/password',
        // not sure if this is necessary?
        acceptedClockSkewMs: -1,
        identifierFormat: null,
        signatureAlgorithm: 'sha256'
    },
    verifyFunction
);

strategy.userProfile = function(accessToken, done) {
    console.log("UserProfile:" + accessToken);
    done(null, accessToken);
};


passport.use('provider', strategy);
passport.serializeUser(function(user, done) {
    console.log("Serializing user");
    done(null, user);
});
passport.deserializeUser(function(user, done) {
    console.log("Deserializing user");
    done(null, user);
});



app.get('/login',
    passport.authenticate('provider', { failureRedirect: '/', failureFlash: true })
);
app.post('/adfs/postResponse',
    function(req, res, next) {
        console.log("Before authenticating: " );
        next();
    },
    passport.authenticate('provider', { failureRedirect: '/', failureFlash: true }),
    function(req, res) {
        console.log("User: " + util.inspect(req.user));
        res.cookie('accessToken', req.user);
        res.redirect('/');
    }
);

进行登录时,我在ADFS服务器上没有看到任何错误,因此在那里看起来不错。

在NodeJS服务器上,我看到:

Express server started on https://localhost:3000 
postResponse entered:
postResponse entered:
...

换句话说,永远不会调用verifyFunction。那不是很奇怪吗?似乎passport-saml模块无法接收SAML响应。

从ADFS到我的回调的POST如下:

curl 'https://localhost:3000/adfs/postResponse' -H 'Origin: https://nonp-adfs.dsgapps.dk' -H 'Content-Type: application/x-www-form-urlencoded' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8' -H 'Referer: https://nonp-adfs.dsgapps.dk/adfs/ls?SAMLRequest=nVJLc9owEP4rHt2xZMdD....' -H 'Accept-Encoding: gzip, deflate, br' -H 'Accept-Language: en-US,en;q=0.9,da;q=0.8' -H  --data 'SAMLResponse=PHNhbWxwOlJlc3.......' --compressed --insecure

这是完整的NodeJS程序:

'use strict';

var util = require('util');
var https = require('https');
var app = require('express')();
var cookieParser = require('cookie-parser');
var passport = require('passport');
var fs = require('fs');
var SamlStrategy = require('passport-saml').Strategy;
var path = require("path");

const root = __dirname;
const verifyFunction = function(profile, done) {
    console.log("Verifying"+ profile);
    return done(null,
        {
            upn: profile['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn'],
            // e.g. if you added a Group claim
            group: profile['http://schemas.xmlsoap.org/claims/Group']
        });
};

var strategy = new SamlStrategy(
    {
        entryPoint: 'https://nonp-adfs.dsgapps.dk/adfs/ls',
        issuer: 'acme_tools_com',
        callbackUrl: 'https://localhost:3000/adfs/postResponse',
        privateCert: fs.readFileSync(root + '/acme_tools_com.key', 'utf-8'),
        cert: fs.readFileSync(root + '/acme_tools_com.cert', 'utf-8'),
        authnContext: 'http://schemas.microsoft.com/ws/2008/06/identity/authenticationmethod/password',
        // not sure if this is necessary?
        acceptedClockSkewMs: -1,
        identifierFormat: null,
        signatureAlgorithm: 'sha256'
    },
    verifyFunction
);

strategy.userProfile = function(accessToken, done) {
    console.log("UserProfile:" + accessToken);
    done(null, accessToken);
};


passport.use('provider', strategy);
passport.serializeUser(function(user, done) {
    console.log("Serializing user");
    done(null, user);
});
passport.deserializeUser(function(user, done) {
    console.log("Deserializing user");
    done(null, user);
});

function validateAccessToken(accessToken) {
    console.log("AccessToken: "+ accessToken);
    return;
}


// Configure express app
app.use(cookieParser());
app.use(passport.initialize());

app.get('/login',
    passport.authenticate('provider', { failureRedirect: '/', failureFlash: true })
);
app.post('/adfs/postResponse',
    function(req, res, next) {
        console.log("Before authenticating: " );
        next();
    },
    passport.authenticate('provider', { failureRedirect: '/', failureFlash: true }),
    function(req, res) {
        console.log("User: " + util.inspect(req.user));
        res.cookie('accessToken', req.user);
        res.redirect('/');
    }
);
app.get('/', function (req, res) {
    req.user = req.cookies['accessToken'];
    res.send(
        !req.user ? '<a href="/login">Log In</a>' : '<a href="/logout">Log Out</a>' +
        '<pre>' + JSON.stringify(req.user, null, 2) + '</pre>');
});
app.get('/logout', function (req, res) {
    res.clearCookie('accessToken');
    res.redirect('/');
});

var certOptions = {
    key: fs.readFileSync(root + '/localhost.key'),
    cert: fs.readFileSync(root + '/localhost.cert')
}

var server = https.createServer(certOptions, app).listen(3000)
console.log('Express server started on https://localhost:3000');

1 个答案:

答案 0 :(得分:0)

问题原来很简单。我需要包括中间件来解析POST数据。

将这些行添加到我的NodeJS模块的顶部后,它开始工作。

list1 = ['ab','cd','gfa','eha','tu','asb','acd','cgf','ceh','dtu','ased','ra','re','sdgfsycbjs','jcjhcbsd']
list2 = ['abx','cd','gfr','eha','tu','asb','acl','cgfta','cpah','adtu','assa','fd','as','sbddsvc','jbcbh']
list3 = ['abs','cd','gfv','eh','tu','asb','ased','cgf','ceh','adtu','assa','qw','uy','hdsjb','bcjh']
a = []
b = []
c = []
ab = []
bc = []
ca = []
abc = []
for item in list1:
        if item in list2:
            if item in list3:
                if item not in abc:
                    abc.append(item)
            else:
                ab.append(item)
        else:
            if item not in ca:
                a.append(item)

for item in list2:
    if item in list3:
        if item in list1:
            if item not in abc:
                abc.append(item)
        else:
            bc.append(item)
    else:
        if item not in ab:
            b.append(item)        

for item in list3:
    if item in list1:
        if item in list2:
            if item not in abc:
                abc.append(item)
        else:
            ca.append(item)
    else:
        if item not in bc:
            c.append(item)