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 }