auth/internal/metadata/delivery/http/metadata_http.go

145 lines
5.1 KiB
Go

package http
import (
"encoding/json"
"fmt"
"net/http"
"source.toby3d.me/toby3d/auth/internal/common"
"source.toby3d.me/toby3d/auth/internal/domain"
)
type (
//nolint:tagliatelle // https://indieauth.net/source/#indieauth-server-metadata
Response struct {
// URL of a page containing human-readable information that
// developers might need to know when using the server.
ServiceDocumentation string `json:"service_documentation,omitempty"`
// The Authorization Endpoint.
AuthorizationEndpoint string `json:"authorization_endpoint"`
// The Token Endpoint.
TokenEndpoint string `json:"token_endpoint"`
// The Introspection Endpoint.
IntrospectionEndpoint string `json:"introspection_endpoint"`
// The User Info Endpoint.
UserinfoEndpoint string `json:"userinfo_endpoint,omitempty"`
// The Revocation Endpoint.
RevocationEndpoint string `json:"revocation_endpoint,omitempty"`
// The server's issuer identifier.
Issuer string `json:"issuer"`
// JSON array containing the value "none".
RevocationEndpointAuthMethodsSupported []string `json:"revocation_endpoint_auth_methods_supported,omitempty"` //nolint:lll
// JSON array containing the response_type values supported.
ResponseTypesSupported []string `json:"response_types_supported,omitempty"`
// JSON array containing grant type values supported.
GrantTypesSupported []string `json:"grant_types_supported,omitempty"`
// JSON array containing scope values supported by the
// IndieAuth server.
ScopesSupported []string `json:"scopes_supported,omitempty"`
// JSON array containing the methods supported for PKCE.
CodeChallengeMethodsSupported []string `json:"code_challenge_methods_supported"`
// JSON array containing a list of client authentication methods
// supported by this introspection endpoint.
IntrospectionEndpointAuthMethodsSupported []string `json:"introspection_endpoint_auth_methods_supported,omitempty"` //nolint:lll
// Boolean parameter indicating whether the authorization server
// provides the iss parameter.
AuthorizationResponseIssParameterSupported bool `json:"authorization_response_iss_parameter_supported,omitempty"` //nolint:lll
}
Handler struct {
metadata *domain.Metadata
}
)
func NewHandler(metadata *domain.Metadata) *Handler {
return &Handler{metadata: metadata}
}
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.Method != "" && r.Method != http.MethodGet {
http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
return
}
w.Header().Set(common.HeaderContentType, common.MIMEApplicationJSONCharsetUTF8)
_ = json.NewEncoder(w).Encode(NewResponse(h.metadata))
}
func NewResponse(src *domain.Metadata) *Response {
out := &Response{
AuthorizationEndpoint: "",
AuthorizationResponseIssParameterSupported: src.AuthorizationResponseIssParameterSupported,
CodeChallengeMethodsSupported: make([]string, 0),
GrantTypesSupported: make([]string, 0),
IntrospectionEndpoint: "",
IntrospectionEndpointAuthMethodsSupported: make([]string, 0),
Issuer: "",
ResponseTypesSupported: make([]string, 0),
RevocationEndpoint: "",
RevocationEndpointAuthMethodsSupported: make([]string, 0),
ScopesSupported: make([]string, 0),
ServiceDocumentation: "",
TokenEndpoint: "",
UserinfoEndpoint: "",
}
for i := range src.ScopesSupported {
out.ScopesSupported = append(out.ScopesSupported, src.ScopesSupported[i].String())
}
for i := range src.ResponseTypesSupported {
out.ResponseTypesSupported = append(out.ResponseTypesSupported, src.ResponseTypesSupported[i].String())
}
for i := range src.GrantTypesSupported {
out.GrantTypesSupported = append(out.GrantTypesSupported, src.GrantTypesSupported[i].String())
}
for i := range src.CodeChallengeMethodsSupported {
out.CodeChallengeMethodsSupported = append(out.CodeChallengeMethodsSupported,
src.CodeChallengeMethodsSupported[i].String())
}
out.IntrospectionEndpointAuthMethodsSupported = append(out.IntrospectionEndpointAuthMethodsSupported,
src.IntrospectionEndpointAuthMethodsSupported...)
// NOTE(toby3d) = If a revocation endpoint is provided, this property
// should also be provided with the value ["none"], since the omission
// of this value defaults to client_secret_basic according to RFC8414.
out.RevocationEndpointAuthMethodsSupported = append(out.RevocationEndpointAuthMethodsSupported,
src.RevocationEndpointAuthMethodsSupported...)
for dst, src := range map[*string]fmt.Stringer{
&out.AuthorizationEndpoint: src.AuthorizationEndpoint,
&out.IntrospectionEndpoint: src.IntrospectionEndpoint,
&out.Issuer: src.Issuer,
&out.RevocationEndpoint: src.RevocationEndpoint,
&out.ServiceDocumentation: src.ServiceDocumentation,
&out.TokenEndpoint: src.TokenEndpoint,
&out.UserinfoEndpoint: src.UserinfoEndpoint,
} {
if src == nil {
continue
}
*dst = src.String()
}
return out
}