sdk/artifact/jwt.go
Louis Seubert 0f40c0b375
All checks were successful
default / ensure tests work (push) Successful in 28s
feat: artifacts client
2026-09-13 10:53:42 +02:00

84 lines
2.1 KiB
Go

package artifact
import (
"encoding/base64"
"encoding/json"
"fmt"
"os"
"strings"
)
// JobInfo returns the backend workflow run and job identifiers encoded in the
// ACTIONS_RUNTIME_TOKEN environment variable. The token is only available from
// the context of an action.
func JobInfo() (run, job string, err error) {
return jobInfo(os.Getenv)
}
func jobInfo(env func(string) string) (string, string, error) {
var empty string
rt := env("ACTIONS_RUNTIME_TOKEN")
if rt == "" {
return empty, empty, fmt.Errorf("env ACTIONS_RUNTIME_TOKEN is only available from the context of an action")
}
jt, err := newJwtDecode(rt)
if err != nil {
return empty, empty, err
}
scp, ok := jt.Payload["scp"].(string)
if !ok {
return empty, empty, fmt.Errorf("no scp in ACTIONS_RUNTIME_TOKEN")
}
for _, scopes := range strings.Split(scp, " ") {
parts := strings.Split(scopes, ":")
if parts[0] != "Actions.Results" {
continue
}
if len(parts) != 3 {
return empty, empty, fmt.Errorf("invalid scp in ACTIONS_RUNTIME_TOKEN")
}
return parts[1], parts[2], nil
}
return empty, empty, fmt.Errorf("no backend ids found")
}
type jwt struct {
Header map[string]any
Payload map[string]any
Signature string
}
func newJwtDecode(token string) (*jwt, error) {
parts := strings.Split(token, ".")
if len(parts) != 3 {
return nil, fmt.Errorf("invalid JWT: expected 3 segments")
}
decode := func(seg string) ([]byte, error) {
if l := len(seg) % 4; l != 0 {
seg += strings.Repeat("=", 4-l)
}
return base64.URLEncoding.DecodeString(seg)
}
headerBytes, err := decode(parts[0])
if err != nil {
return nil, fmt.Errorf("invalid JWT header: %v", err)
}
var header map[string]any
if err := json.Unmarshal(headerBytes, &header); err != nil {
return nil, fmt.Errorf("invalid JWT header json: %v", err)
}
payloadBytes, err := decode(parts[1])
if err != nil {
return nil, fmt.Errorf("invalid JWT payload: %v", err)
}
var payload map[string]any
if err := json.Unmarshal(payloadBytes, &payload); err != nil {
return nil, fmt.Errorf("invalid JWT payload json: %v", err)
}
return &jwt{Header: header, Payload: payload, Signature: parts[2]}, nil
}