Skip to content

How to use with ADFS

Chris Barth edited this page May 29, 2023 · 2 revisions

Active Directory Federation Services

This document attempts to describe a complete solution for integrating with AD FS.

Creating a self-signed certificate

Create a folder to contain your keys and certificates.

Download mellon_create_metadata.sh

Create a new key, certificate and relying party XML as follows:

./mellon_create_metadata.sh acme_tools_com https://acme_tools.com/adfs/postResponse

Retrieve ADFS Certificate

Use retrieve_adfs_certificate.sh to get your AD FS server's signing certificate:

retrieve_adfs_certificate.sh

#!/bin/sh # Author: Tim Brody <T.D.Brody@soton.ac.uk> # Date: 2015-02-11 # # Retrieve the signing certificate from an ADFS instance in PEM format. ADFS_SERVER=$1 if [ "$#" -ne "1" ]; then echo "Usage: $0 <adfs server URL>" exit 1 fi URL=$ADFS_SERVER/FederationMetadata/2007-06/FederationMetadata.xml TEMPFILE=$(mktemp) if [[ $(command -v wget) ]]; then wget --no-check-certificate -q -O $TEMPFILE $URL elif [[ $(command -v curl) ]]; then curl -sk $URL -o $TEMPFILE else echo "Neither curl or wget was found" exit 127 fi if [ $? -ne 0 ]; then echo "Error requesting $URL" exit 1 fi echo "-----BEGIN CERTIFICATE-----" (xmllint --shell $TEMPFILE | grep -v '^/ >' | grep -v '^ ----' | fold -w 64) << EndOfScript setns a=urn:oasis:names:tc:SAML:2.0:metadata setns b=http://www.w3.org/2000/09/xmldsig# cat /a:EntityDescriptor/b:Signature/b:KeyInfo/b:X509Data/b:X509Certificate/text() EndOfScript echo "-----END CERTIFICATE-----" unlink $TEMPFILE
./retrieve_adfs_certificate.sh https://adfs.acme_tools.com/ > adfs.acme_tools.com.crt

Create Relying Party

Copy the acme_tools_com.xml to your AD FS server. Use the AD FS management relying party wizard to import this XML, creating a relying party. Create claim rules to pass authentication information to your application.

This example assumes you will pass in the UPN.

Set AD FS Relying Party to sign the message and the assertion

Set-AdfsRelyingPartyTrust -TargetIdentifier "acme_tools_com" -SamlResponseSignature "MessageAndAssertion"

Create a Passport framework

Create a separate file for passport configuration (assumed to be config/passport.js).

var fs = require("fs"), passport = require("passport"), SamlStrategy = require("passport-saml").Strategy; passport.serializeUser(function (user, done) { done(null, user); }); passport.deserializeUser(function (user, done) { done(null, user); }); passport.use( new SamlStrategy( { entryPoint: "https://adfs.acme_tools.com/adfs/ls/", issuer: "acme_tools_com", callbackUrl: "https://acme_tools.com/adfs/postResponse", privateKey: fs.readFileSync("/path/to/acme_tools_com.key", "utf-8"), cert: fs.readFileSync("/path/to/adfs.acme_tools.com.crt", "utf-8"), // other authn contexts are available e.g. windows single sign-on // see: https://learn.microsoft.com/en-us/dotnet/api/system.identitymodel.tokens.authenticationmethods?view=netframework-4.8#fields authnContext: [ "http://schemas.microsoft.com/ws/2008/06/identity/authenticationmethod/password", ], identifierFormat: null, // this is configured under the Advanced tab in AD FS relying party signatureAlgorithm: "sha256", racComparison: "exact", // default to exact RequestedAuthnContext Comparison Type // From the metadata document audience: "https://adfs.acme_tools.com/FederationMetadata/2007-06/FederationMetadata.xml", }, function (profile, done) { 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"], }); } ) ); module.exports = passport;

Express Application

 var passport = require('passport'); var express = require('express'); ... etc. var app = express(); require('./config/passport.js'); ... add the usual express pro-forma app.use(passport.initialize()); app.use(passport.session()); app.get('/login', passport.authenticate('saml', { failureRedirect: '/', failureFlash: true }), function(req, res) { res.redirect('https://acme_tools.com'); } ); app.post('/adfs/postResponse', passport.authenticate('saml', { failureRedirect: '/', failureFlash: true }), function(req, res) { res.redirect('https://acme_tools.com'); } ); app.get('/secure', validUser, routes.secure); function validUser(req, res, next) { if (!req.user) { res.redirect('https://acme_tools.com/login'); } next(); } var server = http.createServer(app);

Troubleshooting

ADFS 2016

If you are setting up an ADFS 2016 server, you might run into the following issue with the previous settings:

An error occurred during an attempt to read the federation metadata. Verify that the specified URL or host name is a valid federation metadata endpoint. Verify your proxy server setting. For more information about how to verify you proxy sever setting, see the AD FS Troubleshooting Guide http://go.microsoft.com/fwlink/?LinkId=182180). Error message: EntityDescriptor 'acme_tools_com'. ID0014: The value 'NameIDFormat' must be an absolute URI.

NameIDFormat Error Popup

If you remove the identifierFormat, it works as expected.