👽 Refactored metadata support due IndieAuth spec changes

This commit is contained in:
Maxim Lebedev 2022-02-17 21:13:11 +05:00
parent 0dcfdfc1ac
commit 02c459b5c2
Signed by: toby3d
GPG Key ID: 1F14E25B7C119FC5
3 changed files with 79 additions and 52 deletions

View File

@ -13,58 +13,51 @@ import (
type ( type (
//nolint: tagliatelle // https://indieauth.net/source/#indieauth-server-metadata //nolint: tagliatelle // https://indieauth.net/source/#indieauth-server-metadata
MetadataResponse struct { MetadataResponse struct {
// The server's issuer identifier. The issuer identifier is a // The server's issuer identifier.
// URL that uses the "https" scheme and has no query or fragment Issuer string `json:"issuer"`
// components. The identifier MUST be a prefix of the
// indieauth-metadata URL. e.g. for an indieauth-metadata
// endpoint
// https://example.com/.well-known/oauth-authorization-server,
// the issuer URL could be https://example.com/, or for a
// metadata URL of
// https://example.com/wp-json/indieauth/1.0/metadata, the
// issuer URL could be https://example.com/wp-json/indieauth/1.0
Issuer *domain.ClientID `json:"issuer"`
// The Authorization Endpoint. // The Authorization Endpoint.
AuthorizationEndpoint *domain.URL `json:"authorization_endpoint"` AuthorizationEndpoint string `json:"authorization_endpoint"`
// The Token Endpoint. // The Token Endpoint.
TokenEndpoint *domain.URL `json:"token_endpoint"` TokenEndpoint string `json:"token_endpoint"`
// The Introspection Endpoint.
IntrospectionEndpoint string `json:"introspection_endpoint"`
// 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
// The Revocation Endpoint.
RevocationEndpoint string `json:"revocation_endpoint,omitempty"`
// JSON array containing the value "none".
RevocationEndpointAuthMethodsSupported []string `json:"revocation_endpoint_auth_methods_supported,omitempty"` //nolint: lll
// JSON array containing scope values supported by the // JSON array containing scope values supported by the
// IndieAuth server. Servers MAY choose not to advertise some // IndieAuth server.
// supported scope values even when this parameter is used. ScopesSupported []string `json:"scopes_supported,omitempty"`
ScopesSupported []domain.Scope `json:"scopes_supported,omitempty"`
// JSON array containing the response_type values supported. // JSON array containing the response_type values supported.
// This differs from RFC8414 in that this parameter is OPTIONAL ResponseTypesSupported []string `json:"response_types_supported,omitempty"`
// and that, if omitted, the default is code.
ResponseTypesSupported []domain.ResponseType `json:"response_types_supported,omitempty"`
// JSON array containing grant type values supported. If // JSON array containing grant type values supported.
// omitted, the default value differs from RFC8414 and is GrantTypesSupported []string `json:"grant_types_supported,omitempty"`
// authorization_code.
GrantTypesSupported []domain.GrantType `json:"grant_types_supported,omitempty"`
// URL of a page containing human-readable information that // URL of a page containing human-readable information that
// developers might need to know when using the server. This // developers might need to know when using the server.
// might be a link to the IndieAuth spec or something more ServiceDocumentation string `json:"service_documentation,omitempty"`
// personal to your implementation.
ServiceDocumentation *domain.URL `json:"service_documentation,omitempty"`
// JSON array containing the methods supported for PKCE. This // JSON array containing the methods supported for PKCE.
// parameter differs from RFC8414 in that it is not optional as CodeChallengeMethodsSupported []string `json:"code_challenge_methods_supported"`
// PKCE is REQUIRED.
CodeChallengeMethodsSupported []domain.CodeChallengeMethod `json:"code_challenge_methods_supported"`
// Boolean parameter indicating whether the authorization server // Boolean parameter indicating whether the authorization server
// provides the iss parameter. If omitted, the default value is // provides the iss parameter.
// false. As the iss parameter is REQUIRED, this is provided for AuthorizationResponseIssParameterSupported bool `json:"authorization_response_iss_parameter_supported,omitempty"` //nolint: lll
// compatibility with OAuth 2.0 servers implementing the
// parameter. // The User Info Endpoint.
// UserinfoEndpoint string `json:"userinfo_endpoint,omitempty"`
//nolint: lll
AuthorizationResponseIssParameterSupported bool `json:"authorization_response_iss_parameter_supported,omitempty"`
} }
RequestHandler struct { RequestHandler struct {
@ -89,15 +82,45 @@ func (h *RequestHandler) Register(r *router.Router) {
func (h *RequestHandler) read(ctx *http.RequestCtx) { func (h *RequestHandler) read(ctx *http.RequestCtx) {
ctx.SetStatusCode(http.StatusOK) ctx.SetStatusCode(http.StatusOK)
ctx.SetContentType(common.MIMEApplicationJSONCharsetUTF8) ctx.SetContentType(common.MIMEApplicationJSONCharsetUTF8)
scopes, responseTypes, grantTypes, codeChallengeMethods := make([]string, 0), make([]string, 0),
make([]string, 0), make([]string, 0)
for i := range h.metadata.ScopesSupported {
scopes = append(scopes, h.metadata.ScopesSupported[i].String())
}
for i := range h.metadata.ResponseTypesSupported {
responseTypes = append(responseTypes, h.metadata.ResponseTypesSupported[i].String())
}
for i := range h.metadata.GrantTypesSupported {
grantTypes = append(grantTypes, h.metadata.GrantTypesSupported[i].String())
}
for i := range h.metadata.CodeChallengeMethodsSupported {
codeChallengeMethods = append(codeChallengeMethods,
h.metadata.CodeChallengeMethodsSupported[i].String())
}
_ = json.NewEncoder(ctx).Encode(&MetadataResponse{ _ = json.NewEncoder(ctx).Encode(&MetadataResponse{
Issuer: h.metadata.Issuer, AuthorizationEndpoint: h.metadata.AuthorizationEndpoint.String(),
AuthorizationEndpoint: h.metadata.AuthorizationEndpoint, IntrospectionEndpoint: h.metadata.IntrospectionEndpoint.String(),
TokenEndpoint: h.metadata.TokenEndpoint, Issuer: h.metadata.Issuer.String(),
ScopesSupported: h.metadata.ScopesSupported, RevocationEndpoint: h.metadata.RevocationEndpoint.String(),
ResponseTypesSupported: h.metadata.ResponseTypesSupported, ServiceDocumentation: h.metadata.ServiceDocumentation.String(),
GrantTypesSupported: h.metadata.GrantTypesSupported, TokenEndpoint: h.metadata.TokenEndpoint.String(),
ServiceDocumentation: h.metadata.ServiceDocumentation, UserinfoEndpoint: h.metadata.UserinfoEndpoint.String(),
CodeChallengeMethodsSupported: h.metadata.CodeChallengeMethodsSupported,
AuthorizationResponseIssParameterSupported: h.metadata.AuthorizationResponseIssParameterSupported, AuthorizationResponseIssParameterSupported: h.metadata.AuthorizationResponseIssParameterSupported,
CodeChallengeMethodsSupported: codeChallengeMethods,
GrantTypesSupported: grantTypes,
IntrospectionEndpointAuthMethodsSupported: h.metadata.IntrospectionEndpointAuthMethodsSupported,
ResponseTypesSupported: responseTypes,
ScopesSupported: scopes,
// 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.
RevocationEndpointAuthMethodsSupported: h.metadata.RevocationEndpointAuthMethodsSupported,
}) })
} }

View File

@ -35,7 +35,6 @@ func TestMetadata(t *testing.T) {
result := new(delivery.MetadataResponse) result := new(delivery.MetadataResponse)
if err = json.Unmarshal(body, result); err != nil { if err = json.Unmarshal(body, result); err != nil {
e := err.(*json.SyntaxError) t.Fatal(err)
t.Fatalf("%s#ERROR#%s", body[:e.Offset], body[e.Offset:])
} }
} }

13
main.go
View File

@ -283,6 +283,15 @@ func (app *App) Register(r *router.Router) {
Issuer: indieAuthClient.ID, Issuer: indieAuthClient.ID,
AuthorizationEndpoint: domain.MustParseURL(indieAuthClient.ID.String() + "authorize"), AuthorizationEndpoint: domain.MustParseURL(indieAuthClient.ID.String() + "authorize"),
TokenEndpoint: domain.MustParseURL(indieAuthClient.ID.String() + "token"), TokenEndpoint: domain.MustParseURL(indieAuthClient.ID.String() + "token"),
TicketEndpoint: domain.MustParseURL(indieAuthClient.ID.String() + "ticket"),
MicropubEndpoint: nil,
MicrosubEndpoint: nil,
IntrospectionEndpoint: domain.MustParseURL(indieAuthClient.ID.String() + "introspect"),
RevocationEndpoint: domain.MustParseURL(indieAuthClient.ID.String() + "revocation"),
UserinfoEndpoint: domain.MustParseURL(indieAuthClient.ID.String() + "userinfo"),
ServiceDocumentation: domain.MustParseURL("https://indieauth.net/source/"),
IntrospectionEndpointAuthMethodsSupported: []string{"Bearer"},
RevocationEndpointAuthMethodsSupported: []string{"none"},
ScopesSupported: domain.Scopes{ ScopesSupported: domain.Scopes{
domain.ScopeBlock, domain.ScopeBlock,
domain.ScopeChannels, domain.ScopeChannels,
@ -305,7 +314,6 @@ func (app *App) Register(r *router.Router) {
domain.GrantTypeAuthorizationCode, domain.GrantTypeAuthorizationCode,
domain.GrantTypeTicket, domain.GrantTypeTicket,
}, },
ServiceDocumentation: domain.MustParseURL("https://indieauth.net/source/"),
CodeChallengeMethodsSupported: []domain.CodeChallengeMethod{ CodeChallengeMethodsSupported: []domain.CodeChallengeMethod{
domain.CodeChallengeMethodMD5, domain.CodeChallengeMethodMD5,
domain.CodeChallengeMethodPLAIN, domain.CodeChallengeMethodPLAIN,
@ -314,9 +322,6 @@ func (app *App) Register(r *router.Router) {
domain.CodeChallengeMethodS512, domain.CodeChallengeMethodS512,
}, },
AuthorizationResponseIssParameterSupported: true, AuthorizationResponseIssParameterSupported: true,
TicketEndpoint: domain.MustParseURL(indieAuthClient.ID.String() + "ticket"),
Micropub: nil,
Microsub: nil,
}).Register(r) }).Register(r)
tokenhttpdelivery.NewRequestHandler(app.tokens, app.tickets).Register(r) tokenhttpdelivery.NewRequestHandler(app.tokens, app.tickets).Register(r)
clienthttpdelivery.NewRequestHandler(clienthttpdelivery.NewRequestHandlerOptions{ clienthttpdelivery.NewRequestHandler(clienthttpdelivery.NewRequestHandlerOptions{