Group K-fold with target stratification
To use both groups and stratification, it seems that you should write your own code. Note that you will inevitably loose samples in training and/or in testing set (unless you are lucky).
One way of achieving it is to:

do your split by groups (you could use the GroupKFold method from sklearn)
check the distribution of the targets in training/testing sets.
randomly remove targets in training or testing set to balance the distributions.

Note: It is possible that a group disappear using such algorithm. You might prefer to not randomly remove the targets when balancing the training/testing sets.

Here is a sample code
import pandas as pd
import numpy as np
from sklearn.model_selection import GroupKFold

df = pd.DataFrame({
    'person': ['a', 'b', 'c', 'aa', 'bb', 'cc', 'aaa', 'bbb', 'ccc'],
    'group': [10, 10, 20, 20, 20, 20, 20, 30, 30],
    'target': [1, 2, 2, 3, 2, 3, 1, 2, 3]
})

X = df['person']
y = df['target']
groups = df['group'].values
group_kfold = GroupKFold(n_splits=3)
group_kfold.get_n_splits(X, y, groups)

# First split by groups
for train_index, test_index in group_kfold.split(X, y, groups):
    print(&quot;Groups split: TRAIN:&quot;, train_index, &quot;TEST:&quot;, test_index)
    y_train_grouped, y_test_grouped = y[train_index], y[test_index]

    final_train_index = []
    final_test_index = []
    # Then balance the distributions for each target
    for target in df['target'].unique():
        target_train_index = y_train_grouped[y_train_grouped == target].index.tolist()
        target_test_index = y_test_grouped[y_test_grouped == target].index.tolist()
        n_training = len(target_train_index)
        n_testing = len(target_test_index)
        print(&quot;Target:&quot; + str(target) + &quot; - n_training:&quot; + str(n_training) + &quot; - n_testing:&quot; + str(n_testing) +
              &quot; | target_train_index:&quot; + str(target_train_index) + &quot; - target_test_index:&quot; + str(target_test_index))
        
        # Shuffle to remove randomly
        np.random.shuffle(target_train_index)
        np.random.shuffle(target_test_index)
        
        # Check if we need to remove samples from training or testing set
        if n_training &gt; n_testing:
            while n_training &gt; n_testing:
                target_train_index.pop(0)
                n_training = len(target_train_index)
        if n_training &lt; n_testing:
            while n_training &lt; n_testing:
                target_test_index.pop(0)
                n_testing = len(target_test_index)
        
        # Append new indexes to global train/test indexes
        final_train_index.append(target_train_index)
        final_test_index.append(target_test_index)

    # Flatten for readability
    final_train_index = [item for sublist in final_train_index for item in sublist]
    final_test_index = [item for sublist in final_test_index for item in sublist]
    print(&quot;FINAL split: TRAIN:&quot;, final_train_index,&quot; TEST:&quot;, final_test_index, &quot;\n&quot;)


EDIT
Using stratified cross-validation seems not to be mandatory (see links below), so you might reconsider using it.
You might find this links usefuls:

Why use stratified cross validation? Why does this not damage variance related benefit?
Understanding stratified cross-validation
A study of cross-validation and bootstrap for accuracy estimation and model selection

Regarding over/undersampling, I think it is hard to answer without more details of the data distribution and how imbalanced your classes are.