Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

g1t.sh

Commit

The REST API takes `Authorization: token <token>` as well as `Bearer <token>`, as GitHub does. Octokit, and so `@actions/github` and every action built on it, such as `actions/checkout` asking for a repository's default branch and `actions/github-script`, sends the job's token in that form, which g1t read as no token at all: a private repository answered 404 and rate limits counted the request by address. The API reference says so.

Claude-Session:
https://claude.ai/code/session_01PnwTRSNaC7vEHTAuBzjhrn
Claudeagent and Claudeagentcommitted Parentb198e74Browse files
3 files+21−110/3 viewed
+5−8
127127 request.json().await.unwrap_or(Value::Null)
128128 }
129129
130−/// Who a request's `Authorization: Bearer g1t_…` names. A missing token is
131−/// an anonymous viewer; a wrong one is refused, so that a typo does not
132−/// silently look signed out.
130+/// Who a request's `Authorization: Bearer g1t_…` (or `token g1t_…`, as
131+/// Octokit sends it) names. A missing token is an anonymous viewer; a
132+/// wrong one is refused, so that a typo does not silently look signed out.
133133 async fn authenticate(
134134 request: &Request,
135135 services: &Services,
136136 ) -> Result<std::result::Result<Viewer, Response>> {
137137 let header = request.headers().get("authorization")?.unwrap_or_default();
138− let token = match header.split_once(' ') {
139− Some((scheme, token)) if scheme.eq_ignore_ascii_case("bearer") && !token.is_empty() => {
140− token.trim()
141− }
142− _ => return Ok(Ok(None)),
138+ let Some(token) = limits::bearer(&header) else {
139+ return Ok(Ok(None));
143140 };
144141 let viewer: Viewer = g1t_kit::call(
145142 &services.identity,
+12−3
5555 Sha256::digest(token.as_bytes())[..8].iter().map(|byte| format!("{byte:02x}")).collect()
5656 }
5757
58−/// The bearer token of an `Authorization` header, if it has one.
58+/// The access token of an `Authorization` header, if it has one: `Bearer
59+/// <token>`, or `token <token>`, the form GitHub also takes and Octokit
60+/// (`@actions/github`, so `actions/checkout` and `actions/github-script`)
61+/// sends.
5962 pub fn bearer(header: &str) -> Option<&str> {
6063 match header.split_once(' ') {
61− Some((scheme, token)) if scheme.eq_ignore_ascii_case("bearer") => Some(token.trim()).filter(|t| !t.is_empty()),
64+ Some((scheme, token)) if scheme.eq_ignore_ascii_case("bearer") || scheme.eq_ignore_ascii_case("token") => {
65+ Some(token.trim()).filter(|t| !t.is_empty())
66+ }
6267 _ => None,
6368 }
6469 }
133138 }
134139
135140 #[test]
136− fn only_bearer_tokens_are_read() {
141+ fn bearer_and_token_schemes_are_read() {
137142 assert_eq!(bearer("Bearer g1t_abc "), Some("g1t_abc"));
138143 assert_eq!(bearer("bearer g1t_abc"), Some("g1t_abc"));
144+ // Octokit's form, as `actions/checkout` sends it for a default branch.
145+ assert_eq!(bearer("token g1t_abc"), Some("g1t_abc"));
146+ assert_eq!(bearer("Token g1t_abc"), Some("g1t_abc"));
147+ assert_eq!(bearer("token "), None);
139148 assert_eq!(bearer("Basic dXNlcjpwYXNz"), None);
140149 assert_eq!(bearer("Bearer "), None);
141150 assert_eq!(bearer(""), None);
+4−0
4747 -H "Authorization: Bearer $G1T_TOKEN"
4848 ```
4949
50+`Authorization: token $G1T_TOKEN`, the other form GitHub takes, works the
51+same, so Octokit and the actions built on it, such as `actions/checkout`
52+and `actions/github-script`, are signed in with the token they are given.
53+
5054 Public data can be read without a token. A token that is not valid is
5155 rejected with `401` rather than treated as anonymous. Each endpoint's page
5256 says whether it needs a token.

Comments

Sign in to comment.