Ctrl K
ComunidadGuías…

coderd/workspaceapps/cookies.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package workspaceapps

import (
	"crypto/sha256"
	"encoding/hex"
	"net/http"

	"github.com/coder/coder/v2/coderd/httpmw"
	"github.com/coder/coder/v2/codersdk"
)

type AppCookies struct {
	PathAppSessionToken      string
	SubdomainAppSessionToken string
	SignedAppToken           string
}

// NewAppCookies returns the cookie names for the app session token for the
// given hostname. The subdomain cookie is unique per workspace proxy and is
// based on a hash of the workspace proxy subdomain hostname. See
// SubdomainAppSessionTokenCookie for more details.
func NewAppCookies(hostname string) AppCookies {
	return AppCookies{
		PathAppSessionToken:      codersdk.PathAppSessionTokenCookie,
		SubdomainAppSessionToken: SubdomainAppSessionTokenCookie(hostname),
		SignedAppToken:           codersdk.SignedAppTokenCookie,
	}
}

// CookieNameForAccessMethod returns the cookie name for the long-lived session
// token for the given access method.
func (c AppCookies) CookieNameForAccessMethod(accessMethod AccessMethod) string {
	if accessMethod == AccessMethodSubdomain {
		return c.SubdomainAppSessionToken
	}
	// Path-based and terminal apps are on the same domain:
	return c.PathAppSessionToken
}

// SubdomainAppSessionTokenCookie returns the cookie name for the subdomain app
// session token. This is unique per workspace proxy and is based on a hash of
// the workspace proxy subdomain hostname.
//
// The reason the cookie needs to be unique per workspace proxy is to avoid
// cookies from one proxy (e.g. the primary) being sent on requests to a
// different proxy underneath the wildcard.
//
// E.g. `*.dev.coder.com` and `*.sydney.dev.coder.com`
//
// If you have an expired cookie on the primary proxy (valid for
// `*.dev.coder.com`), your browser will send it on all requests to the Sydney
// proxy as it's underneath the wildcard.
//
// By using a unique cookie name per workspace proxy, we can avoid this issue.
func SubdomainAppSessionTokenCookie(hostname string) string {
	hash := sha256.Sum256([]byte(hostname))
	// 16 bytes of uniqueness is probably enough.
	str := hex.EncodeToString(hash[:16])
	return codersdk.SubdomainAppSessionTokenCookie + "_" + str
}

// AppConnectSessionTokenFromRequest returns the session token from the request
// if it exists. The access method is used to determine which cookie name to
// use.
//
// We use different cookie names for path apps and for subdomain apps to avoid
// both being set and sent to the server at the same time and the server using
// the wrong value.
//
// We use different cookie names for:
// - path apps: coder_path_app_session_token
// - subdomain apps: coder_subdomain_app_session_token_{unique_hash}
//
// We prefer the access-method-specific cookie first, then fall back to standard
// Coder token extraction (query parameters, Coder-Session-Token header, etc.).
func (c AppCookies) TokenFromRequest(r *http.Request, accessMethod AccessMethod) string {
	// Prefer the access-method-specific cookie first.
	//
	// Workspace app requests commonly include an `Authorization` header intended
	// for the upstream app (e.g. API calls). `httpmw.APITokenFromRequest` supports
	// RFC 6750 bearer tokens, so if we consult it first we'd incorrectly treat
	// that upstream header as a Coder session token and ignore the app session
	// cookie, breaking token renewal for subdomain apps.
	cookie, err := r.Cookie(c.CookieNameForAccessMethod(accessMethod))
	if err == nil && cookie.Value != "" {
		return cookie.Value
	}

	// Fall back to standard Coder token extraction (session cookie, query param,
	// Coder-Session-Token header, and then Authorization: Bearer).
	token := httpmw.APITokenFromRequest(r)
	if token != "" {
		return token
	}

	return ""
}