Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Varied bugfixes #934

Merged
merged 10 commits into from
Jun 12, 2024
14 changes: 12 additions & 2 deletions client/incus.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,17 @@ func (r *ProtocolIncus) DoHTTP(req *http.Request) (*http.Response, error) {
return r.http.Do(req)
}

// DoWebsocket performs a websocket connection, using OIDC authentication if set.
func (r *ProtocolIncus) DoWebsocket(dialer websocket.Dialer, uri string, req *http.Request) (*websocket.Conn, *http.Response, error) {
r.addClientHeaders(req)

if r.oidcClient != nil {
return r.oidcClient.dial(dialer, uri, req)
}

return dialer.Dial(uri, req.Header)
}

// addClientHeaders sets headers from client settings.
// User-Agent (if r.httpUserAgent is set).
// X-Incus-authenticated (if r.requireAuthenticated is set).
Expand Down Expand Up @@ -436,10 +447,9 @@ func (r *ProtocolIncus) rawWebsocket(url string) (*websocket.Conn, error) {
// Create temporary http.Request using the http url, not the ws one, so that we can add the client headers
// for the websocket request.
req := &http.Request{URL: &r.httpBaseURL, Header: http.Header{}}
r.addClientHeaders(req)

// Establish the connection
conn, resp, err := dialer.Dial(url, req.Header)
conn, resp, err := r.DoWebsocket(dialer, url, req)
if err != nil {
if resp != nil {
_, _, err = incusParseResponse(resp)
Expand Down
35 changes: 35 additions & 0 deletions client/incus_oidc.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"syscall"
"time"

"github.com/gorilla/websocket"
"github.com/zitadel/oidc/v3/pkg/client/rp"
httphelper "github.com/zitadel/oidc/v3/pkg/http"
"github.com/zitadel/oidc/v3/pkg/oidc"
Expand Down Expand Up @@ -147,6 +148,40 @@ func (o *oidcClient) do(req *http.Request) (*http.Response, error) {
return resp, nil
}

// dial function executes a websocket request and handles OIDC authentication and refresh.
func (o *oidcClient) dial(dialer websocket.Dialer, uri string, req *http.Request) (*websocket.Conn, *http.Response, error) {
conn, resp, err := dialer.Dial(uri, req.Header)
if err != nil && resp == nil {
hallyn marked this conversation as resolved.
Show resolved Hide resolved
return nil, nil, err
}

// Return immediately if the error is not HTTP status unauthorized.
if conn != nil && resp.StatusCode != http.StatusUnauthorized {
return conn, resp, nil
}

issuer := resp.Header.Get("X-Incus-OIDC-issuer")
clientID := resp.Header.Get("X-Incus-OIDC-clientid")
audience := resp.Header.Get("X-Incus-OIDC-audience")

if issuer == "" || clientID == "" {
return nil, resp, err
}

err = o.refresh(issuer, clientID)
if err != nil {
err = o.authenticate(issuer, clientID, audience)
if err != nil {
return nil, resp, err
}
}

// Set the new access token in the header.
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", o.tokens.AccessToken))

return dialer.Dial(uri, req.Header)
}

// getProvider initializes a new OpenID Connect Relying Party for a given issuer and clientID.
// The function also creates a secure CookieHandler with random encryption and hash keys, and applies a series of configurations on the Relying Party.
func (o *oidcClient) getProvider(issuer string, clientID string) (rp.RelyingParty, error) {
Expand Down