javascriptexpresspassport.jspassport-saml

passport-saml & Accessing SAML Values


I have passport-saml working correctly but I'm not able to access the ADFS attributes that are being returned from the claim rule, for example the firstName attribute.

The below code displays the following in console.log when hitting the /login endpoint in a web browser:

GET [/] user authenticated! req.user: {}

Any ideas on why the below isn't working, it seems that the req object isn’t being populated?

var app = express();

var samlStrategy = new saml.Strategy(
    {
        entryPoint: 'https://adfs.mydomain/adfs/ls/',
        issuer: 'https://foo.mydomain/login/callback',
        callbackUrl: 'https://foo.mydomain/login/callback',
        privateCert: fs.readFileSync('./certs/my.key', 'utf-8'),
        cert: fs.readFileSync('./certs/adfs.mydomain.crt', 'utf-8'),
        authnContext: 'http://schemas.microsoft.com/ws/2008/06/identity/authenticationmethod/windows',
        acceptedClockSkewMs: -1,
        identifierFormat: null,
        signatureAlgorithm: 'sha256'
    },
    function (profile, done) {
        return done(null,
            {
                nameIDFormat: profile.nameIDFormat,
                nameID: profile.nameID,
                firstName: profile.firstName
            }
        );
    }
);

passport.serializeUser(function (user, done) {
    done(null, user);
});

passport.deserializeUser(function (user, done) {
    done(null, user);
});

passport.use(samlStrategy);

app.use(cookieParser());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());

app.use(session({ resave: true, saveUninitialized: false, secret: 'xxxxx' }));

app.use(passport.initialize());
app.use(passport.session());

app.get('/', function (req, res) {
    if (req.isAuthenticated()) {
        console.log('GET [/] user authenticated! req.user: %s \n', JSON.stringify(req.user));
    } else {
        console.log('GET [/] user not authenticated! \n');
    }
    res.send(200);
});

app.get('/login', passport.authenticate('saml', { failureRedirect: '/' }), function (req, res) { res.redirect('/') });

app.post('/login/callback', passport.authenticate('saml', { failureRedirect: '/login', failureFlash: true }),
    function (req, res) {
        res.redirect('/');
    }
);

UPDATE: The issue appears to be in the profile details not being populated, if I set a static value it works correctly:

var samlStrategy = new saml.Strategy(
    {
        entryPoint: 'https://adfs.mydomain/adfs/ls/',
        issuer: 'https://foo.mydomain/login/callback',
        callbackUrl: 'https://foo.mydomain/login/callback',
        privateCert: fs.readFileSync('./certs/my.key', 'utf-8'),
        cert: fs.readFileSync('./certs/adfs.mydomain.crt', 'utf-8'),
        authnContext: 'http://schemas.microsoft.com/ws/2008/06/identity/authenticationmethod/windows',
        acceptedClockSkewMs: -1,
        identifierFormat: null,
        signatureAlgorithm: 'sha256'
    },
    function (profile, done) {
        return done(null,
            {
                nameIDFormat: profile.nameIDFormat,
                nameID: profile.nameID,
                firstName: 'test_user'
            }
        );
    }
);

GET [/] user authenticated! req.user: {"firstName":"test_user"}


Solution

  • The solution was to get the correct elements from the profile, a simple example is below.

    function (profile, done) {
            console.log('passport.use() profile: %s \n', JSON.stringify(profile));
            return done(null,
                {
                    upn: profile['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn']                
                }
            );
        }