mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2024-10-31 22:40:01 +00:00
[feature] Add Mastodon-compatible HTTP signature fallback (#2659)
On outgoing `GET` requests that are signed (e.g. authorized fetch), if the initial request fails with `401`, try again, but _without_ the query parameters included in the HTTP signature. This is primarily useful for compatibility with Mastodon; though hopefully this can be removed in the not-too-distant future, as they've started changing their behavior here. Signed-off-by: Milas Bowman <devnull@milas.dev>
This commit is contained in:
parent
2db115fa36
commit
af1a26a68f
3 changed files with 16 additions and 8 deletions
|
@ -136,7 +136,7 @@ func HTTPSignatureVerifier(ctx context.Context) httpsig.VerifierWithOptions {
|
||||||
|
|
||||||
// SetHTTPSignatureVerifier stores the given http signature verifier and returns the
|
// SetHTTPSignatureVerifier stores the given http signature verifier and returns the
|
||||||
// wrapped context. See HTTPSignatureVerifier() for further information on the verifier value.
|
// wrapped context. See HTTPSignatureVerifier() for further information on the verifier value.
|
||||||
func SetHTTPSignatureVerifier(ctx context.Context, verifier httpsig.Verifier) context.Context {
|
func SetHTTPSignatureVerifier(ctx context.Context, verifier httpsig.VerifierWithOptions) context.Context {
|
||||||
return context.WithValue(ctx, httpSigVerifierKey, verifier)
|
return context.WithValue(ctx, httpSigVerifierKey, verifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,13 +30,13 @@
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewGETSigner returns a new httpsig.Signer instance initialized with GTS GET preferences.
|
// NewGETSigner returns a new httpsig.Signer instance initialized with GTS GET preferences.
|
||||||
func NewGETSigner(expiresIn int64) (httpsig.Signer, error) {
|
func NewGETSigner(expiresIn int64) (httpsig.SignerWithOptions, error) {
|
||||||
sig, _, err := httpsig.NewSigner(prefs, digestAlgo, getHeaders, httpsig.Signature, expiresIn)
|
sig, _, err := httpsig.NewSigner(prefs, digestAlgo, getHeaders, httpsig.Signature, expiresIn)
|
||||||
return sig, err
|
return sig, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPOSTSigner returns a new httpsig.Signer instance initialized with GTS POST preferences.
|
// NewPOSTSigner returns a new httpsig.Signer instance initialized with GTS POST preferences.
|
||||||
func NewPOSTSigner(expiresIn int64) (httpsig.Signer, error) {
|
func NewPOSTSigner(expiresIn int64) (httpsig.SignerWithOptions, error) {
|
||||||
sig, _, err := httpsig.NewSigner(prefs, digestAlgo, postHeaders, httpsig.Signature, expiresIn)
|
sig, _, err := httpsig.NewSigner(prefs, digestAlgo, postHeaders, httpsig.Signature, expiresIn)
|
||||||
return sig, err
|
return sig, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,8 +84,8 @@ type transport struct {
|
||||||
privkey crypto.PrivateKey
|
privkey crypto.PrivateKey
|
||||||
|
|
||||||
signerExp time.Time
|
signerExp time.Time
|
||||||
getSigner httpsig.Signer
|
getSigner httpsig.SignerWithOptions
|
||||||
postSigner httpsig.Signer
|
postSigner httpsig.SignerWithOptions
|
||||||
signerMu sync.Mutex
|
signerMu sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,7 +97,15 @@ func (t *transport) GET(r *http.Request) (*http.Response, error) {
|
||||||
ctx = gtscontext.SetOutgoingPublicKeyID(ctx, t.pubKeyID)
|
ctx = gtscontext.SetOutgoingPublicKeyID(ctx, t.pubKeyID)
|
||||||
r = r.WithContext(ctx) // replace request ctx.
|
r = r.WithContext(ctx) // replace request ctx.
|
||||||
r.Header.Set("User-Agent", t.controller.userAgent)
|
r.Header.Set("User-Agent", t.controller.userAgent)
|
||||||
return t.controller.client.DoSigned(r, t.signGET())
|
|
||||||
|
resp, err := t.controller.client.DoSigned(r, t.signGET(httpsig.SignatureOption{ExcludeQueryStringFromPathPseudoHeader: false}))
|
||||||
|
if err != nil || resp.StatusCode != http.StatusUnauthorized {
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// try again without the path included in the HTTP signature for better compatibility
|
||||||
|
_ = resp.Body.Close()
|
||||||
|
return t.controller.client.DoSigned(r, t.signGET(httpsig.SignatureOption{ExcludeQueryStringFromPathPseudoHeader: true}))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *transport) POST(r *http.Request, body []byte) (*http.Response, error) {
|
func (t *transport) POST(r *http.Request, body []byte) (*http.Response, error) {
|
||||||
|
@ -112,10 +120,10 @@ func (t *transport) POST(r *http.Request, body []byte) (*http.Response, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// signGET will safely sign an HTTP GET request.
|
// signGET will safely sign an HTTP GET request.
|
||||||
func (t *transport) signGET() httpclient.SignFunc {
|
func (t *transport) signGET(opts httpsig.SignatureOption) httpclient.SignFunc {
|
||||||
return func(r *http.Request) (err error) {
|
return func(r *http.Request) (err error) {
|
||||||
t.safesign(func() {
|
t.safesign(func() {
|
||||||
err = t.getSigner.SignRequest(t.privkey, t.pubKeyID, r, nil)
|
err = t.getSigner.SignRequestWithOptions(t.privkey, t.pubKeyID, r, nil, opts)
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue