From bd47b5ddc4eeb1ff8fb42ca2ed5379620eb9cdbc Mon Sep 17 00:00:00 2001 From: David Sharnoff Date: Mon, 14 Nov 2022 08:01:19 -0800 Subject: [PATCH] feat: support EndSession with RelyingParty client (#230) * feat: support EndSession with RelyingPart client * do not error if OP does not provide a redirect * undo that last change, but noice error returns from EndSession * ioutil.ReadAll, for now --- pkg/client/client.go | 38 ++++++++++++++++++++++++++++++++++ pkg/client/rp/relying_party.go | 11 ++++++++++ 2 files changed, 49 insertions(+) diff --git a/pkg/client/client.go b/pkg/client/client.go index d6d27f7..e286a00 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -1,7 +1,11 @@ package client import ( + "errors" + "fmt" + "io/ioutil" "net/http" + "net/url" "reflect" "strings" "time" @@ -71,6 +75,40 @@ func callTokenEndpoint(request interface{}, authFn interface{}, caller TokenEndp }, nil } +type EndSessionCaller interface { + GetEndSessionEndpoint() string + HttpClient() *http.Client +} + +func CallEndSessionEndpoint(request interface{}, authFn interface{}, caller EndSessionCaller) (*url.URL, error) { + req, err := httphelper.FormRequest(caller.GetEndSessionEndpoint(), request, Encoder, authFn) + if err != nil { + return nil, err + } + client := caller.HttpClient() + client.CheckRedirect = func(_ *http.Request, _ []*http.Request) error { + return http.ErrUseLastResponse + } + resp, err := client.Do(req) + defer resp.Body.Close() + if resp.StatusCode < 200 || resp.StatusCode >= 400 { + // TODO: switch to io.ReadAll when go1.15 support is retired + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + return nil, fmt.Errorf("EndSession failure, %d status code: %s", resp.StatusCode, string(body)) + } + location, err := resp.Location() + if err != nil { + if errors.Is(err, http.ErrNoLocation) { + return nil, nil + } + return nil, err + } + return location, nil +} + func NewSignerFromPrivateKeyByte(key []byte, keyID string) (jose.Signer, error) { privateKey, err := crypto.BytesToPrivateKey(key) if err != nil { diff --git a/pkg/client/rp/relying_party.go b/pkg/client/rp/relying_party.go index cb271e7..39c2fe7 100644 --- a/pkg/client/rp/relying_party.go +++ b/pkg/client/rp/relying_party.go @@ -5,6 +5,7 @@ import ( "encoding/base64" "errors" "net/http" + "net/url" "strings" "time" @@ -573,3 +574,13 @@ func RefreshAccessToken(rp RelyingParty, refreshToken, clientAssertion, clientAs } return client.CallTokenEndpoint(request, tokenEndpointCaller{RelyingParty: rp}) } + +func EndSession(rp RelyingParty, idToken, optionalRedirectURI, optionalState string) (*url.URL, error) { + request := oidc.EndSessionRequest{ + IdTokenHint: idToken, + ClientID: rp.OAuthConfig().ClientID, + PostLogoutRedirectURI: optionalRedirectURI, + State: optionalState, + } + return client.CallEndSessionEndpoint(request, nil, rp) +}