From bc7ff907077b523f33b6bf8cb406d55d7c873628 Mon Sep 17 00:00:00 2001 From: Kevin Gimbel Date: Fri, 14 Nov 2025 10:52:13 +0100 Subject: [feature] Implement stub `authorize_interaction` route (#4557) Updates #4548 # Description This PR implements a route for the `/authorize_interaction` endpoint. This endpoint is used when an interaction request comes from a remote Mastodon instance, e.g. when a user clicks on the reply button and enters their GoToSocial server address into the field. closes #4548 ## Checklist - [x] I/we have read the [GoToSocial contribution guidelines](https://codeberg.org/superseriousbusiness/gotosocial/src/branch/main/CONTRIBUTING.md). - [x] I/we have discussed the proposed changes already, either in an issue on the repository, or in the Matrix chat. - [x] I/we have not leveraged AI to create the proposed changes. - [x] I/we have performed a self-review of added code. - [x] I/we have written code that is legible and maintainable by others. - [x] I/we have commented the added code, particularly in hard-to-understand areas. - [x] I/we have made any necessary changes to documentation. - [ ] I/we have added tests that cover new code. - [ ] I/we have run tests and they pass locally with the changes. - [ ] I/we have run `go fmt ./...` and `golangci-lint run`. Co-authored-by: tobi Reviewed-on: https://codeberg.org/superseriousbusiness/gotosocial/pulls/4557 Co-authored-by: Kevin Gimbel Co-committed-by: Kevin Gimbel --- internal/web/authorize-interaction.go | 91 +++++++++++++++++++++++++++++++++ internal/web/web.go | 32 ++++++------ web/template/authorize-interaction.tmpl | 30 +++++++++++ 3 files changed, 138 insertions(+), 15 deletions(-) create mode 100644 internal/web/authorize-interaction.go create mode 100644 web/template/authorize-interaction.tmpl diff --git a/internal/web/authorize-interaction.go b/internal/web/authorize-interaction.go new file mode 100644 index 000000000..46dc00d1d --- /dev/null +++ b/internal/web/authorize-interaction.go @@ -0,0 +1,91 @@ +// GoToSocial +// Copyright (C) GoToSocial Authors admin@gotosocial.org +// SPDX-License-Identifier: AGPL-3.0-or-later +// +// 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 . + +package web + +import ( + "context" + "net/http" + "net/url" + + apimodel "code.superseriousbusiness.org/gotosocial/internal/api/model" + apiutil "code.superseriousbusiness.org/gotosocial/internal/api/util" + "code.superseriousbusiness.org/gotosocial/internal/gtserror" + "github.com/gin-gonic/gin" +) + +// authorizeInteractionGETHandler handles redirects from remote +// (usually Mastodon) instances when a user tries to do a +// "remote interaction" and gives their GoToSocial account/domain. +// We use this handler instead of serving a generic 404 page. +func (m *Module) authorizeInteractionGETHandler(c *gin.Context) { + instance, errWithCode := m.processor.InstanceGetV1(c.Request.Context()) + if errWithCode != nil { + apiutil.WebErrorHandler(c, errWithCode, m.processor.InstanceGetV1) + return + } + + // Return instance we already got from the db, + // don't try to fetch it again when erroring. + instanceGet := func(ctx context.Context) (*apimodel.InstanceV1, gtserror.WithCode) { + return instance, nil + } + + // We only serve text/html at this endpoint. + if _, err := apiutil.NegotiateAccept(c, apiutil.TextHTML); err != nil { + apiutil.WebErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), instanceGet) + return + } + + // Redirects to the "authorize_interaction" + // endpoint should contain the URI of the + // object that the user is trying to interact + // with in the 'uri' query param. + uriStr := c.Query("uri") + if uriStr == "" { + const text = "no uri query parameter found in string" + errWithCode := gtserror.NewWithCode(http.StatusNotFound, text) + apiutil.WebErrorHandler(c, errWithCode, instanceGet) + } + + // Try to parse the object URI. + interactionURI, err := url.Parse(uriStr) + if err != nil { + err := gtserror.Newf("interaction URI could not be parsed: %w", err) + errWithCode := gtserror.NewErrorBadRequest(err, err.Error()) + apiutil.WebErrorHandler(c, errWithCode, instanceGet) + } + + page := apiutil.WebPage{ + Template: "authorize-interaction.tmpl", + Instance: instance, + OGMeta: apiutil.OGBase(instance), + Stylesheets: []string{cssAbout}, + Javascript: []apiutil.JavascriptEntry{ + { + Src: jsFrontend, + Async: true, + Defer: true, + }, + }, + Extra: map[string]any{ + "interactionURI": interactionURI, + }, + } + + apiutil.TemplateWebPage(c, page) +} diff --git a/internal/web/web.go b/internal/web/web.go index 3468ef63b..72b640fb3 100644 --- a/internal/web/web.go +++ b/internal/web/web.go @@ -33,21 +33,22 @@ import ( ) const ( - confirmEmailPath = "/" + uris.ConfirmEmailPath - profileGroupPath = "/@:username" - statusPath = "/statuses/:" + apiutil.WebStatusIDKey // leave out the '/@:username' prefix as this will be served within the profile group - tagsPath = "/tags/:" + apiutil.TagNameKey - customCSSPath = profileGroupPath + "/custom.css" - instanceCustomCSSPath = "/custom.css" - rssFeedPath = profileGroupPath + "/feed.rss" - assetsPathPrefix = "/assets" - distPathPrefix = assetsPathPrefix + "/dist" - themesPathPrefix = assetsPathPrefix + "/themes" - settingsPathPrefix = "/settings" - settingsPanelGlob = settingsPathPrefix + "/*panel" - userPanelPath = settingsPathPrefix + "/user" - adminPanelPath = settingsPathPrefix + "/admin" - signupPath = "/signup" + confirmEmailPath = "/" + uris.ConfirmEmailPath + profileGroupPath = "/@:username" + statusPath = "/statuses/:" + apiutil.WebStatusIDKey // leave out the '/@:username' prefix as this will be served within the profile group + tagsPath = "/tags/:" + apiutil.TagNameKey + customCSSPath = profileGroupPath + "/custom.css" + instanceCustomCSSPath = "/custom.css" + rssFeedPath = profileGroupPath + "/feed.rss" + assetsPathPrefix = "/assets" + distPathPrefix = assetsPathPrefix + "/dist" + themesPathPrefix = assetsPathPrefix + "/themes" + settingsPathPrefix = "/settings" + settingsPanelGlob = settingsPathPrefix + "/*panel" + userPanelPath = settingsPathPrefix + "/user" + adminPanelPath = settingsPathPrefix + "/admin" + signupPath = "/signup" + authorizeInteractionPath = "/authorize_interaction" cacheControlHeader = "Cache-Control" // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control cacheControlNoCache = "no-cache" // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control#response_directives @@ -127,6 +128,7 @@ func (m *Module) Route(r *router.Router, mi ...gin.HandlerFunc) { everythingElseGroup.Handle(http.MethodGet, domainAllowlistPath, m.domainAllowlistGETHandler) everythingElseGroup.Handle(http.MethodGet, tagsPath, m.tagGETHandler) everythingElseGroup.Handle(http.MethodGet, signupPath, m.signupGETHandler) + everythingElseGroup.Handle(http.MethodGet, authorizeInteractionPath, m.authorizeInteractionGETHandler) everythingElseGroup.Handle(http.MethodPost, signupPath, m.signupPOSTHandler) // Redirects from old endpoints for back compat. diff --git a/web/template/authorize-interaction.tmpl b/web/template/authorize-interaction.tmpl new file mode 100644 index 000000000..e2ab397ed --- /dev/null +++ b/web/template/authorize-interaction.tmpl @@ -0,0 +1,30 @@ +{{- /* +// GoToSocial +// Copyright (C) GoToSocial Authors admin@gotosocial.org +// SPDX-License-Identifier: AGPL-3.0-or-later +// +// 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 . +*/ -}} + +{{- with . }} +
+
+

Authorize Remote Interaction

+

It looks like you're trying to interact with a post or account from a different instance.

+

Because GoToSocial does not come with a web client built in, this is not supported.

+

Instead, you can interact with the post or account by copying the URL shown below, and pasting it into the search bar of whatever app you normally use for your GoToSocial account:

+
{{- .interactionURI -}}
+
+
+{{- end }} \ No newline at end of file -- cgit v1.3