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

huangapple go评论96阅读模式
英文:

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

问题

我已经在使用Go程序中的crewjam/saml和Keycloak IDP(在SAML模式下)中实现了SAML登录(我相信这是使用SAMLv2,但不能确定)。基本原理是,在成功登录后,IDP会向程序发送用户的SAML属性,Go SAML库将其转换为JWT并将其设置为HTTP Cookie。此时,IDP会标记用户与服务具有会话,并且用户可以通过JWT访问API。

我遇到的问题是,我不清楚如何进行注销。该库有一个用于注销的URL:

// SloURL是此主机上SAML单点注销端点的完整URL。
// 例如:https://example.com/saml/slo
SloURL url.URL

但是访问此页面只会返回404错误。

那么,我该如何告诉IDP用户的会话已结束?我应该自己删除JWT Cookie,还是会自动处理?

英文:

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?

答案1

得分: 2

以下似乎实现了我所要求的功能:

type Logout struct {
	SP *samlsp.Middleware
}

func (l *Logout) ServeHTTP(w http.ResponseWriter, r *http.Request) {
      //获取JWT信息
	session, err := l.SP.Session.GetSession(r)
	if err != nil {
		WebErrorWarn("获取注销URL会话时出错:" + err.Error(), http.StatusForbidden, w)
		return
	}
       //获取JWT信息的第二部分
	attr := session.(samlsp.JWTSessionClaims)
	if err != nil {
		WebErrorWarn("获取注销URL会话声明时出错:" + err.Error(), http.StatusForbidden, w)
		return
	}

	//将其用作注销请求的名称
	url, err := l.SP.ServiceProvider.MakeRedirectLogoutRequest(attr.Subject, "")
	if err != nil {
		WebErrorWarn("获取注销URL时出错:" + err.Error(), http.StatusInternalServerError, w)
		return
	}

    //从浏览器中删除会话令牌
	err = l.SP.Session.DeleteSession(w, r)
	if err != nil {
		WebErrorWarn("获取注销URL时出错:" + err.Error(), http.StatusInternalServerError, w)
		return
	}

    //重定向到IDP单点注销URL,并嵌入注销的SAML请求
	http.Redirect(w, r, url.String(), http.StatusFound)
}

我自己创建了一个注销URL来提供此功能

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

最后,IDP将客户端重定向回SLO URL,该URL在元数据文件中发送,并且默认为/saml/slo在crewjam/gosaml中。我只需在该URL上设置一个处理程序,向用户确认他们已注销。

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

请注意,/saml/slo URL不应受到SAML保护,否则将再次触发SAML登录。

英文:

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.

答案2

得分: 1

单点注销服务(SLO)URL在IdP元数据中,例如:

<EntityDescriptor ... entityID="https://idp.com/shibboleth">
  <IDPSSODescriptor ... >
    <SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://idp.com/idp/profile/SAML2/POST/SLO"/>
  </IDPSSODescriptor>
</EntityDescriptor>

然后,您需要构造一个注销请求发送到SLO,该请求是针对之前由IdP(使用entityID https://idp.com/shibboleth)发送的NameID,该用户在登录时发送的:

<samlp:LogoutRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="ONELOGIN_21df91a89767879fc0f7df6a1490c6000c81644d" Version="2.0" IssueInstant="2014-07-18T01:13:06Z" Destination="https://idp.com/idp/profile/SAML2/POST/SLO">
  <saml:Issuer>https://idp.com/shibboleth</saml:Issuer>
  <saml:NameID SPNameQualifier="https://idp.com/shibboleth" Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient">ONELOGIN_f92cc1834efc0f73e9c09f482fce80037a6251e7</saml:NameID>
</samlp:LogoutRequest>

然后接收注销响应

<samlp:LogoutResponse xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="_6c3737282f007720e736f0f4028feed8cb9b40291c" Version="2.0" IssueInstant="2014-07-18T01:13:06Z" Destination="http://sp.example.com/demo1/index.php?acs" InResponseTo="ONELOGIN_21df91a89767879fc0f7df6a1490c6000c81644d">
  <saml:Issuer>https://idp.com/shibboleth</saml:Issuer>
  <samlp:Status>
    <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
  </samlp:Status>
</samlp:LogoutResponse>
英文:

The Single Logout Service (SLO) URL is in the IdP metadata, e.g.

&lt;EntityDescriptor ... entityID=&quot;https://idp.com/shibboleth&quot;&gt;
  &lt;IDPSSODescriptor ... &gt;
    &lt;SingleLogoutService Binding=&quot;urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST&quot; Location=&quot;https://idp.com/idp/profile/SAML2/POST/SLO&quot;/&gt;
  &lt;/IDPSSODescriptor&gt;
&lt;/EntityDescriptor&gt;

you then construct a logout request to send to the SLO for the NameID that was previously sent by the IdP (with the entityID https://idp.com/shibboleth) when that user logged in:

&lt;samlp:LogoutRequest xmlns:samlp=&quot;urn:oasis:names:tc:SAML:2.0:protocol&quot; xmlns:saml=&quot;urn:oasis:names:tc:SAML:2.0:assertion&quot; ID=&quot;ONELOGIN_21df91a89767879fc0f7df6a1490c6000c81644d&quot; Version=&quot;2.0&quot; IssueInstant=&quot;2014-07-18T01:13:06Z&quot; Destination=&quot;https://idp.com/idp/profile/SAML2/POST/SLO&quot;&gt;
  &lt;saml:Issuer&gt;https://idp.com/shibboleth&lt;/saml:Issuer&gt;
  &lt;saml:NameID SPNameQualifier=&quot;https://idp.com/shibboleth&quot; Format=&quot;urn:oasis:names:tc:SAML:2.0:nameid-format:transient&quot;&gt;ONELOGIN_f92cc1834efc0f73e9c09f482fce80037a6251e7&lt;/saml:NameID&gt;
&lt;/samlp:LogoutRequest&gt;

and receive the logout response:

&lt;samlp:LogoutResponse xmlns:samlp=&quot;urn:oasis:names:tc:SAML:2.0:protocol&quot; xmlns:saml=&quot;urn:oasis:names:tc:SAML:2.0:assertion&quot; ID=&quot;_6c3737282f007720e736f0f4028feed8cb9b40291c&quot; Version=&quot;2.0&quot; IssueInstant=&quot;2014-07-18T01:13:06Z&quot; Destination=&quot;http://sp.example.com/demo1/index.php?acs&quot; InResponseTo=&quot;ONELOGIN_21df91a89767879fc0f7df6a1490c6000c81644d&quot;&gt;
  &lt;saml:Issuer&gt;https://idp.com/shibboleth&lt;/saml:Issuer&gt;
  &lt;samlp:Status&gt;
    &lt;samlp:StatusCode Value=&quot;urn:oasis:names:tc:SAML:2.0:status:Success&quot;/&gt;
  &lt;/samlp:Status&gt;
&lt;/samlp:LogoutResponse&gt;

答案3

得分: 0

发送注销SAML请求到http://{url}:{port}/auth/realms/{realm}/protocol/saml

英文:

Send logout SAML request to http://{url}:{port}/auth/realms/{realm}/protocol/saml

huangapple
  • 本文由 发表于 2021年10月16日 03:07:57
  • 转载请务必保留本文链接:https://go.coder-hub.com/69589522.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定