This repository has been archived on 2026-09-13. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
workload-sts/internal/protocol/token_exchange.go
T

82 lines
2.6 KiB
Go

package protocol
import (
"fmt"
"net/url"
"regexp"
"strings"
)
const (
TokenExchangeGrantType = "urn:ietf:params:oauth:grant-type:token-exchange"
JWTTokenType = "urn:ietf:params:oauth:token-type:jwt"
AccessTokenType = "urn:ietf:params:oauth:token-type:access_token"
)
var clientIDPattern = regexp.MustCompile(`^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])$`)
type ExchangeRequest struct {
ClientID string
SubjectToken string
SubjectTokenType string
RequestedTokenType string
Audience string
Scopes []string
}
type RequestError struct {
Code string
Description string
}
func (e *RequestError) Error() string {
return fmt.Sprintf("%s: %s", e.Code, e.Description)
}
func ParseExchangeRequest(form url.Values) (ExchangeRequest, error) {
request := ExchangeRequest{
ClientID: form.Get("client_id"),
SubjectToken: form.Get("subject_token"),
SubjectTokenType: form.Get("subject_token_type"),
RequestedTokenType: form.Get("requested_token_type"),
Audience: form.Get("audience"),
Scopes: strings.Fields(form.Get("scope")),
}
if form.Get("grant_type") != TokenExchangeGrantType {
return ExchangeRequest{}, requestError("unsupported_grant_type", "unsupported grant_type")
}
if !clientIDPattern.MatchString(request.ClientID) {
return ExchangeRequest{}, requestError("invalid_request", "client_id must use 3-63 lowercase letters, digits, or interior hyphens")
}
if request.SubjectToken == "" {
return ExchangeRequest{}, requestError("invalid_request", "subject_token is required")
}
if request.SubjectTokenType != JWTTokenType {
return ExchangeRequest{}, requestError("invalid_request", "unsupported subject_token_type")
}
if request.RequestedTokenType != AccessTokenType {
return ExchangeRequest{}, requestError("invalid_request", "unsupported requested_token_type")
}
if request.Audience == "" || len(form["audience"]) != 1 {
return ExchangeRequest{}, requestError("invalid_target", "exactly one audience is required")
}
if len(request.Scopes) == 0 {
return ExchangeRequest{}, requestError("invalid_scope", "scope is required")
}
if form.Has("resource") {
return ExchangeRequest{}, requestError("invalid_target", "resource target is not supported")
}
for _, actorParameter := range []string{"actor_token", "actor_token_type"} {
if form.Has(actorParameter) {
return ExchangeRequest{}, requestError("invalid_request", "actor token is not supported")
}
}
return request, nil
}
func requestError(code, description string) *RequestError {
return &RequestError{Code: code, Description: description}
}