Allow CORS origins from CORS_ALLOWED_ORIGINS
Echo compares allowed origins literally, so "https://*" and "http://*" never matched a real origin and no CORS headers were sent. The allowed origins now come from CORS_ALLOWED_ORIGINS (comma-separated, checked at startup). When it is empty, only same-origin requests work, which is enough for the bundled frontend. Credentials are no longer allowed since the API uses Bearer tokens. Browsers may now send Range and X-Queue-Id and read X-Queue-Id and Content-Range.
This commit is contained in:
1 parent
a705d7b476
commit
d31bf3551c
4 files changed
+85
-8
No files matched your search
@@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -38,6 +39,10 @@ type Config struct {
|
||||
// TrustedProxies are CIDR ranges whose X-Forwarded-For header is trusted when resolving client IPs.
|
||||
TrustedProxies []*net.IPNet
|
||||
|
||||
// CORSAllowedOrigins are the browser origins, like https://music.example.com, that may call the API
|
||||
// from another site. Empty means only same-origin requests, which is enough for the bundled frontend.
|
||||
CORSAllowedOrigins []string
|
||||
|
||||
ReleasesURL string
|
||||
}
|
||||
|
||||
@@ -91,6 +96,11 @@ func load(getenv func(string) string) (Config, error) {
|
||||
errs = append(errs, fmt.Errorf("TRUSTED_PROXIES: %w", err))
|
||||
}
|
||||
|
||||
cfg.CORSAllowedOrigins, err = parseOrigins(getenv("CORS_ALLOWED_ORIGINS"))
|
||||
if err != nil {
|
||||
errs = append(errs, fmt.Errorf("CORS_ALLOWED_ORIGINS: %w", err))
|
||||
}
|
||||
|
||||
return cfg, errors.Join(errs...)
|
||||
}
|
||||
|
||||
@@ -110,6 +120,27 @@ func parseCIDRs(value string) ([]*net.IPNet, error) {
|
||||
return nets, nil
|
||||
}
|
||||
|
||||
// parseOrigins parses a comma-separated list of origins (scheme://host[:port], nothing else).
|
||||
func parseOrigins(value string) ([]string, error) {
|
||||
var origins []string
|
||||
for part := range strings.SplitSeq(value, ",") {
|
||||
part = strings.TrimSpace(part)
|
||||
if part == "" {
|
||||
continue
|
||||
}
|
||||
u, err := url.Parse(part)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if (u.Scheme != "http" && u.Scheme != "https") || u.Host == "" || u.Path != "" || u.RawQuery != "" ||
|
||||
u.Fragment != "" || u.User != nil || strings.Contains(u.Host, "*") {
|
||||
return nil, fmt.Errorf("%q is not an origin like https://music.example.com", part)
|
||||
}
|
||||
origins = append(origins, u.Scheme+"://"+u.Host)
|
||||
}
|
||||
return origins, nil
|
||||
}
|
||||
|
||||
func withDefault(value, fallback string) string {
|
||||
if value == "" {
|
||||
return fallback
|
||||
|
||||
Reference in new issue
Block a user