From 11a79f951a4c5a3b6de4d8cc0131ac87f8a3961b Mon Sep 17 00:00:00 2001 From: donggyu Date: Tue, 16 Jan 2024 19:34:43 +0900 Subject: [PATCH] improve: fix duplicated verification logic --- internal/delivery/http/auth.go | 31 +++++++++++++++++ internal/keycloak/keycloak.go | 12 +++---- .../auth/authenticator/keycloak/keycloak.go | 34 +++---------------- internal/route/route.go | 1 + internal/usecase/auth.go | 29 +++++++++++++++- 5 files changed, 71 insertions(+), 36 deletions(-) diff --git a/internal/delivery/http/auth.go b/internal/delivery/http/auth.go index 2c5fcd4d..1086faed 100644 --- a/internal/delivery/http/auth.go +++ b/internal/delivery/http/auth.go @@ -23,6 +23,7 @@ type IAuthHandler interface { VerifyIdentityForLostId(w http.ResponseWriter, r *http.Request) VerifyIdentityForLostPassword(w http.ResponseWriter, r *http.Request) + VerifyToken(w http.ResponseWriter, r *http.Request) //Authenticate(next http.Handler) http.Handler } type AuthHandler struct { @@ -284,3 +285,33 @@ func (h *AuthHandler) PingToken(w http.ResponseWriter, r *http.Request) { ResponseJSON(w, r, http.StatusOK, nil) } + +// VerifyToken godoc +// @Tags Auth +// @Summary verify token +// @Description verify token +// @Success 200 {object} nil +// @Router /auth/verify-token [get] + +func (h *AuthHandler) VerifyToken(w http.ResponseWriter, r *http.Request) { + token, ok := request.TokenFrom(r.Context()) + if !ok { + log.ErrorfWithContext(r.Context(), "token is not found") + ErrorJSON(w, r, httpErrors.NewInternalServerError(fmt.Errorf("token is not found"), "C_INTERNAL_ERROR", "")) + return + } + + isActive, err := h.usecase.VerifyToken(token) + if err != nil { + log.ErrorfWithContext(r.Context(), "error is :%s(%T)", err.Error(), err) + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + + if !isActive { + ErrorJSON(w, r, httpErrors.NewUnauthorizedError(fmt.Errorf("token is not active"), "C_UNAUTHORIZED", "")) + return + } + + ResponseJSON(w, r, http.StatusOK, nil) +} diff --git a/internal/keycloak/keycloak.go b/internal/keycloak/keycloak.go index 45976edb..7c3040a6 100644 --- a/internal/keycloak/keycloak.go +++ b/internal/keycloak/keycloak.go @@ -36,7 +36,7 @@ type IKeycloak interface { JoinGroup(organizationId string, userId string, groupName string) error LeaveGroup(organizationId string, userId string, groupName string) error - VerifyAccessToken(token string, organizationId string) error + VerifyAccessToken(token string, organizationId string) (bool, error) GetSessions(userId string, organizationId string) (*[]string, error) } type Keycloak struct { @@ -364,17 +364,17 @@ func (k *Keycloak) DeleteUser(organizationId string, userAccountId string) error return nil } -func (k *Keycloak) VerifyAccessToken(token string, organizationId string) error { +func (k *Keycloak) VerifyAccessToken(token string, organizationId string) (bool, error) { ctx := context.Background() rptResult, err := k.client.RetrospectToken(ctx, token, DefaultClientID, k.config.ClientSecret, organizationId) if err != nil { - return err + return false, err } - if !(*rptResult.Active) { - return err + return false, nil } - return nil + + return true, nil } func (k *Keycloak) GetSessions(userId string, organizationId string) (*[]string, error) { diff --git a/internal/middleware/auth/authenticator/keycloak/keycloak.go b/internal/middleware/auth/authenticator/keycloak/keycloak.go index 89c516c2..f480cc7e 100644 --- a/internal/middleware/auth/authenticator/keycloak/keycloak.go +++ b/internal/middleware/auth/authenticator/keycloak/keycloak.go @@ -53,23 +53,19 @@ func (a *keycloakAuthenticator) AuthenticateToken(r *http.Request, token string) return nil, false, err } - if parsedToken.Method.Alg() != "RS256" { - return nil, false, fmt.Errorf("invalid token") - } - - if parsedToken.Claims.Valid() != nil { - return nil, false, fmt.Errorf("invalid token") - } - organizationId, ok := parsedToken.Claims.(jwtWithouKey.MapClaims)["organization"].(string) if !ok { return nil, false, fmt.Errorf("organization is not found in token") } - if err := a.kc.VerifyAccessToken(token, organizationId); err != nil { + isActive, err := a.kc.VerifyAccessToken(token, organizationId) + if err != nil { log.Errorf("failed to verify access token: %v", err) return nil, false, err } + if !isActive { + return nil, false, fmt.Errorf("token is not active") + } roleProjectMapping := make(map[string]string) for _, role := range parsedToken.Claims.(jwtWithouKey.MapClaims)["tks-role"].([]interface{}) { @@ -93,26 +89,6 @@ func (a *keycloakAuthenticator) AuthenticateToken(r *http.Request, token string) return nil, false, fmt.Errorf("session id is not found in token") } - sessionIds, err := a.kc.GetSessions(userId.String(), organizationId) - if err != nil { - log.Errorf("failed to get sessions: %v", err) - - return nil, false, err - } - if len(*sessionIds) == 0 { - return nil, false, fmt.Errorf("invalid session") - } - var matched bool = false - for _, id := range *sessionIds { - if id == requestSessionId { - matched = true - break - } - } - if !matched { - return nil, false, fmt.Errorf("invalid session") - } - userInfo := &user.DefaultInfo{ UserId: userId, OrganizationId: organizationId, diff --git a/internal/route/route.go b/internal/route/route.go index e82d6214..c798bb8c 100644 --- a/internal/route/route.go +++ b/internal/route/route.go @@ -80,6 +80,7 @@ func SetupRouter(db *gorm.DB, argoClient argowf.ArgoClient, kc keycloak.IKeycloa r.HandleFunc(API_PREFIX+API_VERSION+"/auth/find-password/verification", authHandler.FindPassword).Methods(http.MethodPost) r.HandleFunc(API_PREFIX+API_VERSION+"/auth/find-id/code", authHandler.VerifyIdentityForLostId).Methods(http.MethodPost) r.HandleFunc(API_PREFIX+API_VERSION+"/auth/find-password/code", authHandler.VerifyIdentityForLostPassword).Methods(http.MethodPost) + r.HandleFunc(API_PREFIX+API_VERSION+"/auth/verify-token", authHandler.VerifyToken).Methods(http.MethodGet) //r.HandleFunc(API_PREFIX+API_VERSION+"/cookie-test", authHandler.CookieTest).Methods(http.MethodPost) //r.HandleFunc(API_PREFIX+API_VERSION+"/auth/callback", authHandler.CookieTestCallback).Methods(http.MethodGet) diff --git a/internal/usecase/auth.go b/internal/usecase/auth.go index 3455505b..a7d2479e 100644 --- a/internal/usecase/auth.go +++ b/internal/usecase/auth.go @@ -39,6 +39,7 @@ type IAuthUsecase interface { FetchRoles() (out []domain.Role, err error) SingleSignIn(organizationId, accountId, password string) ([]*http.Cookie, error) SingleSignOut(organizationId string) (string, []*http.Cookie, error) + VerifyToken(token string) (bool, error) } const ( @@ -121,10 +122,14 @@ func (u *AuthUsecase) PingToken(accessToken string, organizationId string) error return fmt.Errorf("invalid token") } - if err := u.kc.VerifyAccessToken(accessToken, organizationId); err != nil { + isActive, err := u.kc.VerifyAccessToken(accessToken, organizationId) + if err != nil { log.Errorf("failed to verify access token: %v", err) return err } + if !isActive { + return fmt.Errorf("token is not active") + } userId, err := uuid.Parse(parsedToken.Claims.(jwtWithouKey.MapClaims)["sub"].(string)) if err != nil { @@ -381,6 +386,28 @@ func (u *AuthUsecase) SingleSignOut(organizationId string) (string, []*http.Cook return redirectUrl, cookies, nil } +func (u *AuthUsecase) VerifyToken(token string) (bool, error) { + parsedToken, _, err := new(jwtWithouKey.Parser).ParseUnverified(token, jwtWithouKey.MapClaims{}) + if err != nil { + return false, err + } + org, ok := parsedToken.Claims.(jwtWithouKey.MapClaims)["organization"].(string) + if !ok { + return false, fmt.Errorf("organization is not found in token") + } + + isActive, err := u.kc.VerifyAccessToken(token, org) + if err != nil { + log.Errorf("failed to verify access token: %v", err) + return false, err + } + if !isActive { + return false, fmt.Errorf("token is not active") + } + + return true, nil +} + func (u *AuthUsecase) isExpiredEmailCode(code repository.CacheEmailCode) bool { return !helper.IsDurationExpired(code.UpdatedAt, internal.EmailCodeExpireTime) }