diff --git a/internal/api/errorcodes.go b/internal/api/errorcodes.go index 2c85efdec..334a28be3 100644 --- a/internal/api/errorcodes.go +++ b/internal/api/errorcodes.go @@ -18,6 +18,9 @@ const ( ErrorCodeNoAuthorization ErrorCode = "no_authorization" ErrorCodeUserNotFound ErrorCode = "user_not_found" ErrorCodeSessionNotFound ErrorCode = "session_not_found" + ErrorCodeSessionExpired ErrorCode = "session_expired" + ErrorCodeRefreshTokenNotFound ErrorCode = "refresh_token_not_found" + ErrorCodeRefreshTokenAlreadyUsed ErrorCode = "refresh_token_already_used" ErrorCodeFlowStateNotFound ErrorCode = "flow_state_not_found" ErrorCodeFlowStateExpired ErrorCode = "flow_state_expired" ErrorCodeSignupDisabled ErrorCode = "signup_disabled" @@ -71,7 +74,7 @@ const ( ErrorCodeOverRequestRateLimit ErrorCode = "over_request_rate_limit" ErrorCodeOverEmailSendRateLimit ErrorCode = "over_email_send_rate_limit" ErrorCodeOverSMSSendRateLimit ErrorCode = "over_sms_send_rate_limit" - ErrorBadCodeVerifier ErrorCode = "bad_code_verifier" + ErrorCodeBadCodeVerifier ErrorCode = "bad_code_verifier" ErrorCodeAnonymousProviderDisabled ErrorCode = "anonymous_provider_disabled" ErrorCodeHookTimeout ErrorCode = "hook_timeout" ErrorCodeHookTimeoutAfterRetry ErrorCode = "hook_timeout_after_retry" diff --git a/internal/api/token.go b/internal/api/token.go index d8790e679..cc945f2e1 100644 --- a/internal/api/token.go +++ b/internal/api/token.go @@ -269,7 +269,7 @@ func (a *API) PKCE(ctx context.Context, w http.ResponseWriter, r *http.Request) return err } if err := flowState.VerifyPKCE(params.CodeVerifier); err != nil { - return badRequestError(ErrorBadCodeVerifier, err.Error()) + return badRequestError(ErrorCodeBadCodeVerifier, err.Error()) } var token *AccessTokenResponse diff --git a/internal/api/token_refresh.go b/internal/api/token_refresh.go index 9a537c68f..7eae233aa 100644 --- a/internal/api/token_refresh.go +++ b/internal/api/token_refresh.go @@ -47,13 +47,13 @@ func (a *API) RefreshTokenGrant(ctx context.Context, w http.ResponseWriter, r *h user, token, session, err := models.FindUserWithRefreshToken(db, params.RefreshToken, false) if err != nil { if models.IsNotFoundError(err) { - return oauthError("invalid_grant", "Invalid Refresh Token: Refresh Token Not Found") + return badRequestError(ErrorCodeRefreshTokenNotFound, "Invalid Refresh Token: Refresh Token Not Found") } return internalServerError(err.Error()) } if user.IsBanned() { - return oauthError("invalid_grant", "Invalid Refresh Token: User Banned") + return badRequestError(ErrorCodeUserBanned, "Invalid Refresh Token: User Banned") } if session == nil { @@ -71,10 +71,10 @@ func (a *API) RefreshTokenGrant(ctx context.Context, w http.ResponseWriter, r *h // do nothing case models.SessionTimedOut: - return oauthError("invalid_grant", "Invalid Refresh Token: Session Expired (Inactivity)") + return badRequestError(ErrorCodeSessionExpired, "Invalid Refresh Token: Session Expired (Inactivity)") default: - return oauthError("invalid_grant", "Invalid Refresh Token: Session Expired") + return badRequestError(ErrorCodeSessionExpired, "Invalid Refresh Token: Session Expired") } // Basic checks above passed, now we need to serialize access @@ -153,7 +153,7 @@ func (a *API) RefreshTokenGrant(ctx context.Context, w http.ResponseWriter, r *h if s.LastRefreshedAt(nil).After(session.LastRefreshedAt(&token.UpdatedAt)) { // session is not the most // recently active one - return oauthError("invalid_grant", "Invalid Refresh Token: Session Expired (Revoked by Newer Login)") + return badRequestError(ErrorCodeSessionExpired, "Invalid Refresh Token: Session Expired (Revoked by Newer Login)") } } @@ -196,7 +196,7 @@ func (a *API) RefreshTokenGrant(ctx context.Context, w http.ResponseWriter, r *h } } - return storage.NewCommitWithError(oauthError("invalid_grant", "Invalid Refresh Token: Already Used").WithInternalMessage("Possible abuse attempt: %v", token.ID)) + return storage.NewCommitWithError(badRequestError(ErrorCodeRefreshTokenAlreadyUsed, "Invalid Refresh Token: Already Used").WithInternalMessage("Possible abuse attempt: %v", token.ID)) } } } diff --git a/internal/api/token_test.go b/internal/api/token_test.go index 53a492b11..fc89d4f8b 100644 --- a/internal/api/token_test.go +++ b/internal/api/token_test.go @@ -89,13 +89,13 @@ func (ts *TokenTestSuite) TestSessionTimebox() { assert.Equal(ts.T(), http.StatusBadRequest, w.Code) var firstResult struct { - Error string `json:"error"` - ErrorDescription string `json:"error_description"` + ErrorCode string `json:"error_code"` + Message string `json:"msg"` } assert.NoError(ts.T(), json.NewDecoder(w.Result().Body).Decode(&firstResult)) - assert.Equal(ts.T(), "invalid_grant", firstResult.Error) - assert.Equal(ts.T(), "Invalid Refresh Token: Session Expired", firstResult.ErrorDescription) + assert.Equal(ts.T(), ErrorCodeSessionExpired, firstResult.ErrorCode) + assert.Equal(ts.T(), "Invalid Refresh Token: Session Expired", firstResult.Message) } func (ts *TokenTestSuite) TestSessionInactivityTimeout() { @@ -124,13 +124,13 @@ func (ts *TokenTestSuite) TestSessionInactivityTimeout() { assert.Equal(ts.T(), http.StatusBadRequest, w.Code) var firstResult struct { - Error string `json:"error"` - ErrorDescription string `json:"error_description"` + ErrorCode string `json:"error_code"` + Message string `json:"msg"` } assert.NoError(ts.T(), json.NewDecoder(w.Result().Body).Decode(&firstResult)) - assert.Equal(ts.T(), "invalid_grant", firstResult.Error) - assert.Equal(ts.T(), "Invalid Refresh Token: Session Expired (Inactivity)", firstResult.ErrorDescription) + assert.Equal(ts.T(), ErrorCodeSessionExpired, firstResult.ErrorCode) + assert.Equal(ts.T(), "Invalid Refresh Token: Session Expired (Inactivity)", firstResult.Message) } func (ts *TokenTestSuite) TestFailedToSaveRefreshTokenResultCase() { @@ -213,13 +213,13 @@ func (ts *TokenTestSuite) TestSingleSessionPerUserNoTags() { assert.True(ts.T(), ts.API.config.Sessions.SinglePerUser) var firstResult struct { - Error string `json:"error"` - ErrorDescription string `json:"error_description"` + ErrorCode string `json:"error_code"` + Message string `json:"msg"` } assert.NoError(ts.T(), json.NewDecoder(w.Result().Body).Decode(&firstResult)) - assert.Equal(ts.T(), "invalid_grant", firstResult.Error) - assert.Equal(ts.T(), "Invalid Refresh Token: Session Expired (Revoked by Newer Login)", firstResult.ErrorDescription) + assert.Equal(ts.T(), ErrorCodeSessionExpired, firstResult.ErrorCode) + assert.Equal(ts.T(), "Invalid Refresh Token: Session Expired (Revoked by Newer Login)", firstResult.Message) } func (ts *TokenTestSuite) TestRateLimitTokenRefresh() { @@ -428,13 +428,13 @@ func (ts *TokenTestSuite) TestRefreshTokenReuseRevocation() { assert.Equal(ts.T(), http.StatusBadRequest, w.Code) var response struct { - Error string `json:"error"` - ErrorDescription string `json:"error_description"` + ErrorCode string `json:"error_code"` + Message string `json:"msg"` } require.NoError(ts.T(), json.NewDecoder(w.Body).Decode(&response)) - require.Equal(ts.T(), response.Error, "invalid_grant") - require.Equal(ts.T(), response.ErrorDescription, "Invalid Refresh Token: Already Used") + require.Equal(ts.T(), ErrorCodeRefreshTokenAlreadyUsed, response.ErrorCode) + require.Equal(ts.T(), "Invalid Refresh Token: Already Used", response.Message) // ensure that the refresh tokens are marked as revoked in the database for _, refreshToken := range refreshTokens { @@ -461,13 +461,13 @@ func (ts *TokenTestSuite) TestRefreshTokenReuseRevocation() { assert.Equal(ts.T(), http.StatusBadRequest, w.Code, "For refresh token %d", i) var response struct { - Error string `json:"error"` - ErrorDescription string `json:"error_description"` + ErrorCode string `json:"error_code"` + Message string `json:"msg"` } require.NoError(ts.T(), json.NewDecoder(w.Body).Decode(&response)) - require.Equal(ts.T(), response.Error, "invalid_grant", "For refresh token %d", i) - require.Equal(ts.T(), response.ErrorDescription, "Invalid Refresh Token: Already Used", "For refresh token %d", i) + require.Equal(ts.T(), ErrorCodeRefreshTokenAlreadyUsed, response.ErrorCode, "For refresh token %d", i) + require.Equal(ts.T(), "Invalid Refresh Token: Already Used", response.Message, "For refresh token %d", i) } }