token.go (3929B)
1 // GoToSocial 2 // Copyright (C) GoToSocial Authors admin@gotosocial.org 3 // SPDX-License-Identifier: AGPL-3.0-or-later 4 // 5 // This program is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Affero General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Affero General Public License for more details. 14 // 15 // You should have received a copy of the GNU Affero General Public License 16 // along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 package auth 19 20 import ( 21 "net/http" 22 "net/url" 23 24 apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util" 25 "github.com/superseriousbusiness/gotosocial/internal/gtserror" 26 "github.com/superseriousbusiness/gotosocial/internal/oauth" 27 28 "github.com/gin-gonic/gin" 29 ) 30 31 type tokenRequestForm struct { 32 GrantType *string `form:"grant_type" json:"grant_type" xml:"grant_type"` 33 Code *string `form:"code" json:"code" xml:"code"` 34 RedirectURI *string `form:"redirect_uri" json:"redirect_uri" xml:"redirect_uri"` 35 ClientID *string `form:"client_id" json:"client_id" xml:"client_id"` 36 ClientSecret *string `form:"client_secret" json:"client_secret" xml:"client_secret"` 37 Scope *string `form:"scope" json:"scope" xml:"scope"` 38 } 39 40 // TokenPOSTHandler should be served as a POST at https://example.org/oauth/token 41 // The idea here is to serve an oauth access token to a user, which can be used for authorizing against non-public APIs. 42 func (m *Module) TokenPOSTHandler(c *gin.Context) { 43 if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil { 44 apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1) 45 return 46 } 47 48 help := []string{} 49 50 form := &tokenRequestForm{} 51 if err := c.ShouldBind(form); err != nil { 52 apiutil.OAuthErrorHandler(c, gtserror.NewErrorBadRequest(oauth.InvalidRequest(), err.Error())) 53 return 54 } 55 56 c.Request.Form = url.Values{} 57 58 var grantType string 59 if form.GrantType != nil { 60 grantType = *form.GrantType 61 c.Request.Form.Set("grant_type", grantType) 62 } else { 63 help = append(help, "grant_type was not set in the token request form, but must be set to authorization_code or client_credentials") 64 } 65 66 if form.ClientID != nil { 67 c.Request.Form.Set("client_id", *form.ClientID) 68 } else { 69 help = append(help, "client_id was not set in the token request form") 70 } 71 72 if form.ClientSecret != nil { 73 c.Request.Form.Set("client_secret", *form.ClientSecret) 74 } else { 75 help = append(help, "client_secret was not set in the token request form") 76 } 77 78 if form.RedirectURI != nil { 79 c.Request.Form.Set("redirect_uri", *form.RedirectURI) 80 } else { 81 help = append(help, "redirect_uri was not set in the token request form") 82 } 83 84 var code string 85 if form.Code != nil { 86 if grantType != "authorization_code" { 87 help = append(help, "a code was provided in the token request form, but grant_type was not set to authorization_code") 88 } else { 89 code = *form.Code 90 c.Request.Form.Set("code", code) 91 } 92 } else if grantType == "authorization_code" { 93 help = append(help, "code was not set in the token request form, but must be set since grant_type is authorization_code") 94 } 95 96 if form.Scope != nil { 97 c.Request.Form.Set("scope", *form.Scope) 98 } 99 100 if len(help) != 0 { 101 apiutil.OAuthErrorHandler(c, gtserror.NewErrorBadRequest(oauth.InvalidRequest(), help...)) 102 return 103 } 104 105 token, errWithCode := m.processor.OAuthHandleTokenRequest(c.Request) 106 if errWithCode != nil { 107 apiutil.OAuthErrorHandler(c, errWithCode) 108 return 109 } 110 111 c.Header("Cache-Control", "no-store") 112 c.Header("Pragma", "no-cache") 113 c.JSON(http.StatusOK, token) 114 }