From ab2b04c3cd8fa76f35b5635fa775badc9e17ea31 Mon Sep 17 00:00:00 2001 From: itsscb Date: Mon, 9 Oct 2023 00:25:57 +0200 Subject: [PATCH] Add check on db if session is expired Fixes #76 --- bff/api/middleware_test.go | 5 ++++- bff/api/session.go | 17 ++++++++++------- bff/api/token.go | 6 ++++++ bff/gapi/authorization.go | 23 ++++++++++++----------- bff/gapi/rpc_login.go | 21 ++++++++++++++------- bff/gapi/rpc_refresh_token.go | 7 ++++++- bff/token/maker.go | 5 ++++- bff/token/paseto_maker.go | 10 +++++++--- bff/token/paseto_maker_test.go | 8 ++++++-- bff/token/payload.go | 7 +------ 10 files changed, 70 insertions(+), 39 deletions(-) diff --git a/bff/api/middleware_test.go b/bff/api/middleware_test.go index 65fb6eb..7ac4683 100644 --- a/bff/api/middleware_test.go +++ b/bff/api/middleware_test.go @@ -22,7 +22,10 @@ func addAuthorization( email string, duration time.Duration, ) { - token, payload, err := tokenMaker.CreateToken(email, duration) + id, err := tokenMaker.NewTokenID() + require.NoError(t, err) + + token, payload, err := tokenMaker.CreateToken(email, id, duration) require.NoError(t, err) require.NotEmpty(t, payload) diff --git a/bff/api/session.go b/bff/api/session.go index f270d21..40ad207 100644 --- a/bff/api/session.go +++ b/bff/api/session.go @@ -50,19 +50,22 @@ func (server *Server) loginAccount(ctx *gin.Context) { return } - accessToken, accessPayload, err := server.tokenMaker.CreateToken( - account.Email, - server.config.AccessTokenDuration, - ) + id, err := server.tokenMaker.NewTokenID() if err != nil { - ctx.JSON(http.StatusInternalServerError, errorResponse(err)) - return + ctx.JSON(http.StatusInternalServerError, errorResponse(errors.New("failed to create session token"))) } - refreshToken, refreshPayload, err := server.tokenMaker.CreateToken( account.Email, + id, server.config.RefreshTokenDuration, ) + + accessToken, accessPayload, err := server.tokenMaker.CreateToken( + account.Email, + id, + server.config.AccessTokenDuration, + ) + if err != nil { ctx.JSON(http.StatusInternalServerError, errorResponse(err)) return diff --git a/bff/api/token.go b/bff/api/token.go index cd3f11a..3d16029 100644 --- a/bff/api/token.go +++ b/bff/api/token.go @@ -66,10 +66,16 @@ func (server *Server) renewAccessToken(ctx *gin.Context) { return } + id, err := server.tokenMaker.NewTokenID() + if err != nil { + ctx.JSON(http.StatusInternalServerError, errorResponse(errors.New("failed to create session token"))) + } accessToken, accessPayload, err := server.tokenMaker.CreateToken( refreshPayload.Email, + id, server.config.AccessTokenDuration, ) + if err != nil { ctx.JSON(http.StatusInternalServerError, errorResponse(err)) return diff --git a/bff/gapi/authorization.go b/bff/gapi/authorization.go index d091e2e..196b9bc 100644 --- a/bff/gapi/authorization.go +++ b/bff/gapi/authorization.go @@ -2,8 +2,10 @@ package gapi import ( "context" + "database/sql" "fmt" "strings" + "time" "github.com/itsscb/df/bff/token" "google.golang.org/grpc/metadata" @@ -42,18 +44,17 @@ func (server *Server) authorizeUser(ctx context.Context) (*token.Payload, error) return nil, fmt.Errorf("invalid access token: %s", err) } - // TODO: #76 Add check on db if session is expired - // session, err := server.store.GetSession(ctx, payload.ID) - // if err != nil { - // if err == sql.ErrNoRows { - // return nil, fmt.Errorf("no valid session found") - // } - // return nil, fmt.Errorf("could not get session") - // } + session, err := server.store.GetSession(ctx, payload.ID) + if err != nil { + if err == sql.ErrNoRows { + return nil, fmt.Errorf("no valid session found") + } + return nil, fmt.Errorf("could not get session") + } - // if session.IsBlocked || time.Now().After(session.ExpiresAt) { - // return nil, fmt.Errorf("blocked or expired") - // } + if session.IsBlocked || time.Now().After(session.ExpiresAt) { + return nil, fmt.Errorf("session blocked or expired") + } return payload, nil } diff --git a/bff/gapi/rpc_login.go b/bff/gapi/rpc_login.go index 5e2199e..ff55721 100644 --- a/bff/gapi/rpc_login.go +++ b/bff/gapi/rpc_login.go @@ -35,16 +35,14 @@ func (server *Server) Login(ctx context.Context, req *pb.LoginRequest) (*pb.Logi return nil, status.Error(codes.PermissionDenied, "invalid password") } - accessToken, accessPayload, err := server.tokenMaker.CreateToken( - account.Email, - server.config.AccessTokenDuration, - ) + id, err := server.tokenMaker.NewTokenID() if err != nil { - return nil, status.Error(codes.Internal, "failed to create access token") + return nil, status.Error(codes.Internal, "failed to create token id") } refreshToken, refreshPayload, err := server.tokenMaker.CreateToken( account.Email, + id, server.config.RefreshTokenDuration, ) if err != nil { @@ -52,9 +50,18 @@ func (server *Server) Login(ctx context.Context, req *pb.LoginRequest) (*pb.Logi } + accessToken, accessPayload, err := server.tokenMaker.CreateToken( + account.Email, + id, + server.config.AccessTokenDuration, + ) + if err != nil { + return nil, status.Error(codes.Internal, "failed to create access token") + } + mtdt := server.extractMetadata(ctx) - session, err := server.store.CreateSession(ctx, db.CreateSessionParams{ + _, err = server.store.CreateSession(ctx, db.CreateSessionParams{ ID: refreshPayload.ID, Email: account.Email, RefreshToken: refreshToken, @@ -69,7 +76,7 @@ func (server *Server) Login(ctx context.Context, req *pb.LoginRequest) (*pb.Logi } rsp := &pb.LoginResponse{ - SessionId: session.ID.String(), + SessionId: refreshPayload.ID.String(), AccessToken: accessToken, AccessTokenExpiresAt: timestamppb.New(accessPayload.ExpiredAt), RefreshToken: refreshToken, diff --git a/bff/gapi/rpc_refresh_token.go b/bff/gapi/rpc_refresh_token.go index fab705f..55e7e23 100644 --- a/bff/gapi/rpc_refresh_token.go +++ b/bff/gapi/rpc_refresh_token.go @@ -52,12 +52,17 @@ func (server *Server) RefreshToken(ctx context.Context, req *pb.RefreshTokenRequ return nil, status.Error(codes.PermissionDenied, "session expired") } + id, err := server.tokenMaker.NewTokenID() + if err != nil { + return nil, status.Error(codes.Internal, "failed to create session token") + } accessToken, accessPayload, err := server.tokenMaker.CreateToken( refreshPayload.Email, + id, server.config.AccessTokenDuration, ) if err != nil { - return nil, status.Error(codes.Internal, "cannot create session token") + return nil, status.Error(codes.Internal, "failed to create session token") } rsp := &pb.RefreshTokenResponse{ diff --git a/bff/token/maker.go b/bff/token/maker.go index d2e7577..3598bb4 100644 --- a/bff/token/maker.go +++ b/bff/token/maker.go @@ -2,12 +2,15 @@ package token import ( "time" + + "github.com/google/uuid" ) // Maker is an interface for managing tokens type Maker interface { + NewTokenID() (uuid.UUID, error) // CreateToken creates a new token for a specific username and duration - CreateToken(email string, duration time.Duration) (string, *Payload, error) + CreateToken(email string, id uuid.UUID, duration time.Duration) (string, *Payload, error) // VerifyToken checks if the token is valid or not VerifyToken(token string) (*Payload, error) diff --git a/bff/token/paseto_maker.go b/bff/token/paseto_maker.go index 03a7a59..eaf1aa2 100644 --- a/bff/token/paseto_maker.go +++ b/bff/token/paseto_maker.go @@ -30,9 +30,13 @@ func NewPasetoMaker(privateKeyHex string) (Maker, error) { return maker, nil } +func (maker *PasetoMaker) NewTokenID() (uuid.UUID, error) { + return uuid.NewRandom() +} + // CreateToken creates a new token for a specific username and duration -func (maker *PasetoMaker) CreateToken(email string, duration time.Duration) (string, *Payload, error) { - payload, err := NewPayload(email, duration) +func (maker *PasetoMaker) CreateToken(email string, id uuid.UUID, duration time.Duration) (string, *Payload, error) { + payload, err := NewPayload(email, id, duration) if err != nil { return "", payload, err } @@ -41,7 +45,7 @@ func (maker *PasetoMaker) CreateToken(email string, duration time.Duration) (str token.SetNotBefore(time.Now()) token.SetIssuedAt(payload.IssuedAt) token.SetExpiration(payload.ExpiredAt) - token.SetString("id", payload.ID.String()) + token.SetString("id", id.String()) token.SetString("email", payload.Email) signed := token.V4Sign(maker.privateKey, nil) diff --git a/bff/token/paseto_maker_test.go b/bff/token/paseto_maker_test.go index 2c54a22..cbd9e48 100644 --- a/bff/token/paseto_maker_test.go +++ b/bff/token/paseto_maker_test.go @@ -18,7 +18,9 @@ func TestPasetoMaker(t *testing.T) { issuedAt := time.Now() expiredAt := issuedAt.Add(duration) - token, payload, err := maker.CreateToken(email, duration) + id, err := maker.NewTokenID() + require.NoError(t, err) + token, payload, err := maker.CreateToken(email, id, duration) require.NoError(t, err) require.NotEmpty(t, token) require.NotEmpty(t, payload) @@ -37,7 +39,9 @@ func TestExpiredPasetoToken(t *testing.T) { maker, err := NewPasetoMaker(devPrivateKeyHex) require.NoError(t, err) - token, payload, err := maker.CreateToken(util.RandomEmail(), -time.Minute) + id, err := maker.NewTokenID() + require.NoError(t, err) + token, payload, err := maker.CreateToken(util.RandomEmail(), id, -time.Minute) require.NoError(t, err) require.NotEmpty(t, token) require.NotEmpty(t, payload) diff --git a/bff/token/payload.go b/bff/token/payload.go index 55d2b7f..4f84373 100644 --- a/bff/token/payload.go +++ b/bff/token/payload.go @@ -22,12 +22,7 @@ type Payload struct { } // NewPayload creates a new token payload with a specific accountID and duration -func NewPayload(email string, duration time.Duration) (*Payload, error) { - tokenID, err := uuid.NewRandom() - if err != nil { - return nil, err - } - +func NewPayload(email string, tokenID uuid.UUID, duration time.Duration) (*Payload, error) { payload := &Payload{ ID: tokenID, Email: email,