🎨 Format of the exist code before refactoring
This commit is contained in:
parent
e494526389
commit
6a1e281ebb
|
@ -159,12 +159,12 @@ func (h *RequestHandler) Register(r *router.Router) {
|
||||||
middleware.LogFmt(),
|
middleware.LogFmt(),
|
||||||
}
|
}
|
||||||
|
|
||||||
r.GET("/authorize", chain.RequestHandler(h.handleRender))
|
r.GET("/authorize", chain.RequestHandler(h.handleAuthorize))
|
||||||
r.POST("/api/authorize", chain.RequestHandler(h.handleVerify))
|
r.POST("/api/authorize", chain.RequestHandler(h.handleVerify))
|
||||||
r.POST("/authorize", chain.RequestHandler(h.handleExchange))
|
r.POST("/authorize", chain.RequestHandler(h.handleExchange))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *RequestHandler) handleRender(ctx *http.RequestCtx) {
|
func (h *RequestHandler) handleAuthorize(ctx *http.RequestCtx) {
|
||||||
ctx.SetContentType(common.MIMETextHTMLCharsetUTF8)
|
ctx.SetContentType(common.MIMETextHTMLCharsetUTF8)
|
||||||
|
|
||||||
tags, _, _ := language.ParseAcceptLanguage(string(ctx.Request.Header.Peek(http.HeaderAcceptLanguage)))
|
tags, _, _ := language.ParseAcceptLanguage(string(ctx.Request.Header.Peek(http.HeaderAcceptLanguage)))
|
||||||
|
|
|
@ -35,7 +35,7 @@ type Dependencies struct {
|
||||||
store *sync.Map
|
store *sync.Map
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRender(t *testing.T) {
|
func TestAuthorize(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
deps := NewDependencies(t)
|
deps := NewDependencies(t)
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
//nolint: dupl
|
|
||||||
package domain
|
package domain
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -44,7 +43,7 @@ func ParseAction(uid string) (Action, error) {
|
||||||
func (a *Action) UnmarshalForm(v []byte) error {
|
func (a *Action) UnmarshalForm(v []byte) error {
|
||||||
action, err := ParseAction(string(v))
|
action, err := ParseAction(string(v))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("UnmarshalForm: %w", err)
|
return fmt.Errorf("Action: UnmarshalForm: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
*a = action
|
*a = action
|
||||||
|
@ -56,12 +55,12 @@ func (a *Action) UnmarshalForm(v []byte) error {
|
||||||
func (a *Action) UnmarshalJSON(v []byte) error {
|
func (a *Action) UnmarshalJSON(v []byte) error {
|
||||||
src, err := strconv.Unquote(string(v))
|
src, err := strconv.Unquote(string(v))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("UnmarshalJSON: %w", err)
|
return fmt.Errorf("Action: UnmarshalJSON: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
action, err := ParseAction(src)
|
action, err := ParseAction(src)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("UnmarshalJSON: %w", err)
|
return fmt.Errorf("Action: UnmarshalJSON: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
*a = action
|
*a = action
|
||||||
|
|
|
@ -107,7 +107,7 @@ func ParseClientID(src string) (*ClientID, error) {
|
||||||
func TestClientID(tb testing.TB) *ClientID {
|
func TestClientID(tb testing.TB) *ClientID {
|
||||||
tb.Helper()
|
tb.Helper()
|
||||||
|
|
||||||
clientID, err := ParseClientID("https://app.example.com/")
|
clientID, err := ParseClientID("https://indieauth.example.com/")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tb.Fatal(err)
|
tb.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -119,7 +119,7 @@ func TestClientID(tb testing.TB) *ClientID {
|
||||||
func (cid *ClientID) UnmarshalForm(v []byte) error {
|
func (cid *ClientID) UnmarshalForm(v []byte) error {
|
||||||
clientID, err := ParseClientID(string(v))
|
clientID, err := ParseClientID(string(v))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("UnmarshalForm: %w", err)
|
return fmt.Errorf("ClientID: UnmarshalForm: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
*cid = *clientID
|
*cid = *clientID
|
||||||
|
@ -131,12 +131,12 @@ func (cid *ClientID) UnmarshalForm(v []byte) error {
|
||||||
func (cid *ClientID) UnmarshalJSON(v []byte) error {
|
func (cid *ClientID) UnmarshalJSON(v []byte) error {
|
||||||
src, err := strconv.Unquote(string(v))
|
src, err := strconv.Unquote(string(v))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("UnmarshalJSON: %w", err)
|
return fmt.Errorf("ClientID: UnmarshalJSON: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
clientID, err := ParseClientID(src)
|
clientID, err := ParseClientID(src)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("UnmarshalJSON: %w", err)
|
return fmt.Errorf("ClientID: UnmarshalJSON: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
*cid = *clientID
|
*cid = *clientID
|
||||||
|
|
|
@ -35,15 +35,13 @@ var (
|
||||||
}
|
}
|
||||||
|
|
||||||
CodeChallengeMethodMD5 = CodeChallengeMethod{
|
CodeChallengeMethodMD5 = CodeChallengeMethod{
|
||||||
uid: "MD5",
|
uid: "MD5",
|
||||||
//nolint: gosec // support old clients
|
hash: md5.New(), //nolint: gosec // support old clients
|
||||||
hash: md5.New(),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CodeChallengeMethodS1 = CodeChallengeMethod{
|
CodeChallengeMethodS1 = CodeChallengeMethod{
|
||||||
uid: "S1",
|
uid: "S1",
|
||||||
//nolint: gosec // support old clients
|
hash: sha1.New(), //nolint: gosec // support old clients
|
||||||
hash: sha1.New(),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CodeChallengeMethodS256 = CodeChallengeMethod{
|
CodeChallengeMethodS256 = CodeChallengeMethod{
|
||||||
|
@ -64,7 +62,7 @@ var ErrCodeChallengeMethodUnknown error = NewError(
|
||||||
)
|
)
|
||||||
|
|
||||||
//nolint: gochecknoglobals // maps cannot be constants
|
//nolint: gochecknoglobals // maps cannot be constants
|
||||||
var slugsMethods = map[string]CodeChallengeMethod{
|
var uidsMethods = map[string]CodeChallengeMethod{
|
||||||
CodeChallengeMethodMD5.uid: CodeChallengeMethodMD5,
|
CodeChallengeMethodMD5.uid: CodeChallengeMethodMD5,
|
||||||
CodeChallengeMethodPLAIN.uid: CodeChallengeMethodPLAIN,
|
CodeChallengeMethodPLAIN.uid: CodeChallengeMethodPLAIN,
|
||||||
CodeChallengeMethodS1.uid: CodeChallengeMethodS1,
|
CodeChallengeMethodS1.uid: CodeChallengeMethodS1,
|
||||||
|
@ -75,7 +73,7 @@ var slugsMethods = map[string]CodeChallengeMethod{
|
||||||
// ParseCodeChallengeMethod parse string identifier of code challenge method
|
// ParseCodeChallengeMethod parse string identifier of code challenge method
|
||||||
// into struct enum.
|
// into struct enum.
|
||||||
func ParseCodeChallengeMethod(uid string) (CodeChallengeMethod, error) {
|
func ParseCodeChallengeMethod(uid string) (CodeChallengeMethod, error) {
|
||||||
if method, ok := slugsMethods[strings.ToUpper(uid)]; ok {
|
if method, ok := uidsMethods[strings.ToUpper(uid)]; ok {
|
||||||
return method, nil
|
return method, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,7 +84,7 @@ func ParseCodeChallengeMethod(uid string) (CodeChallengeMethod, error) {
|
||||||
func (ccm *CodeChallengeMethod) UnmarshalForm(v []byte) error {
|
func (ccm *CodeChallengeMethod) UnmarshalForm(v []byte) error {
|
||||||
method, err := ParseCodeChallengeMethod(string(v))
|
method, err := ParseCodeChallengeMethod(string(v))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("UnmarshalForm: %w", err)
|
return fmt.Errorf("CodeChallengeMethod: UnmarshalForm: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
*ccm = method
|
*ccm = method
|
||||||
|
@ -98,12 +96,12 @@ func (ccm *CodeChallengeMethod) UnmarshalForm(v []byte) error {
|
||||||
func (ccm *CodeChallengeMethod) UnmarshalJSON(v []byte) error {
|
func (ccm *CodeChallengeMethod) UnmarshalJSON(v []byte) error {
|
||||||
src, err := strconv.Unquote(string(v))
|
src, err := strconv.Unquote(string(v))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("UnmarshalJSON: %w", err)
|
return fmt.Errorf("CodeChallengeMethod: UnmarshalJSON: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
method, err := ParseCodeChallengeMethod(src)
|
method, err := ParseCodeChallengeMethod(src)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("UnmarshalJSON: %w", err)
|
return fmt.Errorf("CodeChallengeMethod: UnmarshalJSON: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
*ccm = method
|
*ccm = method
|
||||||
|
@ -111,6 +109,10 @@ func (ccm *CodeChallengeMethod) UnmarshalJSON(v []byte) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ccm CodeChallengeMethod) MarshalJSON() ([]byte, error) {
|
||||||
|
return []byte(strconv.Quote(ccm.uid)), nil
|
||||||
|
}
|
||||||
|
|
||||||
// String returns string representation of code challenge method.
|
// String returns string representation of code challenge method.
|
||||||
func (ccm CodeChallengeMethod) String() string {
|
func (ccm CodeChallengeMethod) String() string {
|
||||||
return ccm.uid
|
return ccm.uid
|
||||||
|
|
|
@ -46,7 +46,7 @@ func ParseGrantType(uid string) (GrantType, error) {
|
||||||
func (gt *GrantType) UnmarshalForm(src []byte) error {
|
func (gt *GrantType) UnmarshalForm(src []byte) error {
|
||||||
responseType, err := ParseGrantType(string(src))
|
responseType, err := ParseGrantType(string(src))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("UnmarshalForm: %w", err)
|
return fmt.Errorf("GrantType: UnmarshalForm: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
*gt = responseType
|
*gt = responseType
|
||||||
|
@ -58,12 +58,12 @@ func (gt *GrantType) UnmarshalForm(src []byte) error {
|
||||||
func (gt *GrantType) UnmarshalJSON(v []byte) error {
|
func (gt *GrantType) UnmarshalJSON(v []byte) error {
|
||||||
src, err := strconv.Unquote(string(v))
|
src, err := strconv.Unquote(string(v))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("UnmarshalJSON: %w", err)
|
return fmt.Errorf("GrantType: UnmarshalJSON: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
responseType, err := ParseGrantType(src)
|
responseType, err := ParseGrantType(src)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("UnmarshalJSON: %w", err)
|
return fmt.Errorf("GrantType: UnmarshalJSON: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
*gt = responseType
|
*gt = responseType
|
||||||
|
@ -71,6 +71,10 @@ func (gt *GrantType) UnmarshalJSON(v []byte) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (gt GrantType) MarshalJSON() ([]byte, error) {
|
||||||
|
return []byte(strconv.Quote(gt.uid)), nil
|
||||||
|
}
|
||||||
|
|
||||||
// String returns string representation of grant type.
|
// String returns string representation of grant type.
|
||||||
func (gt GrantType) String() string {
|
func (gt GrantType) String() string {
|
||||||
return gt.uid
|
return gt.uid
|
||||||
|
|
|
@ -115,7 +115,7 @@ func TestMe(tb testing.TB, src string) *Me {
|
||||||
func (m *Me) UnmarshalForm(v []byte) error {
|
func (m *Me) UnmarshalForm(v []byte) error {
|
||||||
me, err := ParseMe(string(v))
|
me, err := ParseMe(string(v))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("UnmarshalForm: %w", err)
|
return fmt.Errorf("Me: UnmarshalForm: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
*m = *me
|
*m = *me
|
||||||
|
@ -127,12 +127,12 @@ func (m *Me) UnmarshalForm(v []byte) error {
|
||||||
func (m *Me) UnmarshalJSON(v []byte) error {
|
func (m *Me) UnmarshalJSON(v []byte) error {
|
||||||
src, err := strconv.Unquote(string(v))
|
src, err := strconv.Unquote(string(v))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("UnmarshalJSON: %w", err)
|
return fmt.Errorf("Me: UnmarshalJSON: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
me, err := ParseMe(src)
|
me, err := ParseMe(src)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("UnmarshalJSON: %w", err)
|
return fmt.Errorf("Me: UnmarshalJSON: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
*m = *me
|
*m = *me
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package domain
|
package domain
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
//nolint: tagliatelle // https://indieauth.net/source/#indieauth-server-metadata
|
//nolint: tagliatelle // https://indieauth.net/source/#indieauth-server-metadata
|
||||||
type Metadata struct {
|
type Metadata struct {
|
||||||
// The server's issuer identifier. The issuer identifier is a URL that
|
// The server's issuer identifier. The issuer identifier is a URL that
|
||||||
|
@ -10,7 +12,7 @@ type Metadata struct {
|
||||||
// issuer URL could be https://example.com/, or for a metadata URL of
|
// 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
|
// https://example.com/wp-json/indieauth/1.0/metadata, the issuer URL
|
||||||
// could be https://example.com/wp-json/indieauth/1.0
|
// could be https://example.com/wp-json/indieauth/1.0
|
||||||
Issuer *URL `json:"issuer"`
|
Issuer *ClientID `json:"issuer"`
|
||||||
|
|
||||||
// The Authorization Endpoint.
|
// The Authorization Endpoint.
|
||||||
AuthorizationEndpoint *URL `json:"authorization_endpoint"`
|
AuthorizationEndpoint *URL `json:"authorization_endpoint"`
|
||||||
|
@ -60,3 +62,48 @@ type Metadata struct {
|
||||||
// WARN(toby3d): experimental
|
// WARN(toby3d): experimental
|
||||||
Microsub *URL `json:"microsub,omitempty"`
|
Microsub *URL `json:"microsub,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestMetadata returns valid random generated Metadata for tests.
|
||||||
|
func TestMetadata(tb testing.TB) *Metadata {
|
||||||
|
tb.Helper()
|
||||||
|
|
||||||
|
return &Metadata{
|
||||||
|
Issuer: TestClientID(tb),
|
||||||
|
AuthorizationEndpoint: TestURL(tb, "https://indieauth.example.com/auth"),
|
||||||
|
TokenEndpoint: TestURL(tb, "https://indieauth.example.com/token"),
|
||||||
|
ScopesSupported: Scopes{
|
||||||
|
ScopeBlock,
|
||||||
|
ScopeChannels,
|
||||||
|
ScopeCreate,
|
||||||
|
ScopeDelete,
|
||||||
|
ScopeDraft,
|
||||||
|
ScopeEmail,
|
||||||
|
ScopeFollow,
|
||||||
|
ScopeMedia,
|
||||||
|
ScopeMute,
|
||||||
|
ScopeProfile,
|
||||||
|
ScopeRead,
|
||||||
|
ScopeUpdate,
|
||||||
|
},
|
||||||
|
ResponseTypesSupported: []ResponseType{
|
||||||
|
ResponseTypeCode,
|
||||||
|
ResponseTypeID,
|
||||||
|
},
|
||||||
|
GrantTypesSupported: []GrantType{
|
||||||
|
GrantTypeAuthorizationCode,
|
||||||
|
GrantTypeTicket,
|
||||||
|
},
|
||||||
|
ServiceDocumentation: TestURL(tb, "https://indieauth.net/draft/"),
|
||||||
|
CodeChallengeMethodsSupported: []CodeChallengeMethod{
|
||||||
|
CodeChallengeMethodMD5,
|
||||||
|
CodeChallengeMethodPLAIN,
|
||||||
|
CodeChallengeMethodS1,
|
||||||
|
CodeChallengeMethodS256,
|
||||||
|
CodeChallengeMethodS512,
|
||||||
|
},
|
||||||
|
AuthorizationResponseIssParameterSupported: true,
|
||||||
|
TicketEndpoint: TestURL(tb, "https://auth.example.org/ticket"),
|
||||||
|
Micropub: TestURL(tb, "https://example.com/micropub"),
|
||||||
|
Microsub: TestURL(tb, "https://example.com/microsub"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
//nolint: dupl
|
|
||||||
package domain
|
package domain
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -51,7 +50,7 @@ func ParseResponseType(uid string) (ResponseType, error) {
|
||||||
func (rt *ResponseType) UnmarshalForm(src []byte) error {
|
func (rt *ResponseType) UnmarshalForm(src []byte) error {
|
||||||
responseType, err := ParseResponseType(string(src))
|
responseType, err := ParseResponseType(string(src))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("UnmarshalForm: %w", err)
|
return fmt.Errorf("ResponseType: UnmarshalForm: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
*rt = responseType
|
*rt = responseType
|
||||||
|
@ -63,12 +62,12 @@ func (rt *ResponseType) UnmarshalForm(src []byte) error {
|
||||||
func (rt *ResponseType) UnmarshalJSON(v []byte) error {
|
func (rt *ResponseType) UnmarshalJSON(v []byte) error {
|
||||||
uid, err := strconv.Unquote(string(v))
|
uid, err := strconv.Unquote(string(v))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("UnmarshalJSON: %w", err)
|
return fmt.Errorf("ResponseType: UnmarshalJSON: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
responseType, err := ParseResponseType(uid)
|
responseType, err := ParseResponseType(uid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("UnmarshalJSON: %w", err)
|
return fmt.Errorf("ResponseType: UnmarshalJSON: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
*rt = responseType
|
*rt = responseType
|
||||||
|
@ -76,6 +75,10 @@ func (rt *ResponseType) UnmarshalJSON(v []byte) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (rt ResponseType) MarshalJSON() ([]byte, error) {
|
||||||
|
return []byte(strconv.Quote(rt.uid)), nil
|
||||||
|
}
|
||||||
|
|
||||||
// String returns string representation of response type.
|
// String returns string representation of response type.
|
||||||
func (rt ResponseType) String() string {
|
func (rt ResponseType) String() string {
|
||||||
return rt.uid
|
return rt.uid
|
||||||
|
|
|
@ -81,6 +81,15 @@ func ParseScope(uid string) (Scope, error) {
|
||||||
return ScopeUndefined, fmt.Errorf("%w: %s", ErrScopeUnknown, uid)
|
return ScopeUndefined, fmt.Errorf("%w: %s", ErrScopeUnknown, uid)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s Scope) MarshalJSON() ([]byte, error) {
|
||||||
|
return []byte(strconv.Quote(s.uid)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns string representation of scope.
|
||||||
|
func (s Scope) String() string {
|
||||||
|
return s.uid
|
||||||
|
}
|
||||||
|
|
||||||
// UnmarshalForm implements custom unmarshler for form values.
|
// UnmarshalForm implements custom unmarshler for form values.
|
||||||
func (s *Scopes) UnmarshalForm(v []byte) error {
|
func (s *Scopes) UnmarshalForm(v []byte) error {
|
||||||
scopes := make(Scopes, 0)
|
scopes := make(Scopes, 0)
|
||||||
|
@ -88,7 +97,7 @@ func (s *Scopes) UnmarshalForm(v []byte) error {
|
||||||
for _, rawScope := range strings.Fields(string(v)) {
|
for _, rawScope := range strings.Fields(string(v)) {
|
||||||
scope, err := ParseScope(rawScope)
|
scope, err := ParseScope(rawScope)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("UnmarshalForm: %w", err)
|
return fmt.Errorf("Scopes: UnmarshalForm: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if scopes.Has(scope) {
|
if scopes.Has(scope) {
|
||||||
|
@ -107,7 +116,7 @@ func (s *Scopes) UnmarshalForm(v []byte) error {
|
||||||
func (s *Scopes) UnmarshalJSON(v []byte) error {
|
func (s *Scopes) UnmarshalJSON(v []byte) error {
|
||||||
src, err := strconv.Unquote(string(v))
|
src, err := strconv.Unquote(string(v))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("UnmarshalJSON: %w", err)
|
return fmt.Errorf("Scopes: UnmarshalJSON: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
result := make(Scopes, 0)
|
result := make(Scopes, 0)
|
||||||
|
@ -115,7 +124,7 @@ func (s *Scopes) UnmarshalJSON(v []byte) error {
|
||||||
for _, rawScope := range strings.Fields(src) {
|
for _, rawScope := range strings.Fields(src) {
|
||||||
scope, err := ParseScope(rawScope)
|
scope, err := ParseScope(rawScope)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("UnmarshalJSON: %w", err)
|
return fmt.Errorf("Scopes: UnmarshalJSON: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if result.Has(scope) {
|
if result.Has(scope) {
|
||||||
|
@ -141,11 +150,6 @@ func (s Scopes) MarshalJSON() ([]byte, error) {
|
||||||
return []byte(strconv.Quote(strings.Join(scopes, " "))), nil
|
return []byte(strconv.Quote(strings.Join(scopes, " "))), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// String returns string representation of scope.
|
|
||||||
func (s Scope) String() string {
|
|
||||||
return s.uid
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns string representation of scopes.
|
// String returns string representation of scopes.
|
||||||
func (s Scopes) String() string {
|
func (s Scopes) String() string {
|
||||||
scopes := make([]string, len(s))
|
scopes := make([]string, len(s))
|
||||||
|
|
|
@ -14,7 +14,7 @@ type URL struct {
|
||||||
*http.URI
|
*http.URI
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseURL parse strings as URL.
|
// ParseURL parse string as URL.
|
||||||
func ParseURL(src string) (*URL, error) {
|
func ParseURL(src string) (*URL, error) {
|
||||||
u := http.AcquireURI()
|
u := http.AcquireURI()
|
||||||
if err := u.Parse(nil, []byte(src)); err != nil {
|
if err := u.Parse(nil, []byte(src)); err != nil {
|
||||||
|
@ -24,6 +24,16 @@ func ParseURL(src string) (*URL, error) {
|
||||||
return &URL{URI: u}, nil
|
return &URL{URI: u}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MustParseURL parse string as URL or panic.
|
||||||
|
func MustParseURL(src string) *URL {
|
||||||
|
uri, err := ParseURL(src)
|
||||||
|
if err != nil {
|
||||||
|
panic("MustParseURL: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return uri
|
||||||
|
}
|
||||||
|
|
||||||
// TestURL returns URL of provided input for tests.
|
// TestURL returns URL of provided input for tests.
|
||||||
func TestURL(tb testing.TB, src string) *URL {
|
func TestURL(tb testing.TB, src string) *URL {
|
||||||
tb.Helper()
|
tb.Helper()
|
||||||
|
@ -40,7 +50,7 @@ func TestURL(tb testing.TB, src string) *URL {
|
||||||
func (u *URL) UnmarshalForm(v []byte) error {
|
func (u *URL) UnmarshalForm(v []byte) error {
|
||||||
url, err := ParseURL(string(v))
|
url, err := ParseURL(string(v))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("UnmarshalForm: %w", err)
|
return fmt.Errorf("URL: UnmarshalForm: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
*u = *url
|
*u = *url
|
||||||
|
@ -52,12 +62,12 @@ func (u *URL) UnmarshalForm(v []byte) error {
|
||||||
func (u *URL) UnmarshalJSON(v []byte) error {
|
func (u *URL) UnmarshalJSON(v []byte) error {
|
||||||
src, err := strconv.Unquote(string(v))
|
src, err := strconv.Unquote(string(v))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("UnmarshalJSON: %w", err)
|
return fmt.Errorf("URL: UnmarshalJSON: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
url, err := ParseURL(src)
|
url, err := ParseURL(src)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("UnmarshalJSON: %w", err)
|
return fmt.Errorf("URL: UnmarshalJSON: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
*u = *url
|
*u = *url
|
||||||
|
@ -65,6 +75,10 @@ func (u *URL) UnmarshalJSON(v []byte) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u URL) MarshalJSON() ([]byte, error) {
|
||||||
|
return []byte(strconv.Quote(u.String())), nil
|
||||||
|
}
|
||||||
|
|
||||||
// URL returns url.URL representation of URL.
|
// URL returns url.URL representation of URL.
|
||||||
func (u URL) URL() *url.URL {
|
func (u URL) URL() *url.URL {
|
||||||
if u.URI == nil {
|
if u.URI == nil {
|
||||||
|
|
|
@ -23,39 +23,39 @@ type (
|
||||||
// metadata URL of
|
// metadata URL of
|
||||||
// https://example.com/wp-json/indieauth/1.0/metadata, the
|
// https://example.com/wp-json/indieauth/1.0/metadata, the
|
||||||
// issuer URL could be https://example.com/wp-json/indieauth/1.0
|
// issuer URL could be https://example.com/wp-json/indieauth/1.0
|
||||||
Issuer string `json:"issuer"`
|
Issuer *domain.ClientID `json:"issuer"`
|
||||||
|
|
||||||
// The Authorization Endpoint.
|
// The Authorization Endpoint.
|
||||||
AuthorizationEndpoint string `json:"authorization_endpoint"`
|
AuthorizationEndpoint *domain.URL `json:"authorization_endpoint"`
|
||||||
|
|
||||||
// The Token Endpoint.
|
// The Token Endpoint.
|
||||||
TokenEndpoint string `json:"token_endpoint"`
|
TokenEndpoint *domain.URL `json:"token_endpoint"`
|
||||||
|
|
||||||
// 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. Servers MAY choose not to advertise some
|
||||||
// supported scope values even when this parameter is used.
|
// 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
|
// This differs from RFC8414 in that this parameter is OPTIONAL
|
||||||
// and that, if omitted, the default is code.
|
// and that, if omitted, the default is code.
|
||||||
ResponseTypesSupported []string `json:"response_types_supported,omitempty"`
|
ResponseTypesSupported []domain.ResponseType `json:"response_types_supported,omitempty"`
|
||||||
|
|
||||||
// JSON array containing grant type values supported. If
|
// JSON array containing grant type values supported. If
|
||||||
// omitted, the default value differs from RFC8414 and is
|
// omitted, the default value differs from RFC8414 and is
|
||||||
// authorization_code.
|
// authorization_code.
|
||||||
GrantTypesSupported []string `json:"grant_types_supported,omitempty"`
|
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. This
|
||||||
// might be a link to the IndieAuth spec or something more
|
// might be a link to the IndieAuth spec or something more
|
||||||
// personal to your implementation.
|
// personal to your implementation.
|
||||||
ServiceDocumentation string `json:"service_documentation,omitempty"`
|
ServiceDocumentation *domain.URL `json:"service_documentation,omitempty"`
|
||||||
|
|
||||||
// JSON array containing the methods supported for PKCE. This
|
// JSON array containing the methods supported for PKCE. This
|
||||||
// parameter differs from RFC8414 in that it is not optional as
|
// parameter differs from RFC8414 in that it is not optional as
|
||||||
// PKCE is REQUIRED.
|
// PKCE is REQUIRED.
|
||||||
CodeChallengeMethodsSupported []string `json:"code_challenge_methods_supported"`
|
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. If omitted, the default value is
|
||||||
|
@ -68,52 +68,13 @@ type (
|
||||||
}
|
}
|
||||||
|
|
||||||
RequestHandler struct {
|
RequestHandler struct {
|
||||||
config *domain.Config
|
metadata *domain.Metadata
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// DefaultMetadataResponse contains all supported types by default.
|
func NewRequestHandler(metadata *domain.Metadata) *RequestHandler {
|
||||||
//nolint: gochecknoglobals // structs cannot be constants
|
|
||||||
var DefaultMetadataResponse = MetadataResponse{
|
|
||||||
AuthorizationEndpoint: "",
|
|
||||||
AuthorizationResponseIssParameterSupported: true,
|
|
||||||
CodeChallengeMethodsSupported: []string{
|
|
||||||
domain.CodeChallengeMethodMD5.String(),
|
|
||||||
domain.CodeChallengeMethodPLAIN.String(),
|
|
||||||
domain.CodeChallengeMethodS1.String(),
|
|
||||||
domain.CodeChallengeMethodS256.String(),
|
|
||||||
domain.CodeChallengeMethodS512.String(),
|
|
||||||
},
|
|
||||||
GrantTypesSupported: []string{
|
|
||||||
domain.GrantTypeAuthorizationCode.String(),
|
|
||||||
domain.GrantTypeTicket.String(),
|
|
||||||
},
|
|
||||||
Issuer: "",
|
|
||||||
ResponseTypesSupported: []string{
|
|
||||||
domain.ResponseTypeCode.String(),
|
|
||||||
domain.ResponseTypeID.String(),
|
|
||||||
},
|
|
||||||
ScopesSupported: []string{
|
|
||||||
domain.ScopeBlock.String(),
|
|
||||||
domain.ScopeChannels.String(),
|
|
||||||
domain.ScopeCreate.String(),
|
|
||||||
domain.ScopeDelete.String(),
|
|
||||||
domain.ScopeDraft.String(),
|
|
||||||
domain.ScopeEmail.String(),
|
|
||||||
domain.ScopeFollow.String(),
|
|
||||||
domain.ScopeMedia.String(),
|
|
||||||
domain.ScopeMute.String(),
|
|
||||||
domain.ScopeProfile.String(),
|
|
||||||
domain.ScopeRead.String(),
|
|
||||||
domain.ScopeUpdate.String(),
|
|
||||||
},
|
|
||||||
ServiceDocumentation: "https://indieauth.net/source/",
|
|
||||||
TokenEndpoint: "",
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewRequestHandler(config *domain.Config) *RequestHandler {
|
|
||||||
return &RequestHandler{
|
return &RequestHandler{
|
||||||
config: config,
|
metadata: metadata,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,12 +87,17 @@ func (h *RequestHandler) Register(r *router.Router) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *RequestHandler) read(ctx *http.RequestCtx) {
|
func (h *RequestHandler) read(ctx *http.RequestCtx) {
|
||||||
resp := DefaultMetadataResponse
|
|
||||||
resp.Issuer = h.config.Server.GetRootURL()
|
|
||||||
resp.AuthorizationEndpoint = resp.Issuer + "authorize"
|
|
||||||
resp.TokenEndpoint = resp.Issuer + "token"
|
|
||||||
|
|
||||||
ctx.SetStatusCode(http.StatusOK)
|
ctx.SetStatusCode(http.StatusOK)
|
||||||
ctx.SetContentType(common.MIMEApplicationJSON)
|
ctx.SetContentType(common.MIMEApplicationJSONCharsetUTF8)
|
||||||
_ = json.NewEncoder(ctx).Encode(&resp)
|
_ = json.NewEncoder(ctx).Encode(&MetadataResponse{
|
||||||
|
Issuer: h.metadata.Issuer,
|
||||||
|
AuthorizationEndpoint: h.metadata.AuthorizationEndpoint,
|
||||||
|
TokenEndpoint: h.metadata.TokenEndpoint,
|
||||||
|
ScopesSupported: h.metadata.ScopesSupported,
|
||||||
|
ResponseTypesSupported: h.metadata.ResponseTypesSupported,
|
||||||
|
GrantTypesSupported: h.metadata.GrantTypesSupported,
|
||||||
|
ServiceDocumentation: h.metadata.ServiceDocumentation,
|
||||||
|
CodeChallengeMethodsSupported: h.metadata.CodeChallengeMethodsSupported,
|
||||||
|
AuthorizationResponseIssParameterSupported: h.metadata.AuthorizationResponseIssParameterSupported,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package http_test
|
package http_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"reflect"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/fasthttp/router"
|
"github.com/fasthttp/router"
|
||||||
|
@ -17,8 +16,8 @@ func TestMetadata(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
r := router.New()
|
r := router.New()
|
||||||
cfg := domain.TestConfig(t)
|
metadata := domain.TestMetadata(t)
|
||||||
delivery.NewRequestHandler(cfg).Register(r)
|
delivery.NewRequestHandler(metadata).Register(r)
|
||||||
|
|
||||||
client, _, cleanup := httptest.New(t, r.Handler)
|
client, _, cleanup := httptest.New(t, r.Handler)
|
||||||
t.Cleanup(cleanup)
|
t.Cleanup(cleanup)
|
||||||
|
@ -36,15 +35,7 @@ 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 {
|
||||||
t.Fatal(err)
|
e := err.(*json.SyntaxError)
|
||||||
}
|
t.Fatalf("%s#ERROR#%s", body[:e.Offset], body[e.Offset:])
|
||||||
|
|
||||||
expResult := delivery.DefaultMetadataResponse
|
|
||||||
expResult.Issuer = cfg.Server.GetRootURL()
|
|
||||||
expResult.AuthorizationEndpoint = expResult.Issuer + "authorize"
|
|
||||||
expResult.TokenEndpoint = expResult.Issuer + "token"
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(*result, expResult) {
|
|
||||||
t.Errorf("Unmarshal(%s) = %+v, want %+v", body, result, expResult)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,8 +39,8 @@ type (
|
||||||
TokenExchangeResponse struct {
|
TokenExchangeResponse struct {
|
||||||
AccessToken string `json:"access_token"`
|
AccessToken string `json:"access_token"`
|
||||||
TokenType string `json:"token_type"`
|
TokenType string `json:"token_type"`
|
||||||
Scope string `json:"scope"`
|
Scope domain.Scopes `json:"scope"`
|
||||||
Me string `json:"me"`
|
Me *domain.Me `json:"me"`
|
||||||
Profile *TokenProfileResponse `json:"profile,omitempty"`
|
Profile *TokenProfileResponse `json:"profile,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,8 +176,8 @@ func (h *RequestHandler) handleExchange(ctx *http.RequestCtx) {
|
||||||
resp := &TokenExchangeResponse{
|
resp := &TokenExchangeResponse{
|
||||||
AccessToken: token.AccessToken,
|
AccessToken: token.AccessToken,
|
||||||
TokenType: "Bearer",
|
TokenType: "Bearer",
|
||||||
Scope: token.Scope.String(),
|
Scope: token.Scope,
|
||||||
Me: token.Me.String(),
|
Me: token.Me,
|
||||||
Profile: nil,
|
Profile: nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -269,9 +269,9 @@ func (h *RequestHandler) handleTicket(ctx *http.RequestCtx) {
|
||||||
_ = encoder.Encode(TokenExchangeResponse{
|
_ = encoder.Encode(TokenExchangeResponse{
|
||||||
AccessToken: tkn.AccessToken,
|
AccessToken: tkn.AccessToken,
|
||||||
TokenType: "Bearer",
|
TokenType: "Bearer",
|
||||||
Scope: tkn.Scope.String(),
|
Scope: tkn.Scope,
|
||||||
Me: tkn.Me.String(),
|
Me: tkn.Me,
|
||||||
Profile: nil,
|
Profile: nil, // TODO(toby3d)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
40
main.go
40
main.go
|
@ -279,7 +279,45 @@ func NewApp(opts NewAppOptions) *App {
|
||||||
func (app *App) Register(r *router.Router) {
|
func (app *App) Register(r *router.Router) {
|
||||||
tickethttpdelivery.NewRequestHandler(app.tickets, app.matcher, config).Register(r)
|
tickethttpdelivery.NewRequestHandler(app.tickets, app.matcher, config).Register(r)
|
||||||
healthhttpdelivery.NewRequestHandler().Register(r)
|
healthhttpdelivery.NewRequestHandler().Register(r)
|
||||||
metadatahttpdelivery.NewRequestHandler(config).Register(r)
|
metadatahttpdelivery.NewRequestHandler(&domain.Metadata{
|
||||||
|
Issuer: indieAuthClient.ID,
|
||||||
|
AuthorizationEndpoint: domain.MustParseURL(indieAuthClient.ID.String() + "authorize"),
|
||||||
|
TokenEndpoint: domain.MustParseURL(indieAuthClient.ID.String() + "token"),
|
||||||
|
ScopesSupported: domain.Scopes{
|
||||||
|
domain.ScopeBlock,
|
||||||
|
domain.ScopeChannels,
|
||||||
|
domain.ScopeCreate,
|
||||||
|
domain.ScopeDelete,
|
||||||
|
domain.ScopeDraft,
|
||||||
|
domain.ScopeEmail,
|
||||||
|
domain.ScopeFollow,
|
||||||
|
domain.ScopeMedia,
|
||||||
|
domain.ScopeMute,
|
||||||
|
domain.ScopeProfile,
|
||||||
|
domain.ScopeRead,
|
||||||
|
domain.ScopeUpdate,
|
||||||
|
},
|
||||||
|
ResponseTypesSupported: []domain.ResponseType{
|
||||||
|
domain.ResponseTypeCode,
|
||||||
|
domain.ResponseTypeID,
|
||||||
|
},
|
||||||
|
GrantTypesSupported: []domain.GrantType{
|
||||||
|
domain.GrantTypeAuthorizationCode,
|
||||||
|
domain.GrantTypeTicket,
|
||||||
|
},
|
||||||
|
ServiceDocumentation: domain.MustParseURL("https://indieauth.net/source/"),
|
||||||
|
CodeChallengeMethodsSupported: []domain.CodeChallengeMethod{
|
||||||
|
domain.CodeChallengeMethodMD5,
|
||||||
|
domain.CodeChallengeMethodPLAIN,
|
||||||
|
domain.CodeChallengeMethodS1,
|
||||||
|
domain.CodeChallengeMethodS256,
|
||||||
|
domain.CodeChallengeMethodS512,
|
||||||
|
},
|
||||||
|
AuthorizationResponseIssParameterSupported: true,
|
||||||
|
TicketEndpoint: domain.MustParseURL(indieAuthClient.ID.String() + "ticket"),
|
||||||
|
Micropub: nil,
|
||||||
|
Microsub: nil,
|
||||||
|
}).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{
|
||||||
Client: indieAuthClient,
|
Client: indieAuthClient,
|
||||||
|
|
Loading…
Reference in New Issue