feat(remote): add remote.auth to use HTTP Header - #2976
Conversation
…askfiles
Authenticating a remote Taskfile so far meant putting the credential in the
include URL, where it leaks into error messages and the confirmation prompt.
`remote.auth` configures free-form headers per host instead, so the URL stays
safe to commit. Values may reference environment variables with ${VAR}.
The headers are injected by a RoundTripper rather than set on the request:
that covers the HEAD probe RemoteExists issues before the GET, and keeps a
cross-host redirect from carrying the credentials. They are resolved when the
request is about to be made, so a cached or offline run does not require a
token it will never send.
RemoteExists treated every non-200 as an absent file, so a server refusing the credentials ended up as "No Taskfile found", sending the user to check the URL rather than the token. A 401 now stops the search and reports the status code; the default names need the same credentials, so trying them would only add rejected requests. A 403 is left alone: it is also what a server without directory listing answers for a readable directory. That message being correct, the expansion no longer needs to refuse an undefined variable: os.ExpandEnv is inlined and expandEnv is gone. The `$$` escape goes with it, so a literal value can no longer hold a `$` followed by a name; a secret carried in an environment variable is unaffected, as os.Expand never rescans what it substituted. Header names are validated with httpguts.ValidHeaderFieldName, the table net/http itself uses, rather than a denylist that let X-Foo(bar) through. golang.org/x/net was already in the module graph, so tidy only moves it to the direct block. Finally, node_http_auth.go becomes http_auth.go: the node_ prefix is for files defining a Node type, and this one holds the auth concern of HTTPNode plus hostMatches, which reader.go uses for trusted hosts.
map[string]map[string]string named neither key. The type already existed in taskfile; package task reaches it through setup.go, so only an import was missing. Callers keep passing a plain map literal, which stays assignable to a named map type.
99b00d1 to
1b7e67d
Compare
The rebase landed these additions in the frozen copy served for the released version, because the commits predated the split into next and latest.
Same next/latest split as the docs: schema.json and schema-taskrc.json are the frozen copies served for the released version.
Aligns the syntax with the rest of Task, and lets functions compose: a
Basic credential no longer needs its base64 computed by hand. The strict
expansion this replaces was already gone, so nothing is lost by the
switch.
Only functions resolve — the configuration file is read before any
Taskfile, so {{.VAR}} has nothing to read and produces an empty header.
That is documented next to the option.
| Download bool | ||
| Offline bool | ||
| TrustedHosts []string | ||
| RemoteAuth taskfile.HostHeaders |
There was a problem hiding this comment.
I don't think Hostheaders is the correct term. It should probably just be Headers
The type is a map of host to headers, not a flat header set. Name it after that shape, and rename the fields carrying it to authHeadersByHost so the lookup step is visible at every call site. Claude-Session: https://claude.ai/code/session_01KNPMznEzkRpxFZMLisjdqL
There was a problem hiding this comment.
@vmaerten Hi, just looking at this and wondering if a more generic "headers" approach would be viable. Similar to curl with its -H option. Its the same code, just without "auth" (also drop from the schema).
Rational is that headers can be set for a number of reasons, from which authorisation is only a subset.
Just for example:
for _, headers := range config.Remote.headers {
byHost[auth.Host] = headers
}
Also, it might be useful, or necessary, to have different headers for requests against the same host. If I understand correctly, you are consolidating (last wins).
But OK, I see that you put this in the taskrc file, and not the includes, so there is no solution for that.
No description provided.