Skip to content

Commit

Permalink
Add error explanation and metrics. Increase state lifetiem to 6 hours
Browse files Browse the repository at this point in the history
  • Loading branch information
berejant committed Nov 7, 2023
1 parent d22ebd7 commit e76a2db
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 18 deletions.
16 changes: 13 additions & 3 deletions ApiController.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import (

const adminUserid = 1

var stateLifetime = time.Hour * 6

//go:embed templates/*.html
var templates embed.FS

Expand Down Expand Up @@ -89,7 +91,7 @@ func (controller *ApiController) getAuthUrl(c *gin.Context) {
if err == nil {
authOptionsClaims.KneuUserId = 0
authOptionsClaims.Issuer = "pigeonAuthorizer"
authOptionsClaims.ExpiresAt = jwt.NewNumericDate(time.Now().Add(time.Minute * 15))
authOptionsClaims.ExpiresAt = jwt.NewNumericDate(time.Now().Add(stateLifetime))

state, err = controller.buildState(authOptionsClaims)
}
Expand Down Expand Up @@ -119,16 +121,22 @@ func (controller *ApiController) completeAuth(c *gin.Context) {
}

authOptionsClaims, err := controller.parseState(state)
if err != nil {
if errors.Is(err, jwt.ErrTokenExpired) {
controller.errorResponse(c, "Посилання для авторизації через сайт КНЕУ отримане через бот недійсне. Отримайте нове посилання надіславши боту команду /start")
authErrorExpiredStateTotal.Inc()
return
} else if err != nil {
controller.errorResponse(c, "Некорректне посилання для авторизації через сайт КНЕУ. Отримайте нове посилання надіславши боту команду /start")
fmt.Fprintf(controller.out, "Failed to parse state: %s\n", err.Error())
controller.errorResponse(c, "Невірний стан")
authErrorWrongStateTotal.Inc()
return
}

tokenResponse, err = controller.oauthClient.GetOauthToken(controller.oauthRedirectUrl, code)
if err != nil {
fmt.Fprintf(controller.out, "Failed to get token: %s\n", err.Error())
controller.errorResponse(c, "Помилка отримання токена")
authErrorFailGetTokenTotal.Inc()
return
}

Expand All @@ -144,6 +152,7 @@ func (controller *ApiController) completeAuth(c *gin.Context) {
if err != nil {
fmt.Fprintf(controller.out, "Failed to get user me: %s\n", err.Error())
controller.errorResponse(c, "Помилка отримання даних користувача")
authErrorFailGetUserTotal.Inc()
return
}

Expand All @@ -163,6 +172,7 @@ func (controller *ApiController) completeAuth(c *gin.Context) {
if err != nil {
fmt.Fprintf(controller.out, "Failed to auth user: %s\n", err.Error())
controller.errorResponse(c, "Помилка завершення авторизації")
authErrorFailFinishAuthTotal.Inc()
return
} else {
controller.successRedirect(c, authOptionsClaims)
Expand Down
70 changes: 59 additions & 11 deletions ApiController_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ func TestCompleteAuth(t *testing.T) {
authOptionsClaims := AuthOptionsClaims{
RegisteredClaims: jwt.RegisteredClaims{
Issuer: "pigeonAuthorizer",
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Minute * 15)),
ExpiresAt: jwt.NewNumericDate(time.Now().Add(stateLifetime)),
},
Client: client,
ClientUserId: clientUserId,
Expand Down Expand Up @@ -298,7 +298,7 @@ func TestCompleteAuth(t *testing.T) {
authOptionsClaims := AuthOptionsClaims{
RegisteredClaims: jwt.RegisteredClaims{
Issuer: "pigeonAuthorizer",
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Minute * 15)),
ExpiresAt: jwt.NewNumericDate(time.Now().Add(stateLifetime)),
},
Client: client,
ClientUserId: clientUserId,
Expand Down Expand Up @@ -345,7 +345,7 @@ func TestCompleteAuth(t *testing.T) {
authOptionsClaims := AuthOptionsClaims{
RegisteredClaims: jwt.RegisteredClaims{
Issuer: "pigeonAuthorizer",
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Minute * 15)),
ExpiresAt: jwt.NewNumericDate(time.Now().Add(stateLifetime)),
},
Client: client,
ClientUserId: clientUserId,
Expand Down Expand Up @@ -424,7 +424,7 @@ func TestCompleteAuth(t *testing.T) {
authOptionsClaims := AuthOptionsClaims{
RegisteredClaims: jwt.RegisteredClaims{
Issuer: "pigeonAuthorizer",
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Minute * 15)),
ExpiresAt: jwt.NewNumericDate(time.Now().Add(stateLifetime)),
},
Client: client,
ClientUserId: clientUserId,
Expand All @@ -444,6 +444,54 @@ func TestCompleteAuth(t *testing.T) {
assert.Contains(t, w.Body.String(), "Вам потрібно використати особистий кабінет студента")
})

t.Run("error_expired_state", func(t *testing.T) {
client := "telegram"
clientUserId := "999"
finalRedirectUri := "http://example.com"
code := "qwerty1234"

oauthClient := kneu.NewMockOauthClientInterface(t)
apiClient := kneu.NewMockApiClientInterface(t)

tokenResponse := kneu.OauthTokenResponse{}

out := &bytes.Buffer{}
controller := &ApiController{
out: out,
config: config,
oauthClient: oauthClient,
apiClientFactory: func(token string) kneu.ApiClientInterface {
assert.Equal(t, tokenResponse.AccessToken, token)
return apiClient
},
countCache: NewCountCache(1),
}

router := (controller).setupRouter()

authOptionsClaims := AuthOptionsClaims{
RegisteredClaims: jwt.RegisteredClaims{
Issuer: "pigeonAuthorizer",
ExpiresAt: jwt.NewNumericDate(time.Now().Add(-time.Hour * 6)),
},
Client: client,
ClientUserId: clientUserId,
RedirectUri: finalRedirectUri,
KneuUserId: 0,
}

state, _ := controller.buildState(authOptionsClaims)

w := httptest.NewRecorder()
req, _ := http.NewRequest(http.MethodGet, "/complete?code="+code+"&state="+state, nil)

router.ServeHTTP(w, req)

assert.Equal(t, http.StatusBadRequest, w.Code)
assert.Contains(t, w.Body.String(), "Не вдалося завершити авторизацію")
assert.Contains(t, w.Body.String(), "Посилання для авторизації через сайт КНЕУ отримане через бот недійсне. Отримайте нове посилання надіславши боту команду /start")
})

t.Run("error_wrong_state", func(t *testing.T) {
code := "qwerty1234"

Expand Down Expand Up @@ -475,7 +523,7 @@ func TestCompleteAuth(t *testing.T) {

assert.Equal(t, http.StatusBadRequest, w.Code)
assert.Contains(t, w.Body.String(), "Не вдалося завершити авторизацію")
assert.Contains(t, w.Body.String(), "Невірний стан")
assert.Contains(t, w.Body.String(), "Некорректне посилання для авторизації через сайт КНЕУ")
assert.Contains(t, out.String(), "Failed to parse state")
})

Expand Down Expand Up @@ -511,7 +559,7 @@ func TestCompleteAuth(t *testing.T) {
authOptionsClaims := AuthOptionsClaims{
RegisteredClaims: jwt.RegisteredClaims{
Issuer: "pigeonAuthorizer",
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Minute * 15)),
ExpiresAt: jwt.NewNumericDate(time.Now().Add(stateLifetime)),
},
Client: client,
ClientUserId: clientUserId,
Expand Down Expand Up @@ -572,7 +620,7 @@ func TestCompleteAuth(t *testing.T) {
authOptionsClaims := AuthOptionsClaims{
RegisteredClaims: jwt.RegisteredClaims{
Issuer: "pigeonAuthorizer",
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Minute * 15)),
ExpiresAt: jwt.NewNumericDate(time.Now().Add(stateLifetime)),
},
Client: client,
ClientUserId: clientUserId,
Expand Down Expand Up @@ -667,7 +715,7 @@ func TestCompleteAuth(t *testing.T) {
authOptionsClaims := AuthOptionsClaims{
RegisteredClaims: jwt.RegisteredClaims{
Issuer: "pigeonAuthorizer",
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Minute * 15)),
ExpiresAt: jwt.NewNumericDate(time.Now().Add(stateLifetime)),
},
Client: client,
ClientUserId: clientUserId,
Expand Down Expand Up @@ -716,7 +764,7 @@ func TestCompleteAuth(t *testing.T) {
authOptionsClaims := AuthOptionsClaims{
RegisteredClaims: jwt.RegisteredClaims{
Issuer: "pigeonAuthorizer",
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Minute * 15)),
ExpiresAt: jwt.NewNumericDate(time.Now().Add(stateLifetime)),
},
Client: client,
ClientUserId: clientUserId,
Expand Down Expand Up @@ -789,7 +837,7 @@ func TestCompleteAdminAuth(t *testing.T) {
authOptionsClaims := AuthOptionsClaims{
RegisteredClaims: jwt.RegisteredClaims{
Issuer: "pigeonAuthorizer",
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Minute * 15)),
ExpiresAt: jwt.NewNumericDate(time.Now().Add(stateLifetime)),
},
Client: client,
ClientUserId: clientUserId,
Expand Down Expand Up @@ -824,7 +872,7 @@ func TestCompleteAdminAuth(t *testing.T) {
authOptionsClaims := AuthOptionsClaims{
RegisteredClaims: jwt.RegisteredClaims{
Issuer: "pigeonAuthorizer",
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Minute * 15)),
ExpiresAt: jwt.NewNumericDate(time.Now().Add(stateLifetime)),
},
Client: client,
ClientUserId: clientUserId,
Expand Down
4 changes: 0 additions & 4 deletions app.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package main

import (
"fmt"
"github.com/VictoriaMetrics/metrics"
"github.com/berejant/go-kneu"
"github.com/gin-gonic/gin"
"github.com/kneu-messenger-pigeon/events"
Expand All @@ -15,9 +14,6 @@ import (

const ExitCodeMainError = 1

var getAuthUrlRequestsTotal = metrics.NewCounter("get_auth_url_requests_total")
var completeAuthRequestsTotal = metrics.NewCounter("complete_auth_requests_total")

func runApp(out io.Writer, listenAndServe func(string, http.Handler) error) error {
envFilename := ""
if _, err := os.Stat(".env"); err == nil {
Expand Down
12 changes: 12 additions & 0 deletions metrics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package main

import "github.com/VictoriaMetrics/metrics"

var getAuthUrlRequestsTotal = metrics.NewCounter(`get_auth_url_requests_total`)
var completeAuthRequestsTotal = metrics.NewCounter(`complete_auth_requests_total`)

var authErrorExpiredStateTotal = metrics.NewCounter(`auth_error{error="expired_state"}`)
var authErrorWrongStateTotal = metrics.NewCounter(`auth_error{error="wrong_state"}`)
var authErrorFailGetTokenTotal = metrics.NewCounter(`auth_error{error="fail_get_token"}`)
var authErrorFailGetUserTotal = metrics.NewCounter(`auth_error{error="fail_get_user"}`)
var authErrorFailFinishAuthTotal = metrics.NewCounter(`auth_error{error="fail_finish_auth"}`)

0 comments on commit e76a2db

Please sign in to comment.