diff --git a/internal/auth/delivery/http/auth_http.go b/internal/auth/delivery/http/auth_http.go
index f106352..c760960 100644
--- a/internal/auth/delivery/http/auth_http.go
+++ b/internal/auth/delivery/http/auth_http.go
@@ -206,7 +206,7 @@ func (h *RequestHandler) handleAuthorize(ctx *http.RequestCtx) {
return
}
- if !client.ValidateRedirectURI(req.RedirectURI) {
+ if !client.ValidateRedirectURI(req.RedirectURI.URL) {
ctx.SetStatusCode(http.StatusBadRequest)
web.WriteTemplate(ctx, &web.ErrorPage{
BaseOf: baseOf,
@@ -251,14 +251,10 @@ func (h *RequestHandler) handleVerify(ctx *http.RequestCtx) {
return
}
- redirectURL := http.AcquireURI()
- defer http.ReleaseURI(redirectURL)
- req.RedirectURI.CopyTo(redirectURL)
-
if strings.EqualFold(req.Authorize, "deny") {
domain.NewError(domain.ErrorCodeAccessDenied, "user deny authorization request", "", req.State).
- SetReirectURI(redirectURL)
- ctx.Redirect(redirectURL.String(), http.StatusFound)
+ SetReirectURI(req.RedirectURI.URL)
+ ctx.Redirect(req.RedirectURI.String(), http.StatusFound)
return
}
@@ -266,7 +262,7 @@ func (h *RequestHandler) handleVerify(ctx *http.RequestCtx) {
code, err := h.useCase.Generate(ctx, auth.GenerateOptions{
ClientID: req.ClientID,
Me: req.Me,
- RedirectURI: req.RedirectURI,
+ RedirectURI: req.RedirectURI.URL,
CodeChallengeMethod: req.CodeChallengeMethod,
Scope: req.Scope,
CodeChallenge: req.CodeChallenge,
@@ -284,10 +280,10 @@ func (h *RequestHandler) handleVerify(ctx *http.RequestCtx) {
"iss": h.config.Server.GetRootURL(),
"state": req.State,
} {
- redirectURL.QueryArgs().Set(key, val)
+ req.RedirectURI.Query().Set(key, val)
}
- ctx.Redirect(redirectURL.String(), http.StatusFound)
+ ctx.Redirect(req.RedirectURI.String(), http.StatusFound)
}
func (h *RequestHandler) handleExchange(ctx *http.RequestCtx) {
@@ -307,7 +303,7 @@ func (h *RequestHandler) handleExchange(ctx *http.RequestCtx) {
me, profile, err := h.useCase.Exchange(ctx, auth.ExchangeOptions{
Code: req.Code,
ClientID: req.ClientID,
- RedirectURI: req.RedirectURI,
+ RedirectURI: req.RedirectURI.URL,
CodeVerifier: req.CodeVerifier,
})
if err != nil {
@@ -322,8 +318,8 @@ func (h *RequestHandler) handleExchange(ctx *http.RequestCtx) {
if profile != nil {
userInfo = &AuthProfileResponse{
Email: profile.GetEmail(),
- Photo: profile.GetPhoto(),
- URL: profile.GetURL(),
+ Photo: &domain.URL{URL: profile.GetPhoto()},
+ URL: &domain.URL{URL: profile.GetURL()},
Name: profile.GetName(),
}
}
diff --git a/internal/auth/usecase.go b/internal/auth/usecase.go
index 166e6c5..cf92b73 100644
--- a/internal/auth/usecase.go
+++ b/internal/auth/usecase.go
@@ -2,6 +2,7 @@ package auth
import (
"context"
+ "net/url"
"source.toby3d.me/toby3d/auth/internal/domain"
)
@@ -10,7 +11,7 @@ type (
GenerateOptions struct {
ClientID *domain.ClientID
Me *domain.Me
- RedirectURI *domain.URL
+ RedirectURI *url.URL
CodeChallengeMethod domain.CodeChallengeMethod
Scope domain.Scopes
CodeChallenge string
@@ -18,7 +19,7 @@ type (
ExchangeOptions struct {
ClientID *domain.ClientID
- RedirectURI *domain.URL
+ RedirectURI *url.URL
Code string
CodeVerifier string
}
diff --git a/internal/client/repository/http/http_client.go b/internal/client/repository/http/http_client.go
index 7fe2391..81e6a21 100644
--- a/internal/client/repository/http/http_client.go
+++ b/internal/client/repository/http/http_client.go
@@ -4,6 +4,7 @@ import (
"context"
"fmt"
"net"
+ "net/url"
http "github.com/valyala/fasthttp"
@@ -65,9 +66,9 @@ func (repo *httpClientRepository) Get(ctx context.Context, cid *domain.ClientID)
client := &domain.Client{
ID: cid,
- RedirectURI: make([]*domain.URL, 0),
- Logo: make([]*domain.URL, 0),
- URL: make([]*domain.URL, 0),
+ RedirectURI: make([]*url.URL, 0),
+ Logo: make([]*url.URL, 0),
+ URL: make([]*url.URL, 0),
Name: make([]string, 0),
}
@@ -93,24 +94,24 @@ func extract(dst *domain.Client, src *http.Response) {
for _, logo := range httputil.ExtractProperty(src, itemType, propertyLogo) {
var (
- uri *domain.URL
+ u *url.URL
err error
)
switch l := logo.(type) {
case string:
- uri, err = domain.ParseURL(l)
+ u, err = url.Parse(l)
case map[string]string:
if value, ok := l["value"]; ok {
- uri, err = domain.ParseURL(value)
+ u, err = url.Parse(value)
}
}
- if err != nil || containsURL(dst.Logo, uri) {
+ if err != nil || containsURL(dst.Logo, u) {
continue
}
- dst.Logo = append(dst.Logo, uri)
+ dst.Logo = append(dst.Logo, u)
}
for _, property := range httputil.ExtractProperty(src, itemType, propertyURL) {
@@ -119,7 +120,7 @@ func extract(dst *domain.Client, src *http.Response) {
continue
}
- if u, err := domain.ParseURL(prop); err == nil || !containsURL(dst.URL, u) {
+ if u, err := url.Parse(prop); err == nil || !containsURL(dst.URL, u) {
dst.URL = append(dst.URL, u)
}
}
@@ -138,7 +139,7 @@ func containsString(src []string, find string) bool {
return false
}
-func containsURL(src []*domain.URL, find *domain.URL) bool {
+func containsURL(src []*url.URL, find *url.URL) bool {
for i := range src {
if src[i].String() != find.String() {
continue
diff --git a/internal/domain/app.go b/internal/domain/app.go
index 751119d..e1b1a49 100644
--- a/internal/domain/app.go
+++ b/internal/domain/app.go
@@ -1,8 +1,10 @@
package domain
+import "net/url"
+
type App struct {
- Logo []*URL
- URL []*URL
+ Logo []*url.URL
+ URL []*url.URL
Name []string
}
@@ -16,7 +18,7 @@ func (a App) GetName() string {
}
// GetURL safe returns first URL, if any.
-func (a App) GetURL() *URL {
+func (a App) GetURL() *url.URL {
if len(a.URL) == 0 {
return nil
}
@@ -25,7 +27,7 @@ func (a App) GetURL() *URL {
}
// GetLogo safe returns first logo, if any.
-func (a App) GetLogo() *URL {
+func (a App) GetLogo() *url.URL {
if len(a.Logo) == 0 {
return nil
}
diff --git a/internal/domain/client.go b/internal/domain/client.go
index a983113..a2fb8d1 100644
--- a/internal/domain/client.go
+++ b/internal/domain/client.go
@@ -1,8 +1,8 @@
package domain
import (
- "bytes"
"net"
+ "net/url"
"strings"
"testing"
)
@@ -10,9 +10,9 @@ import (
// Client describes the client requesting data about the user.
type Client struct {
ID *ClientID
- Logo []*URL
- RedirectURI []*URL
- URL []*URL
+ Logo []*url.URL
+ RedirectURI []*url.URL
+ URL []*url.URL
Name []string
}
@@ -20,9 +20,9 @@ type Client struct {
func NewClient(cid *ClientID) *Client {
return &Client{
ID: cid,
- Logo: make([]*URL, 0),
- RedirectURI: make([]*URL, 0),
- URL: make([]*URL, 0),
+ Logo: make([]*url.URL, 0),
+ RedirectURI: make([]*url.URL, 0),
+ URL: make([]*url.URL, 0),
Name: make([]string, 0),
}
}
@@ -31,20 +31,15 @@ func NewClient(cid *ClientID) *Client {
func TestClient(tb testing.TB) *Client {
tb.Helper()
- redirects := make([]*URL, 0)
- for _, redirect := range []string{
- "https://app.example.com/redirect",
- "https://app.example.net/redirect",
- } {
- redirects = append(redirects, TestURL(tb, redirect))
- }
-
return &Client{
- ID: TestClientID(tb),
- Name: []string{"Example App"},
- URL: []*URL{TestURL(tb, "https://app.example.com/")},
- Logo: []*URL{TestURL(tb, "https://app.example.com/logo.png")},
- RedirectURI: redirects,
+ ID: TestClientID(tb),
+ Name: []string{"Example App"},
+ URL: []*url.URL{{Scheme: "https", Host: "app.example.com", Path: "/"}},
+ Logo: []*url.URL{{Scheme: "https", Host: "app.example.com", Path: "/logo.png"}},
+ RedirectURI: []*url.URL{
+ {Scheme: "https", Host: "app.example.com", Path: "/redirect"},
+ {Scheme: "https", Host: "app.example.net", Path: "/redirect"},
+ },
}
}
@@ -55,14 +50,14 @@ func TestClient(tb testing.TB) *Client {
// match that of the client_id, then the authorization endpoint SHOULD verify
// that the requested redirect_uri matches one of the redirect URLs published by
// the client, and SHOULD block the request from proceeding if not.
-func (c *Client) ValidateRedirectURI(redirectURI *URL) bool {
+func (c *Client) ValidateRedirectURI(redirectURI *url.URL) bool {
if redirectURI == nil {
return false
}
- rHost, rPort, err := net.SplitHostPort(string(redirectURI.Host()))
+ rHost, rPort, err := net.SplitHostPort(redirectURI.Host)
if err != nil {
- rHost = string(redirectURI.Host())
+ rHost = redirectURI.Hostname()
}
cHost, cPort, err := net.SplitHostPort(c.ID.clientID.Host)
@@ -70,7 +65,7 @@ func (c *Client) ValidateRedirectURI(redirectURI *URL) bool {
cHost = c.ID.clientID.Hostname()
}
- if bytes.EqualFold(redirectURI.Scheme(), []byte(c.ID.clientID.Scheme)) &&
+ if strings.EqualFold(redirectURI.Scheme, c.ID.clientID.Scheme) &&
strings.EqualFold(rHost, cHost) &&
strings.EqualFold(rPort, cPort) {
return true
@@ -97,7 +92,7 @@ func (c Client) GetName() string {
}
// GetURL safe returns first URL, if any.
-func (c Client) GetURL() *URL {
+func (c Client) GetURL() *url.URL {
if len(c.URL) == 0 {
return nil
}
@@ -106,7 +101,7 @@ func (c Client) GetURL() *URL {
}
// GetLogo safe returns first logo, if any.
-func (c Client) GetLogo() *URL {
+func (c Client) GetLogo() *url.URL {
if len(c.Logo) == 0 {
return nil
}
diff --git a/internal/domain/client_test.go b/internal/domain/client_test.go
index 0699c53..ae7cef8 100644
--- a/internal/domain/client_test.go
+++ b/internal/domain/client_test.go
@@ -1,7 +1,7 @@
package domain_test
import (
- "fmt"
+ "net/url"
"testing"
"source.toby3d.me/toby3d/auth/internal/domain"
@@ -12,20 +12,17 @@ func TestClient_ValidateRedirectURI(t *testing.T) {
client := domain.TestClient(t)
- for _, tc := range []struct {
- name string
- in *domain.URL
- }{
- {name: "client_id prefix", in: domain.TestURL(t, fmt.Sprint(client.ID, "/callback"))},
- {name: "registered redirect_uri", in: client.RedirectURI[len(client.RedirectURI)-1]},
+ for name, in := range map[string]*url.URL{
+ "client_id prefix": client.ID.URL().JoinPath("/callback"),
+ "registered redirect_uri": client.RedirectURI[len(client.RedirectURI)-1],
} {
- tc := tc
+ name, in := name, in
- t.Run(tc.name, func(t *testing.T) {
+ t.Run(name, func(t *testing.T) {
t.Parallel()
- if result := client.ValidateRedirectURI(tc.in); !result {
- t.Errorf("ValidateRedirectURI(%v) = %t, want %t", tc.in, result, true)
+ if out := client.ValidateRedirectURI(in); !out {
+ t.Errorf("ValidateRedirectURI(%v) = %t, want %t", in, out, true)
}
})
}
diff --git a/internal/domain/error.go b/internal/domain/error.go
index 03302ce..b0d5748 100644
--- a/internal/domain/error.go
+++ b/internal/domain/error.go
@@ -2,10 +2,10 @@ package domain
import (
"fmt"
+ "net/url"
"strconv"
"strings"
- http "github.com/valyala/fasthttp"
"golang.org/x/xerrors"
"source.toby3d.me/toby3d/auth/internal/common"
@@ -226,8 +226,8 @@ func (e Error) FormatError(printer xerrors.Printer) error {
// SetReirectURI sets fasthttp.QueryArgs with the request state, code,
// description and error URI in the provided fasthttp.URI.
-func (e Error) SetReirectURI(uri *http.URI) {
- if uri == nil {
+func (e Error) SetReirectURI(u *url.URL) {
+ if u == nil {
return
}
@@ -241,7 +241,7 @@ func (e Error) SetReirectURI(uri *http.URI) {
continue
}
- uri.QueryArgs().Set(key, val)
+ u.Query().Set(key, val)
}
}
diff --git a/internal/domain/me.go b/internal/domain/me.go
index d89d870..9a6e6a3 100644
--- a/internal/domain/me.go
+++ b/internal/domain/me.go
@@ -7,21 +7,19 @@ import (
"strconv"
"strings"
"testing"
-
- http "github.com/valyala/fasthttp"
)
// Me is a URL user identifier.
type Me struct {
- id *http.URI
+ id *url.URL
}
// ParseMe parse string as me URL identifier.
//
//nolint:funlen,cyclop
func ParseMe(raw string) (*Me, error) {
- id := http.AcquireURI()
- if err := id.Parse(nil, []byte(raw)); err != nil {
+ id, err := url.Parse(raw)
+ if err != nil {
return nil, NewError(
ErrorCodeInvalidRequest,
err.Error(),
@@ -30,8 +28,7 @@ func ParseMe(raw string) (*Me, error) {
)
}
- scheme := string(id.Scheme())
- if scheme != "http" && scheme != "https" {
+ if id.Scheme != "http" && id.Scheme != "https" {
return nil, NewError(
ErrorCodeInvalidRequest,
"profile URL MUST have either an https or http scheme",
@@ -40,8 +37,11 @@ func ParseMe(raw string) (*Me, error) {
)
}
- path := string(id.PathOriginal())
- if path == "" || strings.Contains(path, "/.") || strings.Contains(path, "/..") {
+ if id.Path == "" {
+ id.Path = "/"
+ }
+
+ if strings.Contains(id.Path, "/.") || strings.Contains(id.Path, "/..") {
return nil, NewError(
ErrorCodeInvalidRequest,
"profile URL MUST contain a path component (/ is a valid path), MUST NOT contain single-dot "+
@@ -51,7 +51,7 @@ func ParseMe(raw string) (*Me, error) {
)
}
- if id.Hash() != nil {
+ if id.Fragment != "" {
return nil, NewError(
ErrorCodeInvalidRequest,
"profile URL MUST NOT contain a fragment component",
@@ -60,7 +60,7 @@ func ParseMe(raw string) (*Me, error) {
)
}
- if id.Username() != nil || id.Password() != nil {
+ if id.User != nil {
return nil, NewError(
ErrorCodeInvalidRequest,
"profile URL MUST NOT contain a username or password component",
@@ -69,8 +69,7 @@ func ParseMe(raw string) (*Me, error) {
)
}
- domain := string(id.Host())
- if domain == "" {
+ if id.Host == "" {
return nil, NewError(
ErrorCodeInvalidRequest,
"profile host name MUST be a domain name",
@@ -79,7 +78,7 @@ func ParseMe(raw string) (*Me, error) {
)
}
- if _, port, _ := net.SplitHostPort(domain); port != "" {
+ if _, port, _ := net.SplitHostPort(id.Host); port != "" {
return nil, NewError(
ErrorCodeInvalidRequest,
"profile MUST NOT contain a port",
@@ -88,7 +87,7 @@ func ParseMe(raw string) (*Me, error) {
)
}
- if net.ParseIP(domain) != nil {
+ if net.ParseIP(id.Host) != nil {
return nil, NewError(
ErrorCodeInvalidRequest,
"profile MUST NOT be ipv4 or ipv6 addresses",
@@ -146,37 +145,15 @@ func (m Me) MarshalJSON() ([]byte, error) {
return []byte(strconv.Quote(m.String())), nil
}
-// URI returns copy of parsed me in *fasthttp.URI representation.
-// This copy MUST be released via fasthttp.ReleaseURI.
-func (m Me) URI() *http.URI {
- if m.id == nil {
- return nil
- }
-
- u := http.AcquireURI()
- m.id.CopyTo(u)
-
- return u
-}
-
// URL returns copy of parsed me in *url.URL representation.
func (m Me) URL() *url.URL {
if m.id == nil {
return nil
}
- return &url.URL{
- ForceQuery: false,
- Fragment: string(m.id.Hash()),
- Host: string(m.id.Host()),
- Opaque: "",
- Path: string(m.id.Path()),
- RawFragment: "",
- RawPath: string(m.id.PathOriginal()),
- RawQuery: string(m.id.QueryString()),
- Scheme: string(m.id.Scheme()),
- User: nil,
- }
+ out, _ := url.Parse(m.id.String())
+
+ return out
}
// String returns string representation of me.
diff --git a/internal/domain/metadata.go b/internal/domain/metadata.go
index 8f46c5f..f078453 100644
--- a/internal/domain/metadata.go
+++ b/internal/domain/metadata.go
@@ -1,6 +1,9 @@
package domain
-import "testing"
+import (
+ "net/url"
+ "testing"
+)
type Metadata struct {
// The server's issuer identifier. The issuer identifier is a URL that
@@ -14,33 +17,33 @@ type Metadata struct {
Issuer *ClientID
// The Authorization Endpoint.
- AuthorizationEndpoint *URL
+ AuthorizationEndpoint *url.URL
// The Token Endpoint.
- TokenEndpoint *URL
+ TokenEndpoint *url.URL
// The Ticket Endpoint.
- TicketEndpoint *URL
+ TicketEndpoint *url.URL
// The Micropub Endpoint.
- MicropubEndpoint *URL
+ MicropubEndpoint *url.URL
// The Microsub Endpoint.
- MicrosubEndpoint *URL
+ MicrosubEndpoint *url.URL
// The Introspection Endpoint.
- IntrospectionEndpoint *URL
+ IntrospectionEndpoint *url.URL
// The Revocation Endpoint.
- RevocationEndpoint *URL
+ RevocationEndpoint *url.URL
// The User Info Endpoint.
- UserinfoEndpoint *URL
+ UserinfoEndpoint *url.URL
// URL of a page containing human-readable information that developers
// might need to know when using the server. This might be a link to the
// IndieAuth spec or something more personal to your implementation.
- ServiceDocumentation *URL
+ ServiceDocumentation *url.URL
// JSON array containing scope values supported by the IndieAuth server.
// Servers MAY choose not to advertise some supported scope values even
@@ -79,15 +82,15 @@ func TestMetadata(tb testing.TB) *Metadata {
return &Metadata{
Issuer: TestClientID(tb),
- AuthorizationEndpoint: TestURL(tb, "https://indieauth.example.com/auth"),
- TokenEndpoint: TestURL(tb, "https://indieauth.example.com/token"),
- TicketEndpoint: TestURL(tb, "https://auth.example.org/ticket"),
- MicropubEndpoint: TestURL(tb, "https://micropub.example.com/"),
- MicrosubEndpoint: TestURL(tb, "https://microsub.example.com/"),
- IntrospectionEndpoint: TestURL(tb, "https://indieauth.example.com/introspect"),
- RevocationEndpoint: TestURL(tb, "https://indieauth.example.com/revocation"),
- UserinfoEndpoint: TestURL(tb, "https://indieauth.example.com/userinfo"),
- ServiceDocumentation: TestURL(tb, "https://indieauth.net/draft/"),
+ AuthorizationEndpoint: &url.URL{Scheme: "https", Host: "indieauth.example.com", Path: "/auth"},
+ TokenEndpoint: &url.URL{Scheme: "https", Host: "indieauth.example.com", Path: "/token"},
+ TicketEndpoint: &url.URL{Scheme: "https", Host: "auth.example.org", Path: "/ticket"},
+ MicropubEndpoint: &url.URL{Scheme: "https", Host: "micropub.example.com", Path: "/"},
+ MicrosubEndpoint: &url.URL{Scheme: "https", Host: "microsub.example.com", Path: "/"},
+ IntrospectionEndpoint: &url.URL{Scheme: "https", Host: "indieauth.example.com", Path: "/introspect"},
+ RevocationEndpoint: &url.URL{Scheme: "https", Host: "indieauth.example.com", Path: "/revocation"},
+ UserinfoEndpoint: &url.URL{Scheme: "https", Host: "indieauth.example.com", Path: "/userinfo"},
+ ServiceDocumentation: &url.URL{Scheme: "https", Host: "indieauth.net", Path: "/draft/"},
ScopesSupported: Scopes{
ScopeBlock,
ScopeChannels,
diff --git a/internal/domain/profile.go b/internal/domain/profile.go
index 3375357..55fdb01 100644
--- a/internal/domain/profile.go
+++ b/internal/domain/profile.go
@@ -1,21 +1,22 @@
package domain
import (
+ "net/url"
"testing"
)
// Profile describes the data about the user.
type Profile struct {
- Photo []*URL `json:"photo,omitempty"`
- URL []*URL `json:"url,omitempty"`
- Email []*Email `json:"email,omitempty"`
- Name []string `json:"name,omitempty"`
+ Photo []*url.URL `json:"photo,omitempty"`
+ URL []*url.URL `json:"url,omitempty"`
+ Email []*Email `json:"email,omitempty"`
+ Name []string `json:"name,omitempty"`
}
func NewProfile() *Profile {
return &Profile{
- Photo: make([]*URL, 0),
- URL: make([]*URL, 0),
+ Photo: make([]*url.URL, 0),
+ URL: make([]*url.URL, 0),
Email: make([]*Email, 0),
Name: make([]string, 0),
}
@@ -28,8 +29,8 @@ func TestProfile(tb testing.TB) *Profile {
return &Profile{
Email: []*Email{TestEmail(tb)},
Name: []string{"Example User"},
- Photo: []*URL{TestURL(tb, "https://user.example.net/photo.jpg")},
- URL: []*URL{TestURL(tb, "https://user.example.net/")},
+ Photo: []*url.URL{{Scheme: "https", Host: "user.example.net", Path: "/photo.jpg"}},
+ URL: []*url.URL{{Scheme: "https", Host: "user.example.net", Path: "/"}},
}
}
@@ -51,7 +52,7 @@ func (p Profile) HasURL() bool {
}
// GetURL safe returns first URL, if any.
-func (p Profile) GetURL() *URL {
+func (p Profile) GetURL() *url.URL {
if len(p.URL) == 0 {
return nil
}
@@ -64,7 +65,7 @@ func (p Profile) HasPhoto() bool {
}
// GetPhoto safe returns first photo, if any.
-func (p Profile) GetPhoto() *URL {
+func (p Profile) GetPhoto() *url.URL {
if len(p.Photo) == 0 {
return nil
}
diff --git a/internal/domain/session.go b/internal/domain/session.go
index 23869d7..bda7542 100644
--- a/internal/domain/session.go
+++ b/internal/domain/session.go
@@ -1,6 +1,7 @@
package domain
import (
+ "net/url"
"testing"
"source.toby3d.me/toby3d/auth/internal/random"
@@ -9,7 +10,7 @@ import (
//nolint:tagliatelle
type Session struct {
ClientID *ClientID `json:"client_id"`
- RedirectURI *URL `json:"redirect_uri"`
+ RedirectURI *url.URL `json:"redirect_uri"`
Me *Me `json:"me"`
Profile *Profile `json:"profile,omitempty"`
Scope Scopes `json:"scope"`
@@ -36,7 +37,7 @@ func TestSession(tb testing.TB) *Session {
CodeChallengeMethod: CodeChallengeMethodPLAIN,
Profile: TestProfile(tb),
Me: TestMe(tb, "https://user.example.net/"),
- RedirectURI: TestURL(tb, "https://example.com/callback"),
+ RedirectURI: &url.URL{Scheme: "https", Host: "example.com", Path: "/callback"},
Scope: Scopes{
ScopeEmail,
ScopeProfile,
diff --git a/internal/domain/ticket.go b/internal/domain/ticket.go
index 63289a2..06ccc4f 100644
--- a/internal/domain/ticket.go
+++ b/internal/domain/ticket.go
@@ -1,12 +1,13 @@
package domain
import (
+ "net/url"
"testing"
)
type Ticket struct {
// The access token will work at this URL.
- Resource *URL
+ Resource *url.URL
// The access token should be used when acting on behalf of this URL.
Subject *Me
@@ -20,7 +21,7 @@ func TestTicket(tb testing.TB) *Ticket {
tb.Helper()
return &Ticket{
- Resource: TestURL(tb, "https://alice.example.com/private/"),
+ Resource: &url.URL{Scheme: "https", Host: "alice.example.com", Path: "/private/"},
Subject: TestMe(tb, "https://bob.example.com/"),
Ticket: "32985723984723985792834",
}
diff --git a/internal/domain/url.go b/internal/domain/url.go
index c922c41..6a95bf7 100644
--- a/internal/domain/url.go
+++ b/internal/domain/url.go
@@ -5,23 +5,21 @@ import (
"net/url"
"strconv"
"testing"
-
- http "github.com/valyala/fasthttp"
)
// URL describe any valid HTTP URL.
type URL struct {
- *http.URI
+ *url.URL
}
// ParseURL parse string as URL.
func ParseURL(src string) (*URL, error) {
- u := http.AcquireURI()
- if err := u.Parse(nil, []byte(src)); err != nil {
+ u, err := url.Parse(src)
+ if err != nil {
return nil, fmt.Errorf("cannot parse URL: %w", err)
}
- return &URL{URI: u}, nil
+ return &URL{URL: u}, nil
}
// MustParseURL parse string as URL or panic.
@@ -38,11 +36,10 @@ func MustParseURL(src string) *URL {
func TestURL(tb testing.TB, src string) *URL {
tb.Helper()
- u := http.AcquireURI()
- u.Update(src)
+ u, _ := url.Parse(src)
return &URL{
- URI: u,
+ URL: u,
}
}
@@ -78,17 +75,3 @@ func (u *URL) UnmarshalJSON(v []byte) error {
func (u URL) MarshalJSON() ([]byte, error) {
return []byte(strconv.Quote(u.String())), nil
}
-
-// URL returns url.URL representation of URL.
-func (u URL) URL() *url.URL {
- if u.URI == nil {
- return nil
- }
-
- result, err := url.ParseRequestURI(u.String())
- if err != nil {
- return nil
- }
-
- return result
-}
diff --git a/internal/domain/user.go b/internal/domain/user.go
index c2f0fd3..259969c 100644
--- a/internal/domain/user.go
+++ b/internal/domain/user.go
@@ -1,17 +1,18 @@
package domain
import (
+ "net/url"
"testing"
)
type User struct {
Me *Me
- AuthorizationEndpoint *URL
- IndieAuthMetadata *URL
- Micropub *URL
- Microsub *URL
- TicketEndpoint *URL
- TokenEndpoint *URL
+ AuthorizationEndpoint *url.URL
+ IndieAuthMetadata *url.URL
+ Micropub *url.URL
+ Microsub *url.URL
+ TicketEndpoint *url.URL
+ TokenEndpoint *url.URL
*Profile
}
@@ -22,11 +23,14 @@ func TestUser(tb testing.TB) *User {
return &User{
Profile: TestProfile(tb),
Me: TestMe(tb, "https://user.example.net/"),
- AuthorizationEndpoint: TestURL(tb, "https://example.org/auth"),
- IndieAuthMetadata: TestURL(tb, "https://example.org/.well-known/oauth-authorization-server"),
- Micropub: TestURL(tb, "https://microsub.example.org/"),
- Microsub: TestURL(tb, "https://micropub.example.org/"),
- TicketEndpoint: TestURL(tb, "https://example.org/ticket"),
- TokenEndpoint: TestURL(tb, "https://example.org/token"),
+ AuthorizationEndpoint: &url.URL{Scheme: "https", Host: "example.org", Path: "/auth"},
+ IndieAuthMetadata: &url.URL{
+ Scheme: "https", Host: "example.org",
+ Path: "/.well-known/oauth-authorization-server",
+ },
+ Micropub: &url.URL{Scheme: "https", Host: "microsub.example.org", Path: "/"},
+ Microsub: &url.URL{Scheme: "https", Host: "micropub.example.org", Path: "/"},
+ TicketEndpoint: &url.URL{Scheme: "https", Host: "example.org", Path: "/ticket"},
+ TokenEndpoint: &url.URL{Scheme: "https", Host: "example.org", Path: "/token"},
}
}
diff --git a/internal/httputil/httputil.go b/internal/httputil/httputil.go
index 9b22a36..d51b62a 100644
--- a/internal/httputil/httputil.go
+++ b/internal/httputil/httputil.go
@@ -20,8 +20,8 @@ var ErrEndpointNotExist = domain.NewError(
"https://indieauth.net/source/#discovery-0",
)
-func ExtractEndpoints(resp *http.Response, rel string) []*domain.URL {
- results := make([]*domain.URL, 0)
+func ExtractEndpoints(resp *http.Response, rel string) []*url.URL {
+ results := make([]*url.URL, 0)
urls, err := ExtractEndpointsFromHeader(resp, rel)
if err == nil {
@@ -36,40 +36,40 @@ func ExtractEndpoints(resp *http.Response, rel string) []*domain.URL {
return results
}
-func ExtractEndpointsFromHeader(resp *http.Response, rel string) ([]*domain.URL, error) {
- results := make([]*domain.URL, 0)
+func ExtractEndpointsFromHeader(resp *http.Response, rel string) ([]*url.URL, error) {
+ results := make([]*url.URL, 0)
for _, link := range linkheader.Parse(string(resp.Header.Peek(http.HeaderLink))) {
if !strings.EqualFold(link.Rel, rel) {
continue
}
- u := http.AcquireURI()
- if err := u.Parse(resp.Header.Peek(http.HeaderHost), []byte(link.URL)); err != nil {
+ u, err := url.ParseRequestURI(link.URL)
+ if err != nil {
return nil, fmt.Errorf("cannot parse header endpoint: %w", err)
}
- results = append(results, &domain.URL{URI: u})
+ results = append(results, u)
}
return results, nil
}
-func ExtractEndpointsFromBody(resp *http.Response, rel string) ([]*domain.URL, error) {
+func ExtractEndpointsFromBody(resp *http.Response, rel string) ([]*url.URL, error) {
endpoints, ok := microformats.Parse(bytes.NewReader(resp.Body()), nil).Rels[rel]
if !ok || len(endpoints) == 0 {
return nil, ErrEndpointNotExist
}
- results := make([]*domain.URL, 0)
+ results := make([]*url.URL, 0)
for i := range endpoints {
- u := http.AcquireURI()
- if err := u.Parse(resp.Header.Peek(http.HeaderHost), []byte(endpoints[i])); err != nil {
+ u, err := url.Parse(endpoints[i])
+ if err != nil {
return nil, fmt.Errorf("cannot parse body endpoint: %w", err)
}
- results = append(results, &domain.URL{URI: u})
+ results = append(results, u)
}
return results, nil
diff --git a/internal/metadata/repository/http/http_metadata.go b/internal/metadata/repository/http/http_metadata.go
index b35d622..618432a 100644
--- a/internal/metadata/repository/http/http_metadata.go
+++ b/internal/metadata/repository/http/http_metadata.go
@@ -74,15 +74,15 @@ func (repo *httpMetadataRepository) Get(ctx context.Context, me *domain.Me) (*do
//nolint:exhaustivestruct // TODO(toby3d)
return &domain.Metadata{
- AuthorizationEndpoint: data.AuthorizationEndpoint,
+ AuthorizationEndpoint: data.AuthorizationEndpoint.URL,
AuthorizationResponseIssParameterSupported: data.AuthorizationResponseIssParameterSupported,
CodeChallengeMethodsSupported: data.CodeChallengeMethodsSupported,
GrantTypesSupported: data.GrantTypesSupported,
Issuer: data.Issuer,
ResponseTypesSupported: data.ResponseTypesSupported,
ScopesSupported: data.ScopesSupported,
- ServiceDocumentation: data.ServiceDocumentation,
- TokenEndpoint: data.TokenEndpoint,
+ ServiceDocumentation: data.ServiceDocumentation.URL,
+ TokenEndpoint: data.TokenEndpoint.URL,
// TODO(toby3d): support extensions?
// Micropub: data.Micropub,
// Microsub: data.Microsub,
diff --git a/internal/profile/repository/http/http_profile.go b/internal/profile/repository/http/http_profile.go
index d6eccb2..ffd4ef1 100644
--- a/internal/profile/repository/http/http_profile.go
+++ b/internal/profile/repository/http/http_profile.go
@@ -3,6 +3,7 @@ package http
import (
"context"
"fmt"
+ "net/url"
http "github.com/valyala/fasthttp"
@@ -66,12 +67,12 @@ func (repo *httpProfileRepository) Get(ctx context.Context, me *domain.Me) (*dom
}
for _, rawURL := range httputil.ExtractProperty(resp, hCard, propertyURL) {
- url, ok := rawURL.(string)
+ rawURL, ok := rawURL.(string)
if !ok {
continue
}
- if u, err := domain.ParseURL(url); err == nil {
+ if u, err := url.Parse(rawURL); err == nil {
result.URL = append(result.URL, u)
}
}
@@ -82,7 +83,7 @@ func (repo *httpProfileRepository) Get(ctx context.Context, me *domain.Me) (*dom
continue
}
- if p, err := domain.ParseURL(photo); err == nil {
+ if p, err := url.Parse(photo); err == nil {
result.Photo = append(result.Photo, p)
}
}
diff --git a/internal/ticket/delivery/http/ticket_http.go b/internal/ticket/delivery/http/ticket_http.go
index 59c6ce3..67a6d89 100644
--- a/internal/ticket/delivery/http/ticket_http.go
+++ b/internal/ticket/delivery/http/ticket_http.go
@@ -136,7 +136,7 @@ func (h *RequestHandler) handleSend(ctx *http.RequestCtx) {
ticket := &domain.Ticket{
Ticket: "",
- Resource: req.Resource,
+ Resource: req.Resource.URL,
Subject: req.Subject,
}
@@ -177,7 +177,7 @@ func (h *RequestHandler) handleRedeem(ctx *http.RequestCtx) {
token, err := h.tickets.Redeem(ctx, &domain.Ticket{
Ticket: req.Ticket,
- Resource: req.Resource,
+ Resource: req.Resource.URL,
Subject: req.Subject,
})
if err != nil {
diff --git a/internal/ticket/delivery/http/ticket_http_test.go b/internal/ticket/delivery/http/ticket_http_test.go
index 8204f00..9eec61b 100644
--- a/internal/ticket/delivery/http/ticket_http_test.go
+++ b/internal/ticket/delivery/http/ticket_http_test.go
@@ -81,7 +81,7 @@ func NewDependencies(tb testing.TB) Dependencies {
r := router.New()
// NOTE(toby3d): private resource
- r.GET(ticket.Resource.URL().EscapedPath(), func(ctx *http.RequestCtx) {
+ r.GET(ticket.Resource.Path, func(ctx *http.RequestCtx) {
ctx.SuccessString(common.MIMETextHTMLCharsetUTF8,
``)
})
diff --git a/internal/ticket/repository/sqlite3/sqlite3_ticket.go b/internal/ticket/repository/sqlite3/sqlite3_ticket.go
index bcd64cc..1c6b615 100644
--- a/internal/ticket/repository/sqlite3/sqlite3_ticket.go
+++ b/internal/ticket/repository/sqlite3/sqlite3_ticket.go
@@ -5,6 +5,7 @@ import (
"database/sql"
"errors"
"fmt"
+ "net/url"
"time"
"github.com/jmoiron/sqlx"
@@ -117,5 +118,5 @@ func NewTicket(src *domain.Ticket) *Ticket {
func (t *Ticket) Populate(dst *domain.Ticket) {
dst.Ticket = t.Ticket
dst.Subject, _ = domain.ParseMe(t.Subject)
- dst.Resource, _ = domain.ParseURL(t.Resource)
+ dst.Resource, _ = url.Parse(t.Resource)
}
diff --git a/internal/ticket/repository/sqlite3/sqlite3_ticket_test.go b/internal/ticket/repository/sqlite3/sqlite3_ticket_test.go
index 3e640a1..55307ea 100644
--- a/internal/ticket/repository/sqlite3/sqlite3_ticket_test.go
+++ b/internal/ticket/repository/sqlite3/sqlite3_ticket_test.go
@@ -12,7 +12,7 @@ import (
repository "source.toby3d.me/toby3d/auth/internal/ticket/repository/sqlite3"
)
-//nolint: gochecknoglobals // slices cannot be contants
+// nolint: gochecknoglobals // slices cannot be contants
var tableColumns = []string{"created_at", "resource", "subject", "ticket"}
func TestCreate(t *testing.T) {
diff --git a/internal/ticket/usecase/ticket_ucase.go b/internal/ticket/usecase/ticket_ucase.go
index cb74ea1..b285525 100644
--- a/internal/ticket/usecase/ticket_ucase.go
+++ b/internal/ticket/usecase/ticket_ucase.go
@@ -3,6 +3,7 @@ package usecase
import (
"context"
"fmt"
+ "net/url"
"time"
json "github.com/goccy/go-json"
@@ -59,7 +60,7 @@ func (useCase *ticketUseCase) Generate(ctx context.Context, tkt *domain.Ticket)
return fmt.Errorf("cannot discovery ticket subject: %w", err)
}
- var ticketEndpoint *domain.URL
+ var ticketEndpoint *url.URL
// NOTE(toby3d): find metadata first
if metadata, err := httputil.ExtractMetadata(resp, useCase.client); err == nil && metadata != nil {
@@ -80,7 +81,7 @@ func (useCase *ticketUseCase) Generate(ctx context.Context, tkt *domain.Ticket)
req.Reset()
req.Header.SetMethod(http.MethodPost)
- req.SetRequestURIBytes(ticketEndpoint.RequestURI())
+ req.SetRequestURI(ticketEndpoint.String())
req.Header.SetContentType(common.MIMEApplicationForm)
req.PostArgs().Set("ticket", tkt.Ticket)
req.PostArgs().Set("subject", tkt.Subject.String())
@@ -107,7 +108,7 @@ func (useCase *ticketUseCase) Redeem(ctx context.Context, tkt *domain.Ticket) (*
return nil, fmt.Errorf("cannot discovery ticket resource: %w", err)
}
- var tokenEndpoint *domain.URL
+ var tokenEndpoint *url.URL
// NOTE(toby3d): find metadata first
if metadata, err := httputil.ExtractMetadata(resp, useCase.client); err == nil && metadata != nil {
diff --git a/internal/ticket/usecase/ticket_ucase_test.go b/internal/ticket/usecase/ticket_ucase_test.go
index 61411ed..73199a8 100644
--- a/internal/ticket/usecase/ticket_ucase_test.go
+++ b/internal/ticket/usecase/ticket_ucase_test.go
@@ -21,7 +21,7 @@ func TestRedeem(t *testing.T) {
ticket := domain.TestTicket(t)
router := router.New()
- router.GET(string(ticket.Resource.Path()), func(ctx *http.RequestCtx) {
+ router.GET(string(ticket.Resource.Path), func(ctx *http.RequestCtx) {
ctx.SuccessString(common.MIMETextHTMLCharsetUTF8, ``)
})
diff --git a/internal/token/delivery/http/token_http.go b/internal/token/delivery/http/token_http.go
index 6139cbc..6b94433 100644
--- a/internal/token/delivery/http/token_http.go
+++ b/internal/token/delivery/http/token_http.go
@@ -244,7 +244,7 @@ func (h *RequestHandler) handleExchange(ctx *http.RequestCtx) {
token, profile, err := h.tokens.Exchange(ctx, token.ExchangeOptions{
ClientID: req.ClientID,
- RedirectURI: req.RedirectURI,
+ RedirectURI: req.RedirectURI.URL,
Code: req.Code,
CodeVerifier: req.CodeVerifier,
})
diff --git a/internal/token/usecase.go b/internal/token/usecase.go
index 92bddbf..818514f 100644
--- a/internal/token/usecase.go
+++ b/internal/token/usecase.go
@@ -2,6 +2,7 @@ package token
import (
"context"
+ "net/url"
"source.toby3d.me/toby3d/auth/internal/domain"
)
@@ -9,7 +10,7 @@ import (
type (
ExchangeOptions struct {
ClientID *domain.ClientID
- RedirectURI *domain.URL
+ RedirectURI *url.URL
Code string
CodeVerifier string
}
diff --git a/internal/user/repository/http/http_user.go b/internal/user/repository/http/http_user.go
index 0fac2d7..bd3252f 100644
--- a/internal/user/repository/http/http_user.go
+++ b/internal/user/repository/http/http_user.go
@@ -3,6 +3,7 @@ package http
import (
"context"
"fmt"
+ "net/url"
http "github.com/valyala/fasthttp"
@@ -117,7 +118,7 @@ func extractUser(dst *domain.User, src *http.Response) {
}
}
-//nolint: cyclop
+//nolint:cyclop
func extractProfile(dst *domain.Profile, src *http.Response) {
for _, name := range httputil.ExtractProperty(src, hCard, propertyName) {
if n, ok := name.(string); ok {
@@ -137,12 +138,12 @@ func extractProfile(dst *domain.Profile, src *http.Response) {
}
for _, rawURL := range httputil.ExtractProperty(src, hCard, propertyURL) {
- url, ok := rawURL.(string)
+ rawURL, ok := rawURL.(string)
if !ok {
continue
}
- if u, err := domain.ParseURL(url); err == nil {
+ if u, err := url.Parse(rawURL); err == nil {
dst.URL = append(dst.URL, u)
}
}
@@ -153,7 +154,7 @@ func extractProfile(dst *domain.Profile, src *http.Response) {
continue
}
- if p, err := domain.ParseURL(photo); err == nil {
+ if p, err := url.Parse(photo); err == nil {
dst.Photo = append(dst.Photo, p)
}
}
diff --git a/internal/user/repository/http/http_user_test.go b/internal/user/repository/http/http_user_test.go
index 328e237..083ad30 100644
--- a/internal/user/repository/http/http_user_test.go
+++ b/internal/user/repository/http/http_user_test.go
@@ -84,7 +84,7 @@ func testHandler(tb testing.TB, user *domain.User) http.RequestHandler {
testBody, user.Name[0], user.URL[0].String(), user.Photo[0].String(), user.Email[0],
))
})
- router.GET(string(user.IndieAuthMetadata.Path()), func(ctx *http.RequestCtx) {
+ router.GET(user.IndieAuthMetadata.Path, func(ctx *http.RequestCtx) {
ctx.SuccessString(common.MIMEApplicationJSONCharsetUTF8, `{
"issuer": "`+user.Me.String()+`",
"authorization_endpoint": "`+user.AuthorizationEndpoint.String()+`",