diff --git a/group.tokens.inc b/group.tokens.inc
index c4e4d68..09a65d5 100644
--- a/group.tokens.inc
+++ b/group.tokens.inc
@@ -116,6 +116,39 @@ function group_token_info() {
     'description' => t('A prettier way of labeling group content of the same plugin type.'),
   ];
 
+  $tokens['group_content']['label'] = [
+    'name' => t('Group content entity label'),
+    'description' => t('The label of the entity object referenced in the group content.'),
+  ];
+
+  // Build per entity type chained tokens.
+  $plugin_manager = \Drupal::service('plugin.manager.group_content_enabler');
+  if ($plugin_manager) {
+    $content_entity_types = [];
+    foreach ($plugin_manager->getAll() as $plugin_id => $plugin) {
+      $entity_type_id = $plugin->getEntityTypeId();
+      if (!isset($content_entity_types[$entity_type_id])) {
+        $entity_type_object = \Drupal::entityTypeManager()->getDefinition($entity_type_id);
+        if ($entity_type_object) {
+          $content_entity_types[$entity_type_id] = $entity_type_object->getLabel();
+        }
+      }
+    }
+
+    foreach ($content_entity_types as $content_entity_type_id => $content_entity_type_label) {
+      $tokens['group_content'][$content_entity_type_id] = [
+        'type' => $content_entity_type_id,
+        'name' => t('Group content entity: @label', [
+          '@label' => $content_entity_type_label,
+        ]),
+        'description' => t('The @label (@type) entity object for the group content.', [
+          '@label' => $content_entity_type_label,
+          '@type' => $content_entity_type_id,
+        ]),
+      ];
+    }
+  }
+
   return ['types' => $types, 'tokens' => $tokens];
 }
 
@@ -206,6 +239,8 @@ function group_tokens($type, $tokens, array $data, array $options, BubbleableMet
   elseif ($type == 'group_content' && !empty($data[$type])) {
     /** @var \Drupal\group\Entity\GroupContentInterface $group_content */
     $group_content = $data['group_content'];
+    $content_entity = $group_content->getEntity();
+    $content_entity_type = $content_entity->getEntityTypeId();
 
     foreach ($tokens as $name => $original) {
       switch ($name) {
@@ -247,6 +282,19 @@ function group_tokens($type, $tokens, array $data, array $options, BubbleableMet
           $bubbleable_metadata->addCacheableDependency($date_format);
           $replacements[$original] = \Drupal::service('date.formatter')->format($group_content->getChangedTime(), 'medium', '', NULL, $langcode);
           break;
+
+        case 'label':
+          $replacements[$original] = $group_content->label();
+          break;
+
+        case $content_entity_type:
+          // Entity chained references.
+          // Add cache dependency.
+          $bubbleable_metadata->addCacheableDependency($content_entity);
+
+          // Provide a string for tokens that are not chained.
+          $replacements[$original] = $content_entity->label();
+          break;
       }
 
       // Actual chaining of tokens handled below.
@@ -261,6 +309,17 @@ function group_tokens($type, $tokens, array $data, array $options, BubbleableMet
       if ($changed_tokens = $token_service->findWithPrefix($tokens, 'changed')) {
         $replacements += $token_service->generate('date', $changed_tokens, ['date' => $group_content->getChangedTime()], $options, $bubbleable_metadata);
       }
+
+      // Content entity chaining of tokens.
+      // Example: "group_content:node:field_cool_value".
+      if ($content_entity && $content_entity_type) {
+        if ($content_entity_tokens = $token_service->findWithPrefix($tokens, $content_entity_type)) {
+          $bubbleable_metadata->addCacheableDependency($content_entity);
+          $replacements += $token_service->generate($content_entity_type, $content_entity_tokens, [
+            $content_entity_type => $content_entity,
+          ], $options, $bubbleable_metadata);
+        }
+      }
     }
   }
 
diff --git a/modules/gnode/gnode.module b/modules/gnode/gnode.module
index 0efb6b1..c441365 100644
--- a/modules/gnode/gnode.module
+++ b/modules/gnode/gnode.module
@@ -299,3 +299,42 @@ function gnode_node_access_records(NodeInterface $node) {
 
   return $records;
 }
+
+/**
+ * Implements hook_path_update().
+ *
+ * Track path alias changes to published node translations that are related
+ * to a group.
+ */
+function gnode_path_update($path) {
+  if (empty($path['source']) || !\Drupal::moduleHandler()->moduleExists('pathauto')) {
+    return;
+  }
+
+  // Extract nid from the source path.
+  if (preg_match('@^\/node\/(\d+)$@', $path['source'], $node_matches)) {
+    // Load the node.
+    $nid = $node_matches[1];
+    $node = \Drupal::entityTypeManager()->getStorage('node')->load($nid);
+    if ($node) {
+      $pathauto = \Drupal::service('pathauto.generator');
+      $entity_mgr = \Drupal::service('entity.repository');
+      $langcode = !empty($path['langcode']) ? $path['langcode'] : \Drupal::service('language.default')->get()->getId();
+      // Get the node translation.
+      $node_translation = $entity_mgr->getTranslationFromContext($node, $langcode);
+
+      // Only update published nodes.
+      if ($node_translation->isPublished()) {
+        // Find the group relations for this node.
+        $relations = \Drupal::entityTypeManager()->getStorage('group_content')->loadByEntity($node_translation);
+        if ($relations) {
+          // Update the alias for each relation in the same language.
+          foreach ($relations as $relation) {
+            $relation_update = $entity_mgr->getTranslationFromContext($relation, $langcode);
+            $pathauto->updateEntityAlias($relation_update, 'update');
+          }
+        }
+      }
+    }
+  }
+}
