auth/internal/metadata/repository/http/http_metadata.go

91 lines
3.8 KiB
Go

package http
import (
"context"
"encoding/json"
"fmt"
http "github.com/valyala/fasthttp"
"source.toby3d.me/toby3d/auth/internal/domain"
"source.toby3d.me/toby3d/auth/internal/metadata"
"source.toby3d.me/toby3d/auth/internal/util"
)
type (
//nolint: tagliatelle,lll
Metadata struct {
Issuer *domain.ClientID `json:"issuer"`
AuthorizationEndpoint *domain.URL `json:"authorization_endpoint"`
IntrospectionEndpoint *domain.URL `json:"introspection_endpoint"`
RevocationEndpoint *domain.URL `json:"revocation_endpoint,omitempty"`
ServiceDocumentation *domain.URL `json:"service_documentation,omitempty"`
TokenEndpoint *domain.URL `json:"token_endpoint"`
UserinfoEndpoint *domain.URL `json:"userinfo_endpoint,omitempty"`
CodeChallengeMethodsSupported []domain.CodeChallengeMethod `json:"code_challenge_methods_supported"`
GrantTypesSupported []domain.GrantType `json:"grant_types_supported,omitempty"`
ResponseTypesSupported []domain.ResponseType `json:"response_types_supported,omitempty"`
ScopesSupported []domain.Scope `json:"scopes_supported,omitempty"`
IntrospectionEndpointAuthMethodsSupported []string `json:"introspection_endpoint_auth_methods_supported,omitempty"`
RevocationEndpointAuthMethodsSupported []string `json:"revocation_endpoint_auth_methods_supported,omitempty"`
AuthorizationResponseIssParameterSupported bool `json:"authorization_response_iss_parameter_supported,omitempty"`
}
httpMetadataRepository struct {
client *http.Client
}
)
const DefaultMaxRedirectsCount int = 10
func NewHTTPMetadataRepository(client *http.Client) metadata.Repository {
return &httpMetadataRepository{
client: client,
}
}
func (repo *httpMetadataRepository) Get(ctx context.Context, me *domain.Me) (*domain.Metadata, error) {
req := http.AcquireRequest()
defer http.ReleaseRequest(req)
req.SetRequestURI(me.String())
req.Header.SetMethod(http.MethodGet)
resp := http.AcquireResponse()
defer http.ReleaseResponse(resp)
if err := repo.client.DoRedirects(req, resp, DefaultMaxRedirectsCount); err != nil {
return nil, fmt.Errorf("failed to make a request to the client: %w", err)
}
endpoints := util.ExtractEndpoints(resp, "indieauth-metadata")
if len(endpoints) == 0 {
return nil, metadata.ErrNotExist
}
_, body, err := repo.client.Get(nil, endpoints[len(endpoints)-1].String())
if err != nil {
return nil, fmt.Errorf("failed to fetch metadata endpoint configuration: %w", err)
}
data := new(Metadata)
if err = json.Unmarshal(body, data); err != nil {
return nil, fmt.Errorf("cannot unmarshal metadata configuration: %w", err)
}
return &domain.Metadata{
AuthorizationEndpoint: data.AuthorizationEndpoint,
AuthorizationResponseIssParameterSupported: data.AuthorizationResponseIssParameterSupported,
CodeChallengeMethodsSupported: data.CodeChallengeMethodsSupported,
GrantTypesSupported: data.GrantTypesSupported,
Issuer: data.Issuer,
ResponseTypesSupported: data.ResponseTypesSupported,
ScopesSupported: data.ScopesSupported,
ServiceDocumentation: data.ServiceDocumentation,
TokenEndpoint: data.TokenEndpoint,
// TODO(toby3d): support extensions?
// Micropub: data.Micropub,
// Microsub: data.Microsub,
// TicketEndpoint: data.TicketEndpoint,
}, nil
}