mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2024-11-24 17:30:59 +00:00
[feature] add 'state' oauth2 param to /oauth/authorize (#730)
This commit is contained in:
parent
7ca5bac7c6
commit
8106b69856
6 changed files with 35 additions and 20 deletions
|
@ -55,13 +55,14 @@ const (
|
|||
callbackStateParam = "state"
|
||||
callbackCodeParam = "code"
|
||||
|
||||
sessionUserID = "userid"
|
||||
sessionClientID = "client_id"
|
||||
sessionRedirectURI = "redirect_uri"
|
||||
sessionForceLogin = "force_login"
|
||||
sessionResponseType = "response_type"
|
||||
sessionScope = "scope"
|
||||
sessionState = "state"
|
||||
sessionUserID = "userid"
|
||||
sessionClientID = "client_id"
|
||||
sessionRedirectURI = "redirect_uri"
|
||||
sessionForceLogin = "force_login"
|
||||
sessionResponseType = "response_type"
|
||||
sessionScope = "scope"
|
||||
sessionInternalState = "internal_state"
|
||||
sessionClientState = "client_state"
|
||||
)
|
||||
|
||||
// Module implements the ClientAPIModule interface for
|
||||
|
|
|
@ -189,6 +189,11 @@ func (m *Module) AuthorizePOSTHandler(c *gin.Context) {
|
|||
errs = append(errs, fmt.Sprintf("key %s was not found in session", sessionScope))
|
||||
}
|
||||
|
||||
var clientState string
|
||||
if s, ok := s.Get(sessionClientState).(string); ok {
|
||||
clientState = s
|
||||
}
|
||||
|
||||
userID, ok := s.Get(sessionUserID).(string)
|
||||
if !ok {
|
||||
errs = append(errs, fmt.Sprintf("key %s was not found in session", sessionUserID))
|
||||
|
@ -246,6 +251,10 @@ func (m *Module) AuthorizePOSTHandler(c *gin.Context) {
|
|||
sessionUserID: {userID},
|
||||
}
|
||||
|
||||
if clientState != "" {
|
||||
c.Request.Form.Set("state", clientState)
|
||||
}
|
||||
|
||||
if err := m.processor.OAuthHandleAuthorizeRequest(c.Writer, c.Request); err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error(), helpfulAdvice), m.processor.InstanceGet)
|
||||
}
|
||||
|
@ -285,7 +294,8 @@ func saveAuthFormToSession(s sessions.Session, form *model.OAuthAuthorize) gtser
|
|||
s.Set(sessionClientID, form.ClientID)
|
||||
s.Set(sessionRedirectURI, form.RedirectURI)
|
||||
s.Set(sessionScope, form.Scope)
|
||||
s.Set(sessionState, uuid.NewString())
|
||||
s.Set(sessionInternalState, uuid.NewString())
|
||||
s.Set(sessionClientState, form.State)
|
||||
|
||||
if err := s.Save(); err != nil {
|
||||
err := fmt.Errorf("error saving form values onto session: %s", err)
|
||||
|
|
|
@ -45,26 +45,26 @@ func (m *Module) CallbackGETHandler(c *gin.Context) {
|
|||
// check the query vs session state parameter to mitigate csrf
|
||||
// https://auth0.com/docs/secure/attack-protection/state-parameters
|
||||
|
||||
state := c.Query(callbackStateParam)
|
||||
if state == "" {
|
||||
returnedInternalState := c.Query(callbackStateParam)
|
||||
if returnedInternalState == "" {
|
||||
m.clearSession(s)
|
||||
err := fmt.Errorf("%s parameter not found on callback query", callbackStateParam)
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
savedStateI := s.Get(sessionState)
|
||||
savedState, ok := savedStateI.(string)
|
||||
savedInternalStateI := s.Get(sessionInternalState)
|
||||
savedInternalState, ok := savedInternalStateI.(string)
|
||||
if !ok {
|
||||
m.clearSession(s)
|
||||
err := fmt.Errorf("key %s was not found in session", sessionState)
|
||||
err := fmt.Errorf("key %s was not found in session", sessionInternalState)
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if state != savedState {
|
||||
if returnedInternalState != savedInternalState {
|
||||
m.clearSession(s)
|
||||
err := errors.New("mismatch between query state and session state")
|
||||
err := errors.New("mismatch between callback state and saved state")
|
||||
api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -58,16 +58,16 @@ func (m *Module) SignInGETHandler(c *gin.Context) {
|
|||
// idp provider is in use, so redirect to it
|
||||
s := sessions.Default(c)
|
||||
|
||||
stateI := s.Get(sessionState)
|
||||
state, ok := stateI.(string)
|
||||
internalStateI := s.Get(sessionInternalState)
|
||||
internalState, ok := internalStateI.(string)
|
||||
if !ok {
|
||||
m.clearSession(s)
|
||||
err := fmt.Errorf("key %s was not found in session", sessionState)
|
||||
err := fmt.Errorf("key %s was not found in session", sessionInternalState)
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
c.Redirect(http.StatusSeeOther, m.idp.AuthCodeURL(state))
|
||||
c.Redirect(http.StatusSeeOther, m.idp.AuthCodeURL(internalState))
|
||||
}
|
||||
|
||||
// SignInPOSTHandler should be served at https://example.org/auth/sign_in.
|
||||
|
|
|
@ -312,7 +312,7 @@ func (suite *StatusCreateTestSuite) TestAttachNewMediaSuccess() {
|
|||
ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", status.BasePath), nil) // the endpoint we're hitting
|
||||
ctx.Request.Header.Set("accept", "application/json")
|
||||
ctx.Request.Form = url.Values{
|
||||
"status": {"here's an image attachment"},
|
||||
"status": {"here's an image attachment"},
|
||||
"media_ids[]": {attachment.ID},
|
||||
}
|
||||
suite.statusModule.StatusCreatePOSTHandler(ctx)
|
||||
|
|
|
@ -33,4 +33,8 @@ type OAuthAuthorize struct {
|
|||
// List of requested OAuth scopes, separated by spaces (or by pluses, if using query parameters).
|
||||
// Must be a subset of scopes declared during app registration. If not provided, defaults to read.
|
||||
Scope string `form:"scope" json:"scope"`
|
||||
// State is used by the application to store request-specific data and/or prevent CSRF attacks.
|
||||
// The authorization server must return the unmodified state value back to the application.
|
||||
// See https://www.oauth.com/oauth2-servers/authorization/the-authorization-request/
|
||||
State string `form:"state" json:"state"`
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue