Skip to content

Commit 11ba7f4

Browse files
committed
Add information related to the LogoutRequest and LogoutResponse that can be either SP of IdP initiated
1 parent 5780c6c commit 11ba7f4

File tree

1 file changed

+155
-2
lines changed

1 file changed

+155
-2
lines changed

TUTORIAL.md

Lines changed: 155 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,7 @@ For the $saml_request_id you need to retrieve it from wherever it was stored dur
358358

359359
The call to $assertion->valid validates the following for the assertion:
360360

361-
1. That the $issuer configure in your application is the $audience of the assertion
361+
1. That the $issuer configured in your application is the $audience of the assertion
362362
2. That the $saml_request_id is the InResponseTo of the assertion
363363
3. That the current time is within the NotBefore and NotAfter datetimes of the assertion
364364

@@ -401,7 +401,160 @@ As a developer you need to review what is returned in the SAML2 Assertion from t
401401

402402
Regardless, the attributes of the assertion will contain those values, It is up to your application to use or ignore them as you see fit.
403403

404-
## Step 3: Generating the Service Provider (SP) Metadata (Optional)
404+
## Step 3: Service Provider initiated LogoutRequest (Optional)
405+
406+
The Service Provider (SP) can initiate a LogoutRequest to the Identity Provider (IdP). This is optional and the IdP can support Single Logout (SLO) which will initiate a process to logout all IdP logins sharing the session.
407+
408+
The process begins with the creation of the LogoutRequest XML with the correct values and then it is sent to the IdP via a Browser Redirect.
409+
410+
The following is from Foswiki's SamlLoginContrib function:
411+
```
412+
413+
# Foswiki's SamlLoginContrib stores the Assertions session_index
414+
my $sessionindex = $this->getSessionValue('saml_session_index');
415+
416+
my $idp = Net::SAML2::IdP->new_from_url(
417+
 url => $this->{Saml}{ metadata},
418+
 cacert => $this->{Saml}{ cacert },
419+
 );
420+
421+
my $logoutrequest = Net::SAML2::Protocol::LogoutRequest->new(
422+
 issuer => $this->{Saml}{ issuer },
423+
 nameid_format => 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress',
424+
 destination => $idp->slo_url('urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect'),
425+
 nameid => $session->{users}->getLoginName($session->{user}),
426+
 session => $sessionindex,
427+
 );
428+
429+
 my $logoutreq = $logoutrequest->as_xml;
430+
431+
 my $redirect = Net::SAML2::Binding::Redirect->new(
432+
 key => $this->{Saml}{ sp_signing_key },
433+
 cert => $this->{Saml}{ sp_signing_cert },
434+
 destination => $idp->slo_url('urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect'),
435+
 param => 'SAMLRequest',
436+
 url => $idp->slo_url('urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect'),
437+
 );
438+
 my $url = $redirect->sign($logoutreq);
439+
440+
# The $url is then sent to the browser as a redirect to initiate the logout.
441+
442+
```
443+
The IdP will respond with a LogoutResponse that is sent to the browser via a HTTP-POST or an HTTP-Redirect depending on the SP's configuration at the IdP (the SP metadata would specify the slo_url that is supported).
444+
445+
The SP would process the LogoutResponse and if it was a sucessful response invalidate the user's session at the SP.
446+
447+
The following is from Foswiki's SamlLoginContrib function:
448+
```
449+
# Foswiki's SamlLoginContrib stores the Assertions session_index
450+
# my $sessionindex = $this->getAndClearSessionValue('saml_session_index');
451+
452+
 my $idp = Net::SAML2::IdP->new_from_url(
453+
 url => $this->{Saml}{metadata},
454+
 cacert => $this->{Saml}{cacert},
455+
 sls_force_lcase_url_encoding => $this->{Saml}{sls_force_lcase_url_encoding},
456+
 sls_double_encoded_response => $this->{Saml}{sls_double_encoded_response}
457+
 );
458+
459+
my $redirect = Net::SAML2::Binding::Redirect->new(
460+
url => $idp->slo_url('urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect'),
461+
key => $this->{Saml}{sp_signing_key},
462+
cert => $idp->cert('signing'),
463+
param => 'SAMLResponse',
464+
sls_force_lcase_url_encoding => $this->{Saml}{sls_force_lcase_url_encoding},
465+
sls_double_encoded_response => $this->{Saml}{sls_double_encoded_response}
466+
);
467+
468+
my ($response, $relaystate) = $redirect->verify($uri);
469+
470+
if ($response) {
471+
 my $logout = Net::SAML2::Protocol::LogoutResponse->new_from_xml(
472+
 xml => $response
473+
 );
474+
475+
 if ($logout->status eq 'urn:oasis:names:tc:SAML:2.0:status:Success') {
476+
 deleteSession(...)
477+
 }
478+
 }
479+
480+
```
481+
482+
### Creating the LogoutRequest
483+
484+
## Step 4: IdP initiated LogoutRequest (Optional)
485+
486+
The Identity Provider (IdP) can intiate a LogoutRequest to the Service Provider (SP). There is no guarantee that this IdP initiated message will even get received by the SP as it requires the IdP to send a HTTP-GET request directly to the SP. It works outside the typical browser interaction for SAML. Indeed for many internal applications there is no direct internet access allowed to the SP application.
487+
488+
The process begins when the SP receives an unsolicated HTTP-GET request from the IdP. The SP must decode that LogoutRequest and process it to logout the user locally.
489+
490+
### Handling the LogoutRequest
491+
492+
The SP needs to create the Net::SAML2::IdP object as is done above (in this case using new_from_xml but could be new_from_url).
493+
494+
```
495+
my $idp = Net::SAML2::IdP->new_from_xml(
496+
xml => $metadata, # URL where the xml is located
497+
cacert => $cacert2, # Filename of the Identity Providers CACert
498+
);
499+
500+
```
501+
Create the Net::SAML2::Binding::Redirect object. Note the sls_force_lcase_url_encoding is used if the IdP sends a URL that has meen URL encoded with lower case characters %2f instead of %2F.
502+
503+
```
504+
my $redirect = Net::SAML2::Binding::Redirect->new(
505+
key => 't/sign-nopw-cert.pem',
506+
cert => $idp->cert('signing'),
507+
sig_hash => 'sha256',
508+
param => 'SAMLRequest',
509+
# The ssl_url destination for redirect
510+
url => $idp->sso_url('urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect'),
511+
#sls_force_lcase_url_encoding => 1,
512+
);
513+
```
514+
Verify signature on the URL, decode the request and retrieve the XML request and RelayState.
515+
516+
```
517+
my ($request, $relaystate) = $redirect->verify($get_request);
518+
519+
```
520+
Create the LogoutRequest object from the decoded XML request.
521+
522+
```
523+
my $logoutrequest = Net::SAML2::Protocol::LogoutRequest->new_from_xml(xml => $request);
524+
525+
```
526+
527+
The data that the SP requires is in the resulting Net::SAML2::Protocol::LogoutRequest object. The SP should perform a local logout of the nameid specified in the LogoutRequest. The session *should* be the session that the IdP sent in the Assertion (need to review). In general however the user associated with the nameid should have their session invalidated and the should be user forced to login again on next access.
528+
529+
```
530+
$VAR1 = \bless( {
531+
'id' => '_754753ec-5845-4d3f-bc06-84dbebf64c38',
532+
'nameid' => 'timlegge@cpan.org',
533+
'destination' => 'https://net-saml2.local/logout',
534+
'issue_instant' => '2022-01-29T00:32:40Z',
535+
'issuer' => bless( do{\(my $o = 'http://keycloak.local/')}, 'URI::http' ),
536+
'session' => '_4f6b29af-0e3c-4970-4f40-9609-fe9843ca1dc0'
537+
}, 'Net::SAML2::Protocol::LogoutRequest' );
538+
539+
```
540+
The logout response should be sent to the IdP by the SP after the local user's session has been invalidated. The LogoutResponse is created by creating the Net::SAML2::Protocol::LogoutResponse object with the correct values. The response_to is the id from the LogoutRequest. It is the LogoutRequest to which the LogoutRespones is related. Below shows the issue and the destination as the opposite of the same values from the LogoutRequest. The issuer in the request is likely where the LogoutResponse should be sent (the destination). More properly the issuer should be the $sp->{issuer} and the destination the $idp->{slo_url}->{urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect}.
541+
542+
```
543+
my $logoutresponse = Net::SAML2::Protocol::LogoutResponse->new(
544+
issuer => $logoutrequest->{destination},
545+
destination => $logoutrequest->{issuer},
546+
status => "urn:oasis:names:tc:SAML:2.0:status:Success",
547+
response_to => $logoutrequest->{id},
548+
);
549+
```
550+
551+
Once you have created the LogoutResponse you sign the XML version of the LogoutResponse using the Net::SAML2::Binding::Redirect. This results in a URL that the SP must use in a GET request to inform the IdP that the session was properly invalidated by the SP.
552+
553+
```
554+
my $logoutrequestsigned = $redirect->sign($logoutresponse->as_xml);
555+
```
556+
557+
## Step 5: Generating the Service Provider (SP) Metadata (Optional)
405558

406559
Some Identity Providers allow you to import a XML file that has the Service Provider settings. This allows you to ensure that the settings defined in your application are the same as those configured as the Service Provider settings in Identity Provider.
407560

0 commit comments

Comments
 (0)