diff --git a/ocis-pkg/oidc/client.go b/ocis-pkg/oidc/client.go index 670e77176..bee227fdd 100644 --- a/ocis-pkg/oidc/client.go +++ b/ocis-pkg/oidc/client.go @@ -138,7 +138,7 @@ func (c *oidcClient) lookupWellKnownOpenidConfiguration(ctx context.Context) err } c.provider = &p c.algorithms = algs - c.remoteKeySet = gOidc.NewRemoteKeySet(ctx, p.JwksURI) + c.remoteKeySet = gOidc.NewRemoteKeySet(gOidc.ClientContext(ctx, c.httpClient), p.JwksURI) } return nil } @@ -249,7 +249,7 @@ func (c *oidcClient) UserInfo(ctx context.Context, tokenSource oauth2.TokenSourc ct := resp.Header.Get("Content-Type") mediaType, _, parseErr := mime.ParseMediaType(ct) if parseErr == nil && mediaType == "application/jwt" { - payload, err := c.remoteKeySet.VerifySignature(ctx, string(body)) + payload, err := c.remoteKeySet.VerifySignature(gOidc.ClientContext(ctx, c.httpClient), string(body)) if err != nil { return nil, fmt.Errorf("oidc: invalid userinfo jwt signature %v", err) } @@ -318,6 +318,9 @@ func (c *oidcClient) verifyAccessTokenJWT(token string) (jwt.RegisteredClaims, [ } func (c *oidcClient) VerifyLogoutToken(ctx context.Context, rawToken string) (*LogoutToken, error) { + if err := c.lookupWellKnownOpenidConfiguration(ctx); err != nil { + return nil, err + } jws, err := jose.ParseSigned(rawToken) if err != nil { return nil, err @@ -352,7 +355,7 @@ func (c *oidcClient) VerifyLogoutToken(ctx context.Context, rawToken string) (*L } // Check issuer. if !c.skipIssuerValidation && token.Issuer != c.issuer { - return nil, fmt.Errorf("oidc: id token issued by a different provider, expected %q got %q", c.issuer, token.Issuer) + return nil, fmt.Errorf("oidc: logout token issued by a different provider, expected %q got %q", c.issuer, token.Issuer) } // If a client ID has been provided, make sure it's part of the audience. SkipClientIDCheck must be true if ClientID is empty. @@ -370,10 +373,10 @@ func (c *oidcClient) VerifyLogoutToken(ctx context.Context, rawToken string) (*L switch len(jws.Signatures) { case 0: - return nil, fmt.Errorf("oidc: id token not signed") + return nil, fmt.Errorf("oidc: logout token not signed") case 1: default: - return nil, fmt.Errorf("oidc: multiple signatures on id token not supported") + return nil, fmt.Errorf("oidc: multiple signatures on logout token not supported") } sig := jws.Signatures[0] @@ -383,10 +386,10 @@ func (c *oidcClient) VerifyLogoutToken(ctx context.Context, rawToken string) (*L } if !contains(supportedSigAlgs, sig.Header.Algorithm) { - return nil, fmt.Errorf("oidc: id token signed with unsupported algorithm, expected %q got %q", supportedSigAlgs, sig.Header.Algorithm) + return nil, fmt.Errorf("oidc: logout token signed with unsupported algorithm, expected %q got %q", supportedSigAlgs, sig.Header.Algorithm) } - gotPayload, err := c.remoteKeySet.VerifySignature(ctx, rawToken) + gotPayload, err := c.remoteKeySet.VerifySignature(gOidc.ClientContext(ctx, c.httpClient), rawToken) if err != nil { return nil, fmt.Errorf("failed to verify signature: %v", err) } diff --git a/services/proxy/pkg/command/server.go b/services/proxy/pkg/command/server.go index d4eb5b0bb..c91387676 100644 --- a/services/proxy/pkg/command/server.go +++ b/services/proxy/pkg/command/server.go @@ -206,23 +206,31 @@ func (h *StaticRouteHandler) handler() http.Handler { return m } +type jse struct { + Error string `json:"error"` + ErrorDescription string `json:"error_description"` +} + // handle backchannel logout requests as per https://openid.net/specs/openid-connect-backchannel-1_0.html#BCRequest func (h *StaticRouteHandler) backchannelLogout(w http.ResponseWriter, r *http.Request) { // parse the application/x-www-form-urlencoded POST request if err := r.ParseForm(); err != nil { render.Status(r, http.StatusBadRequest) + render.JSON(w, r, jse{Error: "invalid_request", ErrorDescription: err.Error()}) return } logoutToken, err := h.oidcClient.VerifyLogoutToken(r.Context(), r.PostFormValue("logout_token")) if err != nil { render.Status(r, http.StatusBadRequest) + render.JSON(w, r, jse{Error: "invalid_request", ErrorDescription: err.Error()}) return } records, err := h.userInfoCache.Read(logoutToken.SessionId) if errors.Is(err, microstore.ErrNotFound) || len(records) == 0 { render.Status(r, http.StatusOK) + render.JSON(w, r, nil) return } @@ -232,6 +240,7 @@ func (h *StaticRouteHandler) backchannelLogout(w http.ResponseWriter, r *http.Re // Spec requires us to return a 400 BadRequest when the session could not be destroyed h.logger.Err(err).Msg("could not delete user info from cache") render.Status(r, http.StatusBadRequest) + render.JSON(w, r, jse{Error: "invalid_request", ErrorDescription: err.Error()}) return } } @@ -240,6 +249,7 @@ func (h *StaticRouteHandler) backchannelLogout(w http.ResponseWriter, r *http.Re _ = h.userInfoCache.Delete(logoutToken.SessionId) render.Status(r, http.StatusOK) + render.JSON(w, r, nil) } func loadMiddlewares(ctx context.Context, logger log.Logger, cfg *config.Config, userInfoCache microstore.Store) alice.Chain {