Document SP initiated logout
Please add an example of usage.
Yes this would be extremely helpful.
A pr on this would be most welcome. The support for logout is still pretty new at this time. Ref #307 and #308
Any news on this? I am struggling with using this library and specially getting the logout working. I understand I need to somehow tell the IDP about the users logout request and also be able to answer IDPs logout request.
Ok I have hacked together the first step of the Redirect:
type Logout struct {
SP *samlsp.Middleware
}
func (l *Logout) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// THIS IS A HACK
// the nameID is encoded in the `sub` field of the JWT, I am sending this as a query param
// but you should be able to get the JWT from the Cookie and then the subject from the JWT
nameID := r.URL.Query().Get("name")
url, err := l.SP.ServiceProvider.MakeRedirectLogoutRequest(nameID, "relayState")
if err != nil {
WebErrorWarn("error get signouturl: "+err.Error(), http.StatusInternalServerError, w)
return
}
//Note you need to set to set session domain so that the is does not contain the port
// will make another ticket on this
err = l.SP.Session.DeleteSession(w, r)
if err != nil {
WebErrorWarn("error get signouturl: "+err.Error(), http.StatusInternalServerError, w)
return
}
http.Redirect(w, r, url.String(), http.StatusFound)
}
The problem is the logout response from the IDP is sent to the SloURL() which I believe does nothing right now(?).
It looks like I just need to:
- register a handler for SloURL
- extract the SAML message
- Get the
logoutRequestIDfrom it - Redirect one last time saying it is all good
Can anyone verify these are the final steps? Provide any insight into getting the logoutRequestID?
Ok I have gotten rid of the hack I used to get the session ID and got comfirmation that this is performing a good logout (the IDP is still keeping the user logged in but it at least closes the SP session early).
I am leaving the relay state blank as I just want the website to land on the Slo URL.
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
}
err = l.SP.Session.DeleteSession(w, r)
if err != nil {
WebErrorWarn("error get signouturl: "+err.Error(), http.StatusInternalServerError, w)
return
}
http.Redirect(w, r, url.String(), http.StatusFound)
}
I think the samlsp Middleware should include a handler to handle idp logout call back. Something like this:
in middleware.go
// ServeHTTP implements http.Handler and serves the SAML-specific HTTP endpoints
// on the URIs specified by m.ServiceProvider.MetadataURL and
// m.ServiceProvider.AcsURL.
func (m *Middleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// ... snip ...
// add function to handle slo
if r.URL.Path == m.ServiceProvider.SloURL.Path {
m.ServeSlo(w, r)
return
}
// ...
}
// ...
// ServeSlo handles logout. It clears session and redirect to /
func (m *Middleware) ServeSlo(w http.ResponseWriter, r *http.Request) {
err := m.Session.DeleteSession(w, r)
if err != nil {
m.OnError(w, r, err)
return
}
http.Redirect(w, r, "/", http.StatusFound)
}
If we don't want to change the library, then the simplest way is to explicitly handle saml/slo route:
in an example main.go:
// assume we are using a global variable to represent the created sp middleware
var (
samlSP *samlsp.Middleware
)
// ServeSlo handles logout. It clears session and redirect to /
func serveSlo(w http.ResponseWriter, r *http.Request) {
err := samlSP.Session.DeleteSession(w, r)
if err != nil {
samlSP.OnError(w, r, err)
return
}
http.Redirect(w, r, "/", http.StatusFound)
}
func main() {
// ...
samlSP, _ = samlsp.New(samlsp.Options{
//...
},
})
// ...
http.HandleFunc("/saml/slo", serveSlo)
//...
}