diff --git a/action/src/index.ts b/action/src/index.ts index d8e69e00..392d833f 100644 --- a/action/src/index.ts +++ b/action/src/index.ts @@ -8,8 +8,6 @@ interface GetTokenParams { } interface GetTokenPayload { - github_token?: string; - id_token?: string; api_url: string; repository: string; sha: string; @@ -80,17 +78,20 @@ export async function assumeRole(params: GetTokenParams) { repository: GITHUB_REPOSITORY, sha: GITHUB_SHA, }; + const headers: { [name: string]: string } = {}; + let token: string; if (isIdTokenAvailable()) { - payload.id_token = await core.getIDToken(params.audience); + token = await core.getIDToken(params.audience); } else { validateGitHubToken(params.githubToken); - payload.github_token = params.githubToken; + token = params.githubToken; } + headers["Authorization"] = `Bearer ${token}`; const client = new http.HttpClient("actions-github-app-token"); - const result = await client.postJson(params.providerEndpoint, payload); - if (result.statusCode !== 200) { + const result = await client.postJson(params.providerEndpoint, payload, headers); + if (result.statusCode !== http.HttpCodes.OK) { const resp = result.result as GetTokenError; core.setFailed(resp.message); return; diff --git a/provider/github-app-token/github-app-token.go b/provider/github-app-token/github-app-token.go index c4d12133..c567d091 100644 --- a/provider/github-app-token/github-app-token.go +++ b/provider/github-app-token/github-app-token.go @@ -86,11 +86,9 @@ func NewHandler() (*Handler, error) { } type requestBody struct { - GitHubToken string `json:"github_token,omitempty"` - IDToken string `json:"id_token,omitempty"` - Repository string `json:"repository"` - SHA string `json:"sha"` - APIURL string `json:"api_url"` + Repository string `json:"repository"` + SHA string `json:"sha"` + APIURL string `json:"api_url"` } type responseBody struct { @@ -131,8 +129,13 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { }) return } + token, err := h.getAuthToken(r.Header) + if err != nil { + h.handleError(w, r, err) + return + } - resp, err := h.handle(ctx, payload) + resp, err := h.handle(ctx, token, payload) if err != nil { h.handleError(w, r, err) return @@ -144,34 +147,32 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { } } -func (h *Handler) handle(ctx context.Context, req *requestBody) (*responseBody, error) { +func (h *Handler) handle(ctx context.Context, token string, req *requestBody) (*responseBody, error) { if err := h.github.ValidateAPIURL(req.APIURL); err != nil { return nil, &validationError{ message: err.Error(), } } - if req.IDToken != "" { - _, err := h.github.ParseIDToken(ctx, req.IDToken) + + // authorize the request + var owner, repo string + if id, err := h.github.ParseIDToken(ctx, token); err == nil { + owner, repo, err = splitOwnerRepo(id.Repository) if err != nil { - return nil, &validationError{ - message: "invalid JSON Web Token", - } + return nil, err } - } else if req.GitHubToken != "" { - err := h.validateGitHubToken(ctx, req) + } else { + err := h.validateGitHubToken(ctx, token, req) if err != nil { return nil, err } - } else { - return nil, &validationError{ - message: "github_token or id_token is required", + owner, repo, err = splitOwnerRepo(req.Repository) + if err != nil { + return nil, err } } - owner, repo, err := splitOwnerRepo(req.Repository) - if err != nil { - return nil, err - } + // issue a new access token inst, err := h.github.GetReposInstallation(ctx, owner, repo) if err != nil { var ghErr *github.ErrUnexpectedStatusCode @@ -189,7 +190,7 @@ func (h *Handler) handle(ctx context.Context, req *requestBody) (*responseBody, } return nil, fmt.Errorf("failed to get resp's installation: %w", err) } - token, err := h.github.CreateAppAccessToken(ctx, inst.ID, &github.CreateAppAccessTokenRequest{ + resp, err := h.github.CreateAppAccessToken(ctx, inst.ID, &github.CreateAppAccessTokenRequest{ Repositories: []string{repo}, }) if err != nil { @@ -197,7 +198,7 @@ func (h *Handler) handle(ctx context.Context, req *requestBody) (*responseBody, } return &responseBody{ - GitHubToken: token.Token, + GitHubToken: resp.Token, }, nil } @@ -243,15 +244,31 @@ func (h *Handler) handleMethodNotAllowed(w http.ResponseWriter) { w.Write(data) } -func (h *Handler) validateGitHubToken(ctx context.Context, req *requestBody) error { +func (h *Handler) getAuthToken(header http.Header) (string, error) { + const prefix = "Bearer " + v := header.Get("Authorization") + if len(v) < len(prefix) { + return "", &validationError{ + message: "invalid Authorization header", + } + } + if !strings.EqualFold(v[:len(prefix)], prefix) { + return "", &validationError{ + message: "invalid Authorization header", + } + } + return v[len(prefix):], nil +} + +func (h *Handler) validateGitHubToken(ctx context.Context, token string, req *requestBody) error { // early check of the token prefix // ref. https://github.blog/changelog/2021-03-31-authentication-token-format-updates-are-generally-available/ - if len(req.GitHubToken) < 4 { + if len(token) < 4 { return &validationError{ message: "GITHUB_TOKEN has invalid format", } } - switch req.GitHubToken[:4] { + switch token[:4] { case "ghp_": // Personal Access Tokens return &validationError{ @@ -281,9 +298,9 @@ func (h *Handler) validateGitHubToken(ctx context.Context, req *requestBody) err message: "GITHUB_TOKEN looks like Personal Access Token. `github-token` must be `${{ github.token }}` or `${{ secrets.GITHUB_TOKEN }}`.", } } - resp, err := h.updateCommitStatus(ctx, req, &github.CreateStatusRequest{ + resp, err := h.updateCommitStatus(ctx, token, req, &github.CreateStatusRequest{ State: github.CommitStateSuccess, - Description: "valid github token", + Description: "valid GitHub token", Context: commitStatusContext, }) if err != nil { @@ -318,10 +335,10 @@ func splitOwnerRepo(fullname string) (owner, repo string, err error) { return } -func (h *Handler) updateCommitStatus(ctx context.Context, req *requestBody, status *github.CreateStatusRequest) (*github.CreateStatusResponse, error) { +func (h *Handler) updateCommitStatus(ctx context.Context, token string, req *requestBody, status *github.CreateStatusRequest) (*github.CreateStatusResponse, error) { owner, repo, err := splitOwnerRepo(req.Repository) if err != nil { return nil, err } - return h.github.CreateStatus(ctx, req.GitHubToken, owner, repo, req.SHA, status) + return h.github.CreateStatus(ctx, token, owner, repo, req.SHA, status) } diff --git a/provider/github-app-token/github-app-token_test.go b/provider/github-app-token/github-app-token_test.go index 7bbc86d9..140aa9f4 100644 --- a/provider/github-app-token/github-app-token_test.go +++ b/provider/github-app-token/github-app-token_test.go @@ -77,10 +77,9 @@ func TestValidateGitHubToken(t *testing.T) { }, }, } - err := h.validateGitHubToken(context.Background(), &requestBody{ - GitHubToken: "ghs_dummyGitHubToken", - Repository: "fuller-inc/actions-aws-assume-role", - SHA: "e3a45c6c16c1464826b36a598ff39e6cc98c4da4", + err := h.validateGitHubToken(context.Background(), "ghs_dummyGitHubToken", &requestBody{ + Repository: "fuller-inc/actions-aws-assume-role", + SHA: "e3a45c6c16c1464826b36a598ff39e6cc98c4da4", }) if err != nil { t.Error(err) @@ -97,10 +96,9 @@ func TestValidateGitHubToken_PermissionError(t *testing.T) { }, }, } - err := h.validateGitHubToken(context.Background(), &requestBody{ - GitHubToken: "ghs_dummyGitHubToken", - Repository: "fuller-inc/actions-aws-assume-role", - SHA: "e3a45c6c16c1464826b36a598ff39e6cc98c4da4", + err := h.validateGitHubToken(context.Background(), "ghs_dummyGitHubToken", &requestBody{ + Repository: "fuller-inc/actions-aws-assume-role", + SHA: "e3a45c6c16c1464826b36a598ff39e6cc98c4da4", }) if err == nil { t.Error("want error, but not") @@ -129,10 +127,9 @@ func TestValidateGitHubToken_InvalidCreator(t *testing.T) { }, }, } - err := h.validateGitHubToken(context.Background(), &requestBody{ - GitHubToken: "ghs_dummyGitHubToken", - Repository: "fuller-inc/actions-aws-assume-role", - SHA: "e3a45c6c16c1464826b36a598ff39e6cc98c4da4", + err := h.validateGitHubToken(context.Background(), "ghs_dummyGitHubToken", &requestBody{ + Repository: "fuller-inc/actions-aws-assume-role", + SHA: "e3a45c6c16c1464826b36a598ff39e6cc98c4da4", }) if err == nil { t.Error("want error, but not")