diff options
| -rw-r--r-- | cmd/gotosocial/action/server/server.go | 2 | ||||
| -rw-r--r-- | cmd/gotosocial/action/testrig/testrig.go | 2 | ||||
| -rw-r--r-- | internal/api/client/auth/auth.go | 5 | ||||
| -rw-r--r-- | internal/api/client/auth/auth_test.go | 12 | ||||
| -rw-r--r-- | internal/api/client/auth/authorize.go | 2 | ||||
| -rw-r--r-- | internal/api/client/auth/authorize_test.go | 2 | ||||
| -rw-r--r-- | internal/api/client/auth/token.go | 69 | ||||
| -rw-r--r-- | internal/api/client/auth/token_test.go | 215 | ||||
| -rw-r--r-- | internal/api/errorhandling.go | 27 | ||||
| -rw-r--r-- | internal/oauth/errors.go | 26 | ||||
| -rw-r--r-- | internal/oauth/server.go | 24 | ||||
| -rw-r--r-- | internal/processing/oauth.go | 35 | ||||
| -rw-r--r-- | internal/processing/processor.go | 3 | ||||
| -rw-r--r-- | testrig/testmodels.go | 17 | 
14 files changed, 411 insertions, 30 deletions
| diff --git a/cmd/gotosocial/action/server/server.go b/cmd/gotosocial/action/server/server.go index dd41f7708..abb373426 100644 --- a/cmd/gotosocial/action/server/server.go +++ b/cmd/gotosocial/action/server/server.go @@ -165,7 +165,7 @@ var Start action.GTSAction = func(ctx context.Context) error {  	}  	// build client api modules -	authModule := auth.New(dbService, oauthServer, idp, processor) +	authModule := auth.New(dbService, idp, processor)  	accountModule := account.New(processor)  	instanceModule := instance.New(processor)  	appsModule := app.New(processor) diff --git a/cmd/gotosocial/action/testrig/testrig.go b/cmd/gotosocial/action/testrig/testrig.go index cb0557771..8fc87de34 100644 --- a/cmd/gotosocial/action/testrig/testrig.go +++ b/cmd/gotosocial/action/testrig/testrig.go @@ -108,7 +108,7 @@ var Start action.GTSAction = func(ctx context.Context) error {  	}  	// build client api modules -	authModule := auth.New(dbService, oauthServer, idp, processor) +	authModule := auth.New(dbService, idp, processor)  	accountModule := account.New(processor)  	instanceModule := instance.New(processor)  	appsModule := app.New(processor) diff --git a/internal/api/client/auth/auth.go b/internal/api/client/auth/auth.go index 10d374838..a097f8004 100644 --- a/internal/api/client/auth/auth.go +++ b/internal/api/client/auth/auth.go @@ -23,7 +23,6 @@ import (  	"github.com/superseriousbusiness/gotosocial/internal/api"  	"github.com/superseriousbusiness/gotosocial/internal/db" -	"github.com/superseriousbusiness/gotosocial/internal/oauth"  	"github.com/superseriousbusiness/gotosocial/internal/oidc"  	"github.com/superseriousbusiness/gotosocial/internal/processing"  	"github.com/superseriousbusiness/gotosocial/internal/router" @@ -68,16 +67,14 @@ const (  // Module implements the ClientAPIModule interface for  type Module struct {  	db        db.DB -	server    oauth.Server  	idp       oidc.IDP  	processor processing.Processor  }  // New returns a new auth module -func New(db db.DB, server oauth.Server, idp oidc.IDP, processor processing.Processor) api.ClientModule { +func New(db db.DB, idp oidc.IDP, processor processing.Processor) api.ClientModule {  	return &Module{  		db:        db, -		server:    server,  		idp:       idp,  		processor: processor,  	} diff --git a/internal/api/client/auth/auth_test.go b/internal/api/client/auth/auth_test.go index f222f714f..6a7c6ab99 100644 --- a/internal/api/client/auth/auth_test.go +++ b/internal/api/client/auth/auth_test.go @@ -19,6 +19,7 @@  package auth_test  import ( +	"bytes"  	"context"  	"fmt"  	"net/http/httptest" @@ -99,7 +100,7 @@ func (suite *AuthStandardTestSuite) SetupTest() {  	if err != nil {  		panic(err)  	} -	suite.authModule = auth.New(suite.db, suite.oauthServer, suite.idp, suite.processor).(*auth.Module) +	suite.authModule = auth.New(suite.db, suite.idp, suite.processor).(*auth.Module)  	testrig.StandardDBSetup(suite.db, suite.testAccounts)  } @@ -107,7 +108,7 @@ func (suite *AuthStandardTestSuite) TearDownTest() {  	testrig.StandardDBTeardown(suite.db)  } -func (suite *AuthStandardTestSuite) newContext(requestMethod string, requestPath string) (*gin.Context, *httptest.ResponseRecorder) { +func (suite *AuthStandardTestSuite) newContext(requestMethod string, requestPath string, requestBody []byte, bodyContentType string) (*gin.Context, *httptest.ResponseRecorder) {  	// create the recorder and gin test context  	recorder := httptest.NewRecorder()  	ctx, engine := gin.CreateTestContext(recorder) @@ -120,9 +121,14 @@ func (suite *AuthStandardTestSuite) newContext(requestMethod string, requestPath  	host := config.GetHost()  	baseURI := fmt.Sprintf("%s://%s", protocol, host)  	requestURI := fmt.Sprintf("%s/%s", baseURI, requestPath) -	ctx.Request = httptest.NewRequest(requestMethod, requestURI, nil) // the endpoint we're hitting + +	ctx.Request = httptest.NewRequest(requestMethod, requestURI, bytes.NewReader(requestBody)) // the endpoint we're hitting  	ctx.Request.Header.Set("accept", "text/html") +	if bodyContentType != "" { +		ctx.Request.Header.Set("Content-Type", bodyContentType) +	} +  	// trigger the session middleware on the context  	store := memstore.NewStore(make([]byte, 32), make([]byte, 32))  	store.Options(router.SessionOptions()) diff --git a/internal/api/client/auth/authorize.go b/internal/api/client/auth/authorize.go index 6f96484a8..233dacfd2 100644 --- a/internal/api/client/auth/authorize.go +++ b/internal/api/client/auth/authorize.go @@ -246,7 +246,7 @@ func (m *Module) AuthorizePOSTHandler(c *gin.Context) {  		sessionUserID:       {userID},  	} -	if err := m.server.HandleAuthorizeRequest(c.Writer, c.Request); err != nil { +	if err := m.processor.OAuthHandleAuthorizeRequest(c.Writer, c.Request); err != nil {  		api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error(), helpfulAdvice), m.processor.InstanceGet)  	}  } diff --git a/internal/api/client/auth/authorize_test.go b/internal/api/client/auth/authorize_test.go index f9c1eceb6..35b995e70 100644 --- a/internal/api/client/auth/authorize_test.go +++ b/internal/api/client/auth/authorize_test.go @@ -69,7 +69,7 @@ func (suite *AuthAuthorizeTestSuite) TestAccountAuthorizeHandler() {  	}  	doTest := func(testCase authorizeHandlerTestCase) { -		ctx, recorder := suite.newContext(http.MethodGet, auth.OauthAuthorizePath) +		ctx, recorder := suite.newContext(http.MethodGet, auth.OauthAuthorizePath, nil, "")  		user := suite.testUsers["unconfirmed_account"]  		account := suite.testAccounts["unconfirmed_account"] diff --git a/internal/api/client/auth/token.go b/internal/api/client/auth/token.go index 34fb62940..fbbd08404 100644 --- a/internal/api/client/auth/token.go +++ b/internal/api/client/auth/token.go @@ -19,20 +19,22 @@  package auth  import ( +	"net/http"  	"net/url"  	"github.com/superseriousbusiness/gotosocial/internal/api"  	"github.com/superseriousbusiness/gotosocial/internal/gtserror" +	"github.com/superseriousbusiness/gotosocial/internal/oauth"  	"github.com/gin-gonic/gin"  ) -type tokenBody struct { -	ClientID     *string `form:"client_id" json:"client_id" xml:"client_id"` -	ClientSecret *string `form:"client_secret" json:"client_secret" xml:"client_secret"` -	Code         *string `form:"code" json:"code" xml:"code"` +type tokenRequestForm struct {  	GrantType    *string `form:"grant_type" json:"grant_type" xml:"grant_type"` +	Code         *string `form:"code" json:"code" xml:"code"`  	RedirectURI  *string `form:"redirect_uri" json:"redirect_uri" xml:"redirect_uri"` +	ClientID     *string `form:"client_id" json:"client_id" xml:"client_id"` +	ClientSecret *string `form:"client_secret" json:"client_secret" xml:"client_secret"`  	Scope        *string `form:"scope" json:"scope" xml:"scope"`  } @@ -44,35 +46,70 @@ func (m *Module) TokenPOSTHandler(c *gin.Context) {  		return  	} -	form := &tokenBody{} +	help := []string{} + +	form := &tokenRequestForm{}  	if err := c.ShouldBind(form); err != nil { -		api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, helpfulAdvice), m.processor.InstanceGet) +		api.OAuthErrorHandler(c, gtserror.NewErrorBadRequest(oauth.InvalidRequest(), err.Error()))  		return  	}  	c.Request.Form = url.Values{} + +	var grantType string +	if form.GrantType != nil { +		grantType = *form.GrantType +		c.Request.Form.Set("grant_type", grantType) +	} else { +		help = append(help, "grant_type was not set in the token request form, but must be set to authorization_code or client_credentials") +	} +  	if form.ClientID != nil {  		c.Request.Form.Set("client_id", *form.ClientID) +	} else { +		help = append(help, "client_id was not set in the token request form")  	} +  	if form.ClientSecret != nil {  		c.Request.Form.Set("client_secret", *form.ClientSecret) +	} else { +		help = append(help, "client_secret was not set in the token request form")  	} -	if form.Code != nil { -		c.Request.Form.Set("code", *form.Code) -	} -	if form.GrantType != nil { -		c.Request.Form.Set("grant_type", *form.GrantType) -	} +  	if form.RedirectURI != nil {  		c.Request.Form.Set("redirect_uri", *form.RedirectURI) +	} else { +		help = append(help, "redirect_uri was not set in the token request form") +	} + +	var code string +	if form.Code != nil { +		if grantType != "authorization_code" { +			help = append(help, "a code was provided in the token request form, but grant_type was not set to authorization_code") +		} else { +			code = *form.Code +			c.Request.Form.Set("code", code) +		} +	} else if grantType == "authorization_code" { +		help = append(help, "code was not set in the token request form, but must be set since grant_type is authorization_code")  	} +  	if form.Scope != nil {  		c.Request.Form.Set("scope", *form.Scope)  	} -	// pass the writer and request into the oauth server handler, which will -	// take care of writing the oauth token into the response etc -	if err := m.server.HandleTokenRequest(c.Writer, c.Request); err != nil { -		api.ErrorHandler(c, gtserror.NewErrorInternalError(err, helpfulAdvice), m.processor.InstanceGet) +	if len(help) != 0 { +		api.OAuthErrorHandler(c, gtserror.NewErrorBadRequest(oauth.InvalidRequest(), help...)) +		return +	} + +	token, errWithCode := m.processor.OAuthHandleTokenRequest(c.Request) +	if errWithCode != nil { +		api.OAuthErrorHandler(c, errWithCode) +		return  	} + +	c.Header("Cache-Control", "no-store") +	c.Header("Pragma", "no-cache") +	c.JSON(http.StatusOK, token)  } diff --git a/internal/api/client/auth/token_test.go b/internal/api/client/auth/token_test.go new file mode 100644 index 000000000..50bbd6918 --- /dev/null +++ b/internal/api/client/auth/token_test.go @@ -0,0 +1,215 @@ +/* +   GoToSocial +   Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org + +   This program is free software: you can redistribute it and/or modify +   it under the terms of the GNU Affero General Public License as published by +   the Free Software Foundation, either version 3 of the License, or +   (at your option) any later version. + +   This program is distributed in the hope that it will be useful, +   but WITHOUT ANY WARRANTY; without even the implied warranty of +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +   GNU Affero General Public License for more details. + +   You should have received a copy of the GNU Affero General Public License +   along with this program.  If not, see <http://www.gnu.org/licenses/>. +*/ + +package auth_test + +import ( +	"context" +	"encoding/json" +	"io/ioutil" +	"net/http" +	"testing" +	"time" + +	"github.com/stretchr/testify/suite" +	apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" +	"github.com/superseriousbusiness/gotosocial/internal/db" +	"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" +	"github.com/superseriousbusiness/gotosocial/testrig" +) + +type TokenTestSuite struct { +	AuthStandardTestSuite +} + +func (suite *TokenTestSuite) TestPOSTTokenEmptyForm() { +	ctx, recorder := suite.newContext(http.MethodPost, "oauth/token", []byte{}, "") +	ctx.Request.Header.Set("accept", "application/json") + +	suite.authModule.TokenPOSTHandler(ctx) + +	suite.Equal(http.StatusBadRequest, recorder.Code) + +	result := recorder.Result() +	defer result.Body.Close() + +	b, err := ioutil.ReadAll(result.Body) +	suite.NoError(err) + +	suite.Equal(`{"error":"invalid_request","error_description":"Bad Request: grant_type was not set in the token request form, but must be set to authorization_code or client_credentials: client_id was not set in the token request form: client_secret was not set in the token request form: redirect_uri was not set in the token request form"}`, string(b)) +} + +func (suite *TokenTestSuite) TestRetrieveClientCredentialsOK() { +	testClient := suite.testClients["local_account_1"] + +	requestBody, w, err := testrig.CreateMultipartFormData( +		"", "", +		map[string]string{ +			"grant_type":    "client_credentials", +			"client_id":     testClient.ID, +			"client_secret": testClient.Secret, +			"redirect_uri":  "http://localhost:8080", +		}) +	if err != nil { +		panic(err) +	} +	bodyBytes := requestBody.Bytes() + +	ctx, recorder := suite.newContext(http.MethodPost, "oauth/token", bodyBytes, w.FormDataContentType()) +	ctx.Request.Header.Set("accept", "application/json") + +	suite.authModule.TokenPOSTHandler(ctx) + +	suite.Equal(http.StatusOK, recorder.Code) + +	result := recorder.Result() +	defer result.Body.Close() + +	b, err := ioutil.ReadAll(result.Body) +	suite.NoError(err) + +	t := &apimodel.Token{} +	err = json.Unmarshal(b, t) +	suite.NoError(err) + +	suite.Equal("Bearer", t.TokenType) +	suite.NotEmpty(t.AccessToken) +	suite.NotEmpty(t.CreatedAt) +	suite.WithinDuration(time.Now(), time.Unix(t.CreatedAt, 0), 1*time.Minute) + +	// there should be a token in the database now too +	dbToken := >smodel.Token{} +	err = suite.db.GetWhere(context.Background(), []db.Where{{Key: "access", Value: t.AccessToken}}, dbToken) +	suite.NoError(err) +	suite.NotNil(dbToken) +} + +func (suite *TokenTestSuite) TestRetrieveAuthorizationCodeOK() { +	testClient := suite.testClients["local_account_1"] +	testUserAuthorizationToken := suite.testTokens["local_account_1_user_authorization_token"] + +	requestBody, w, err := testrig.CreateMultipartFormData( +		"", "", +		map[string]string{ +			"grant_type":    "authorization_code", +			"client_id":     testClient.ID, +			"client_secret": testClient.Secret, +			"redirect_uri":  "http://localhost:8080", +			"code":          testUserAuthorizationToken.Code, +		}) +	if err != nil { +		panic(err) +	} +	bodyBytes := requestBody.Bytes() + +	ctx, recorder := suite.newContext(http.MethodPost, "oauth/token", bodyBytes, w.FormDataContentType()) +	ctx.Request.Header.Set("accept", "application/json") + +	suite.authModule.TokenPOSTHandler(ctx) + +	suite.Equal(http.StatusOK, recorder.Code) + +	result := recorder.Result() +	defer result.Body.Close() + +	b, err := ioutil.ReadAll(result.Body) +	suite.NoError(err) + +	t := &apimodel.Token{} +	err = json.Unmarshal(b, t) +	suite.NoError(err) + +	suite.Equal("Bearer", t.TokenType) +	suite.NotEmpty(t.AccessToken) +	suite.NotEmpty(t.CreatedAt) +	suite.WithinDuration(time.Now(), time.Unix(t.CreatedAt, 0), 1*time.Minute) + +	dbToken := >smodel.Token{} +	err = suite.db.GetWhere(context.Background(), []db.Where{{Key: "access", Value: t.AccessToken}}, dbToken) +	suite.NoError(err) +	suite.NotNil(dbToken) +} + +func (suite *TokenTestSuite) TestRetrieveAuthorizationCodeNoCode() { +	testClient := suite.testClients["local_account_1"] + +	requestBody, w, err := testrig.CreateMultipartFormData( +		"", "", +		map[string]string{ +			"grant_type":    "authorization_code", +			"client_id":     testClient.ID, +			"client_secret": testClient.Secret, +			"redirect_uri":  "http://localhost:8080", +		}) +	if err != nil { +		panic(err) +	} +	bodyBytes := requestBody.Bytes() + +	ctx, recorder := suite.newContext(http.MethodPost, "oauth/token", bodyBytes, w.FormDataContentType()) +	ctx.Request.Header.Set("accept", "application/json") + +	suite.authModule.TokenPOSTHandler(ctx) + +	suite.Equal(http.StatusBadRequest, recorder.Code) + +	result := recorder.Result() +	defer result.Body.Close() + +	b, err := ioutil.ReadAll(result.Body) +	suite.NoError(err) + +	suite.Equal(`{"error":"invalid_request","error_description":"Bad Request: code was not set in the token request form, but must be set since grant_type is authorization_code"}`, string(b)) +} + +func (suite *TokenTestSuite) TestRetrieveAuthorizationCodeWrongGrantType() { +	testClient := suite.testClients["local_account_1"] + +	requestBody, w, err := testrig.CreateMultipartFormData( +		"", "", +		map[string]string{ +			"grant_type":    "client_credentials", +			"client_id":     testClient.ID, +			"client_secret": testClient.Secret, +			"redirect_uri":  "http://localhost:8080", +			"code":          "peepeepoopoo", +		}) +	if err != nil { +		panic(err) +	} +	bodyBytes := requestBody.Bytes() + +	ctx, recorder := suite.newContext(http.MethodPost, "oauth/token", bodyBytes, w.FormDataContentType()) +	ctx.Request.Header.Set("accept", "application/json") + +	suite.authModule.TokenPOSTHandler(ctx) + +	suite.Equal(http.StatusBadRequest, recorder.Code) + +	result := recorder.Result() +	defer result.Body.Close() + +	b, err := ioutil.ReadAll(result.Body) +	suite.NoError(err) + +	suite.Equal(`{"error":"invalid_request","error_description":"Bad Request: a code was provided in the token request form, but grant_type was not set to authorization_code"}`, string(b)) +} + +func TestTokenTestSuite(t *testing.T) { +	suite.Run(t, &TokenTestSuite{}) +} diff --git a/internal/api/errorhandling.go b/internal/api/errorhandling.go index 57659f83c..59b58bcc3 100644 --- a/internal/api/errorhandling.go +++ b/internal/api/errorhandling.go @@ -125,3 +125,30 @@ func ErrorHandler(c *gin.Context, errWithCode gtserror.WithCode, instanceGet fun  		genericErrorHandler(c, instanceGet, accept, errWithCode)  	}  } + +// OAuthErrorHandler is a lot like ErrorHandler, but it specifically returns errors +// that are compatible with https://datatracker.ietf.org/doc/html/rfc6749#section-5.2, +// but serializing errWithCode.Error() in the 'error' field, and putting any help text +// from the error in the 'error_description' field. This means you should be careful not +// to pass any detailed errors (that might contain sensitive information) into the +// errWithCode.Error() field, since the client will see this. Use your noggin! +func OAuthErrorHandler(c *gin.Context, errWithCode gtserror.WithCode) { +	l := logrus.WithFields(logrus.Fields{ +		"path":  c.Request.URL.Path, +		"error": errWithCode.Error(), +		"help":  errWithCode.Safe(), +	}) + +	statusCode := errWithCode.Code() + +	if statusCode == http.StatusInternalServerError { +		l.Error("Internal Server Error") +	} else { +		l.Debug("handling OAuth error") +	} + +	c.JSON(statusCode, gin.H{ +		"error":             errWithCode.Error(), +		"error_description": errWithCode.Safe(), +	}) +} diff --git a/internal/oauth/errors.go b/internal/oauth/errors.go new file mode 100644 index 000000000..25278bdc9 --- /dev/null +++ b/internal/oauth/errors.go @@ -0,0 +1,26 @@ +/* +   GoToSocial +   Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org + +   This program is free software: you can redistribute it and/or modify +   it under the terms of the GNU Affero General Public License as published by +   the Free Software Foundation, either version 3 of the License, or +   (at your option) any later version. + +   This program is distributed in the hope that it will be useful, +   but WITHOUT ANY WARRANTY; without even the implied warranty of +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +   GNU Affero General Public License for more details. + +   You should have received a copy of the GNU Affero General Public License +   along with this program.  If not, see <http://www.gnu.org/licenses/>. +*/ + +package oauth + +import "github.com/superseriousbusiness/oauth2/v4/errors" + +// InvalidRequest returns an oauth spec compliant 'invalid_request' error. +func InvalidRequest() error { +	return errors.New("invalid_request") +} diff --git a/internal/oauth/server.go b/internal/oauth/server.go index bfe615832..4dcc41ceb 100644 --- a/internal/oauth/server.go +++ b/internal/oauth/server.go @@ -25,6 +25,7 @@ import (  	"github.com/sirupsen/logrus"  	"github.com/superseriousbusiness/gotosocial/internal/db" +	"github.com/superseriousbusiness/gotosocial/internal/gtserror"  	"github.com/superseriousbusiness/oauth2/v4"  	"github.com/superseriousbusiness/oauth2/v4/errors"  	"github.com/superseriousbusiness/oauth2/v4/manage" @@ -52,7 +53,7 @@ const (  // Server wraps some oauth2 server functions in an interface, exposing only what is needed  type Server interface { -	HandleTokenRequest(w http.ResponseWriter, r *http.Request) error +	HandleTokenRequest(r *http.Request) (map[string]interface{}, gtserror.WithCode)  	HandleAuthorizeRequest(w http.ResponseWriter, r *http.Request) error  	ValidationBearerToken(r *http.Request) (oauth2.TokenInfo, error)  	GenerateUserAccessToken(ctx context.Context, ti oauth2.TokenInfo, clientSecret string, userID string) (accessToken oauth2.TokenInfo, err error) @@ -116,8 +117,25 @@ func New(ctx context.Context, database db.Basic) Server {  }  // HandleTokenRequest wraps the oauth2 library's HandleTokenRequest function -func (s *s) HandleTokenRequest(w http.ResponseWriter, r *http.Request) error { -	return s.server.HandleTokenRequest(w, r) +func (s *s) HandleTokenRequest(r *http.Request) (map[string]interface{}, gtserror.WithCode) { +	ctx := r.Context() + +	gt, tgr, err := s.server.ValidationTokenRequest(r) +	if err != nil { +		help := fmt.Sprintf("could not validate token request: %s", err) +		return nil, gtserror.NewErrorBadRequest(err, help) +	} + +	ti, err := s.server.GetAccessToken(ctx, gt, tgr) +	if err != nil { +		help := fmt.Sprintf("could not get access token: %s", err) +		return nil, gtserror.NewErrorBadRequest(err, help) +	} + +	data := s.server.GetTokenData(ti) +	data["created_at"] = ti.GetAccessCreateAt().Unix() + +	return data, nil  }  // HandleAuthorizeRequest wraps the oauth2 library's HandleAuthorizeRequest function diff --git a/internal/processing/oauth.go b/internal/processing/oauth.go new file mode 100644 index 000000000..9c974f76e --- /dev/null +++ b/internal/processing/oauth.go @@ -0,0 +1,35 @@ +/* +   GoToSocial +   Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org + +   This program is free software: you can redistribute it and/or modify +   it under the terms of the GNU Affero General Public License as published by +   the Free Software Foundation, either version 3 of the License, or +   (at your option) any later version. + +   This program is distributed in the hope that it will be useful, +   but WITHOUT ANY WARRANTY; without even the implied warranty of +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +   GNU Affero General Public License for more details. + +   You should have received a copy of the GNU Affero General Public License +   along with this program.  If not, see <http://www.gnu.org/licenses/>. +*/ + +package processing + +import ( +	"net/http" + +	"github.com/superseriousbusiness/gotosocial/internal/gtserror" +) + +func (p *processor) OAuthHandleAuthorizeRequest(w http.ResponseWriter, r *http.Request) error { +	// todo: some kind of metrics stuff here +	return p.oauthServer.HandleAuthorizeRequest(w, r) +} + +func (p *processor) OAuthHandleTokenRequest(r *http.Request) (map[string]interface{}, gtserror.WithCode) { +	// todo: some kind of metrics stuff here +	return p.oauthServer.HandleTokenRequest(r) +} diff --git a/internal/processing/processor.go b/internal/processing/processor.go index a7a1c22e0..3afc25196 100644 --- a/internal/processing/processor.go +++ b/internal/processing/processor.go @@ -152,6 +152,9 @@ type Processor interface {  	// NotificationsGet  	NotificationsGet(ctx context.Context, authed *oauth.Auth, limit int, maxID string, sinceID string) (*apimodel.TimelineResponse, gtserror.WithCode) +	OAuthHandleTokenRequest(r *http.Request) (map[string]interface{}, gtserror.WithCode) +	OAuthHandleAuthorizeRequest(w http.ResponseWriter, r *http.Request) error +  	// SearchGet performs a search with the given params, resolving/dereferencing remotely as desired  	SearchGet(ctx context.Context, authed *oauth.Auth, searchQuery *apimodel.SearchQuery) (*apimodel.SearchResult, gtserror.WithCode) diff --git a/testrig/testmodels.go b/testrig/testmodels.go index ee601e053..b74ada751 100644 --- a/testrig/testmodels.go +++ b/testrig/testmodels.go @@ -56,6 +56,23 @@ func NewTestTokens() map[string]*gtsmodel.Token {  			AccessCreateAt:  time.Now(),  			AccessExpiresAt: time.Now().Add(72 * time.Hour),  		}, +		"local_account_1_client_application_token": { +			ID:              "01P9SVWS9J3SPHZQ3KCMBEN70N", +			ClientID:        "01F8MGV8AC3NGSJW0FE8W1BV70", +			RedirectURI:     "http://localhost:8080", +			Access:          "ZTK1MWMWZDGTMGMXOS0ZY2UXLWI5ZWETMWEZYZZIYTLHMZI4", +			AccessCreateAt:  TimeMustParse("2022-06-10T15:22:08Z"), +			AccessExpiresAt: TimeMustParse("2050-01-01T15:22:08Z"), +		}, +		"local_account_1_user_authorization_token": { +			ID:            "01G574M2VTV66YZBC9AZ7HY3FV", +			ClientID:      "01F8MGV8AC3NGSJW0FE8W1BV70", +			UserID:        "01F8MGVGPHQ2D3P3X0454H54Z5", +			RedirectURI:   "http://localhost:8080", +			Code:          "ZJYYMZQ0MTQTZTU1NC0ZNJK4LWE2ZWITYTM1MDHHOTAXNJHL", +			CodeCreateAt:  TimeMustParse("2022-06-10T15:22:08Z"), +			CodeExpiresAt: TimeMustParse("2050-01-01T15:22:08Z"), +		},  		"local_account_2": {  			ID:              "01F8MGVVM1EDVYET710J27XY5R",  			ClientID:        "01F8MGW47HN8ZXNHNZ7E47CDMQ", | 
