Skip to content

Commit d02da70

Browse files
committed
Handle NameID Format in Logout messages.
Previously, the SAML SP code did not consider the NameID Format during Single Logout. This commit fixes the logout handling to be compliant with the SAML 2.0 specifications regarding NameID.
1 parent 4cf0f20 commit d02da70

File tree

2 files changed

+31
-10
lines changed

2 files changed

+31
-10
lines changed

saml_sp.js

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ async function handleSAMLMessage(messageType, r) {
100100

101101
/* Define necessary parameters needed to create a SAML LogoutResponse */
102102
opt.nameID = nameID[0];
103+
opt.nameIDFormat = nameID[1];
103104
opt.inResponseTo = id;
104105
opt.relayState = params.RelayState;
105106

@@ -659,7 +660,8 @@ async function produceSAMLMessage(messageType, r, opt) {
659660
break;
660661
case "LogoutResponse":
661662
/* Obtain the status code for the LogoutResponse message */
662-
opt.statusCode = getLogoutStatusCode(r.variables.saml_name_id, opt.nameID)
663+
opt.statusCode = getLogoutStatusCode(r.variables.saml_name_id, opt.nameID,
664+
r.variables.saml_name_id_format, opt.nameIDFormat);
663665
break;
664666
}
665667

@@ -701,14 +703,17 @@ function setAuthRedirCookie(r) {
701703
];
702704
}
703705

704-
function getLogoutStatusCode(sessionNameID, requestNameID) {
705-
/* If no session exists, return Logout Success */
706+
function getLogoutStatusCode(sessionNameID, requestNameID, sessionFormat, requestFormat) {
707+
/* If no session exists, treat as already logged out */
706708
if (!sessionNameID || sessionNameID === '-') {
707709
return 'urn:oasis:names:tc:SAML:2.0:status:Success';
708710
}
709-
710-
/* If session exists, return Logout Success if NameID matches */
711-
return requestNameID === sessionNameID
711+
/* Treat missing formats as "unspecified" for comparison */
712+
const defaultFmt = 'urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified';
713+
const spFormat = (!sessionFormat || sessionFormat === '-') ? defaultFmt : sessionFormat;
714+
const requestFmt = (!requestFormat || requestFormat === '-') ? defaultFmt : requestFormat;
715+
/* Only return Success if both NameID value and Format match exactly */
716+
return (requestNameID === sessionNameID && spFormat === requestFmt)
712717
? 'urn:oasis:names:tc:SAML:2.0:status:Success'
713718
: 'urn:oasis:names:tc:SAML:2.0:status:Requester';
714719
}
@@ -722,7 +727,9 @@ async function createSAMLMessage(opt, id, messageType) {
722727
nameIDPolicy: `<samlp:NameIDPolicy Format="${opt.nameIDFormat}" AllowCreate="true"/>`,
723728
}),
724729
LogoutRequest: () => ({
725-
nameID: `<saml:NameID>${opt.nameID}</saml:NameID>`,
730+
nameID: opt.nameIDFormat && opt.nameIDFormat !== 'urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified'
731+
? `<saml:NameID Format="${opt.nameIDFormat}">${opt.nameID}</saml:NameID>`
732+
: `<saml:NameID>${opt.nameID}</saml:NameID>`,
726733
}),
727734
LogoutResponse: () => ({
728735
inResponseTo: ` InResponseTo="${opt.inResponseTo}"`,
@@ -1234,6 +1241,7 @@ function parseConfigurationOptions(r, messageType) {
12341241
}
12351242
opt.relayState = r.variables.saml_logout_landing_page;
12361243
opt.nameID = r.variables.saml_name_id;
1244+
opt.nameIDFormat = r.variables.saml_name_id_format;
12371245
opt.allowedClockSkew = validateClockSkew('saml_allowed_clock_skew', 120);
12381246
}
12391247

t/js_saml.t

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ my $sp_pub = $t->read_file('sp.example.com.crt');
285285
my $js_filename = 'saml_sp.js';
286286
$t->write_file($js_filename, read_file("../$js_filename"));
287287

288-
$t->try_run('no njs available')->plan(132);
288+
$t->try_run('no njs available')->plan(134);
289289

290290
my $api_version = (sort { $a <=> $b } @{ api() })[-1];
291291
my $kv = "/api/$api_version/http/keyvals";
@@ -393,7 +393,7 @@ like($r, qr{302.*http://sp.example.com:8080/foo\?a=b}s,
393393
like(get("$kv/saml_response_id"), qr/"_nginx_[^"]+":\s*"1"/,
394394
'kv response id');
395395
like(get("$kv/saml_name_id"), qr/user1/, 'kv response name id');
396-
like(get("$kv/saml_name_id_format"), qr/unspecified/,
396+
like(get("$kv/saml_name_id_format"), qr/emailAddress/,
397397
'kv response name id format');
398398
like(get("$kv/saml_session_index"), qr/_nginx_sessionindex_/,
399399
'kv response session index');
@@ -654,6 +654,8 @@ is($r->{Destination}, $cfg->{saml_idp_slo_url},
654654
is($r->{Issuer}, $cfg->{saml_sp_entity_id}, 'sp logout request issuer');
655655
is($r->{isSigned}, 0, 'sp logout request unsigned');
656656
is($r->{NameID}, 'user1', 'sp logout request nameid');
657+
is($r->{NameIDFormat}, 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress',
658+
'sp logout request nameid format');
657659
like(get("$kv/saml_request_id"), qr/"$r->{ID}":"1"/,
658660
'sp logout request id redeemed');
659661

@@ -764,6 +766,12 @@ $r = parse_response(modify_saml_obj($xml_obj, '//saml:NameID', 'text', 'foo',
764766
is($r->{StatusCode}, 'urn:oasis:names:tc:SAML:2.0:status:Requester',
765767
'idp logout request wrong nameid');
766768

769+
$r = parse_response(modify_saml_obj($xml_obj, '//saml:NameID',
770+
'Format', 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient',
771+
auth_token => $auth_token));
772+
is($r->{StatusCode}, 'urn:oasis:names:tc:SAML:2.0:status:Requester',
773+
'idp logout request wrong nameid format');
774+
767775
# Logout Response
768776

769777
($r, undef) = init_slo($cfg, relay_state => '/foo?a=b');
@@ -960,6 +968,11 @@ sub extract_saml_attributes {
960968
$result->{isSigned} = 0;
961969
}
962970

971+
my ($name_id_node) = $xpc->findnodes('//saml:NameID');
972+
if ($name_id_node) {
973+
$result->{NameIDFormat} = $name_id_node->getAttribute('Format');
974+
}
975+
963976
my ($name_id_policy_node) = $xpc->findnodes('//samlp:NameIDPolicy');
964977
if ($name_id_policy_node) {
965978
$result->{NameIDPolicyFormat} =
@@ -1563,7 +1576,7 @@ END_XML
15631576
$signature
15641577
<saml:Subject>
15651578
<saml:NameID SPNameQualifier=""
1566-
Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
1579+
Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
15671580
>user1</saml:NameID>
15681581
<saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
15691582
<saml:SubjectConfirmationData NotOnOrAfter=""

0 commit comments

Comments
 (0)