package handler import ( "context" "database/sql" "net/http" "strings" "time" "git.haelnorr.com/h/golib/cookies" "git.haelnorr.com/h/golib/hlog" "git.haelnorr.com/h/golib/jwt" "github.com/pkg/errors" ) func revokeAccess( tokenGen *jwt.TokenGenerator, tx *sql.Tx, atStr string, ) error { aT, err := tokenGen.ValidateAccess(tx, atStr) if err != nil { if strings.Contains(err.Error(), "Token is expired") || strings.Contains(err.Error(), "Token has been revoked") { return nil // Token is expired, dont need to revoke it } return errors.Wrap(err, "jwt.ParseAccessToken") } err = aT.Revoke(tx) if err != nil { return errors.Wrap(err, "jwt.RevokeToken") } return nil } func revokeRefresh( tokenGen *jwt.TokenGenerator, tx *sql.Tx, rtStr string, ) error { rT, err := tokenGen.ValidateRefresh(tx, rtStr) if err != nil { if strings.Contains(err.Error(), "Token is expired") || strings.Contains(err.Error(), "Token has been revoked") { return nil // Token is expired, dont need to revoke it } return errors.Wrap(err, "jwt.ParseRefreshToken") } err = rT.Revoke(tx) if err != nil { return errors.Wrap(err, "jwt.RevokeToken") } return nil } // Retrieve and revoke the user's tokens func revokeTokens( tokenGen *jwt.TokenGenerator, tx *sql.Tx, r *http.Request, ) error { // get the tokens from the cookies atStr, rtStr := jwt.GetTokenCookies(r) // revoke the refresh token first as the access token expires quicker // only matters if there is an error revoking the tokens err := revokeRefresh(tokenGen, tx, rtStr) if err != nil { return errors.Wrap(err, "revokeRefresh") } err = revokeAccess(tokenGen, tx, atStr) if err != nil { return errors.Wrap(err, "revokeAccess") } return nil } // Handle a logout request func Logout( conn *sql.DB, tokenGen *jwt.TokenGenerator, logger *hlog.Logger, ) http.Handler { return http.HandlerFunc( func(w http.ResponseWriter, r *http.Request) { ctx, cancel := context.WithTimeout(r.Context(), 15*time.Second) defer cancel() tx, err := conn.BeginTx(ctx, nil) if err != nil { logger.Error().Err(err).Msg("Failed to start database transaction") w.WriteHeader(http.StatusInternalServerError) return } defer tx.Rollback() err = revokeTokens(tokenGen, tx, r) if err != nil { logger.Error().Err(err).Msg("Error occured on user logout") w.WriteHeader(http.StatusInternalServerError) return } tx.Commit() cookies.DeleteCookie(w, "access", "/") cookies.DeleteCookie(w, "refresh", "/") w.Header().Set("HX-Redirect", "/login") }, ) }