| 1 |
|
package authn |
| 2 |
|
|
| 3 |
|
import ( |
| 4 |
|
"net/http" |
| 5 |
|
"strings" |
| 6 |
|
) |
| 7 |
|
|
| 8 |
|
// bearerScheme is the Authorization scheme agents present the token under. |
| 9 |
|
// Matched case-insensitively, as RFC 7235 requires. |
| 10 |
|
const bearerScheme = "bearer" |
| 11 |
|
|
| 12 |
|
// HeaderAgent and HeaderAgentSession carry the mandatory provenance an agent |
| 13 |
|
// must send alongside its token. They are named after the git trailers they end |
| 14 |
|
// up in, so that what an agent sends and what a reviewer reads in `git log` are |
| 15 |
|
// spelled the same way. |
| 16 |
|
// |
| 17 |
|
// There is deliberately no X-Agent-Base header: the base revision is the |
| 18 |
|
// `If-Match` value the write plane already defines, and giving it a second |
| 19 |
|
// spelling is exactly how REST and MCP end up disagreeing about what it means. |
| 20 |
|
const ( |
| 21 |
|
HeaderAgent = "X-Agent" |
| 22 |
|
HeaderAgentSession = "X-Agent-Session" |
| 23 |
|
) |
| 24 |
|
|
| 25 |
|
// BearerFromRequest returns the token from an "Authorization: Bearer <token>" |
| 26 |
|
// header, or "" when the header is absent or uses another scheme. Anything |
| 27 |
|
// after the scheme is returned verbatim apart from surrounding whitespace: the |
| 28 |
|
// token is opaque to this function, and validating its grammar is |
| 29 |
|
// BearerValidator's job and not the header parser's. |
| 30 |
45 |
func BearerFromRequest(r *http.Request) string { |
| 31 |
45 |
h := r.Header.Get("Authorization") |
| 32 |
45 |
if h == "" { |
| 33 |
16 |
return "" |
| 34 |
16 |
} |
| 35 |
29 |
scheme, rest, ok := strings.Cut(h, " ") |
| 36 |
29 |
if !ok || !strings.EqualFold(scheme, bearerScheme) { |
| 37 |
2 |
return "" |
| 38 |
2 |
} |
| 39 |
27 |
return strings.TrimSpace(rest) |
| 40 |
|
} |