Archived
82 lines
2.6 KiB
Go
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}
|
|
}
|