Search code examples
gojwtsingle-sign-onsaml

In SAML, what are the actions that need to be performed in the client and service provider to logout?


I have gotten SAML Login working in a Go program using crewjam/samlwith a Keycloak IDP in SAML mode (I believe this is using SAMLv2 but not positive). The basics are that upon a good login, the IDP send the program the user's SAML attributes, the Go SAML Library translates this to a JWT and sets it as the HTTP Cookie. At this point the IDP marks that the user has a session with the Service and the user can access the API via the JWT.

The problem I am having is that I am unclear how to Logout. The library has a URL for logging out:

// SloURL is the full URL to the SAML Single Logout endpoint on this host.
// i.e. https://example.com/saml/slo
SloURL url.URL

But navigating to this page just returns a 404.

So how do I tell the IDP that the user's session is done? Should I remove the JWT cookie myself or will this be handled?


Solution

  • The following appears to accomplish what I asked for:

    type Logout struct {
        SP *samlsp.Middleware
    }
    
    func (l *Logout) ServeHTTP(w http.ResponseWriter, r *http.Request) {
          //Get the JWT information
        session, err := l.SP.Session.GetSession(r)
        if err != nil {
            WebErrorWarn("error get signouturl session: "+err.Error(), http.StatusForbidden, w)
            return
        }
           //Get the JWT information part 2
        attr := session.(samlsp.JWTSessionClaims)
        if err != nil {
            WebErrorWarn("error get signouturl session claims: "+err.Error(), http.StatusForbidden, w)
            return
        }
    
        //use this as the name for the logout request
        url, err := l.SP.ServiceProvider.MakeRedirectLogoutRequest(attr.Subject, "")
        if err != nil {
            WebErrorWarn("error get signouturl: "+err.Error(), http.StatusInternalServerError, w)
            return
        }
    
        //delete the session token from teh browser 
        err = l.SP.Session.DeleteSession(w, r)
        if err != nil {
            WebErrorWarn("error get signouturl: "+err.Error(), http.StatusInternalServerError, w)
            return
        }
    
        //redirect to the IDP Single log out URLwith the SAMLRequests for logout embedded
        http.Redirect(w, r, url.String(), http.StatusFound)
    }
    
    

    I make my own logout URL to serve this on

        http.Handle("/logout", samlSP.RequireAccount(&Logout{samlSP}))
    

    Finally the IDP redirect the client back to the SLO URL, it is sent in the metadata file and also defaults to /saml/slo in crewjam/gosaml. I just have a handler on that URL to give the user a confirmation that they are no longer signed in.

        http.Handle("/saml/slo", &SLOHandle{})
    

    Note the /saml/slo URL should not be SAML protected otherwise you will trigger SAML login all over again.