diff --git a/reviewboard/admin/forms/auth_settings.py b/reviewboard/admin/forms/auth_settings.py
index c60e413642a49097370017f1ecf8f849b60a754b..03105326ba54cc64d7249cb302ef2b61c4f36081 100644
--- a/reviewboard/admin/forms/auth_settings.py
+++ b/reviewboard/admin/forms/auth_settings.py
@@ -4,6 +4,7 @@ import logging
 
 from django import forms
 from django.utils.translation import gettext_lazy as _
+from djblets.forms.widgets import AmountSelectorWidget
 from djblets.siteconfig.forms import SiteSettingsForm
 
 from reviewboard.accounts.forms.auth import LegacyAuthModuleSettingsForm
@@ -41,6 +42,25 @@ class AuthenticationSettingsForm(SiteSettingsForm):
             'data-subform-group': 'auth-backend',
         }))
 
+    automatic_api_token_expiration = forms.IntegerField(
+        label=_('Automatic token expiration'),
+        help_text=_('API tokens are automatically created when authenticating '
+                    'services to Review Board via the login page. This sets '
+                    'how long it will take for tokens to expire after '
+                    'creation.'),
+        required=False,
+        widget=AmountSelectorWidget(
+            unit_choices=[
+                (1, _('days')),
+                (7, _('weeks')),
+                (30, _('months')),
+                (365, _('years')),
+                (None, _('Never')),
+            ],
+            number_attrs={
+                'min': 0,
+            }))
+
     def __init__(self, siteconfig, *args, **kwargs):
         """Initialize the settings form.
 
@@ -150,6 +170,8 @@ class AuthenticationSettingsForm(SiteSettingsForm):
 
         self.fields['auth_anonymous_access'].initial = \
             not self.siteconfig.get('auth_require_sitewide_login')
+        self.fields['automatic_api_token_expiration'].initial = \
+            self.siteconfig.get('client_token_expiration')
 
     def save(self):
         """Save the form.
@@ -159,6 +181,9 @@ class AuthenticationSettingsForm(SiteSettingsForm):
         """
         self.siteconfig.set('auth_require_sitewide_login',
                             not self.cleaned_data['auth_anonymous_access'])
+        self.siteconfig.set(
+            'client_token_expiration',
+            self.cleaned_data['automatic_api_token_expiration'])
 
         auth_backend = self.cleaned_data['auth_backend']
 
@@ -266,6 +291,18 @@ class AuthenticationSettingsForm(SiteSettingsForm):
         fieldsets = (
             {
                 'classes': ('wide',),
-                'fields': ['auth_anonymous_access', 'auth_backend'],
+                'fields': ['auth_anonymous_access',
+                           'auth_backend'],
+            },
+            {
+                'title': _('API Authentication Settings'),
+                'description': _(
+                    'Manage how applications and services (e.g. RBTools) '
+                    'authenticate to the Review Board API.'
+                ),
+                'classes': ('wide',),
+                'fields': (
+                    'automatic_api_token_expiration',
+                ),
             },
         )
diff --git a/reviewboard/admin/tests/test_authentication_settings_form.py b/reviewboard/admin/tests/test_authentication_settings_form.py
new file mode 100644
index 0000000000000000000000000000000000000000..6891187b73c49decb6babab363e6beb8250c79a8
--- /dev/null
+++ b/reviewboard/admin/tests/test_authentication_settings_form.py
@@ -0,0 +1,97 @@
+"""Unit tests for reviewboard.admin.forms.AuthenticationSettingsForm.
+
+Version Added:
+    5.0.5
+"""
+
+from djblets.siteconfig.models import SiteConfiguration
+
+from reviewboard.admin.forms.auth_settings import \
+    AuthenticationSettingsForm
+from reviewboard.testing.testcase import TestCase
+
+
+class AuthenticationSettingsFormTests(TestCase):
+    """Unit tests for reviewboard.admin.forms.AuthenticationSettingsForm.
+
+    Version Added:
+        5.0.5
+    """
+
+    def test_load_with_none(self) -> None:
+        """Test AuthenticationSettingsForm.load with no API tokens
+        expiration set in siteconfig
+        """
+        siteconfig_settings = {
+            'client_token_expiration': None,
+        }
+
+        with self.siteconfig_settings(siteconfig_settings):
+            siteconfig = SiteConfiguration.objects.get_current()
+            form = AuthenticationSettingsForm(siteconfig)
+            form.load()
+
+            self.assertIsNone(form['automatic_api_token_expiration'].value())
+
+    def test_load_with_date(self) -> None:
+        """Test AuthenticationSettingsForm.load with API tokens
+        expiration set in siteconfig
+        """
+        siteconfig_settings = {
+            'client_token_expiration': 10,
+        }
+
+        with self.siteconfig_settings(siteconfig_settings):
+            siteconfig = SiteConfiguration.objects.get_current()
+            form = AuthenticationSettingsForm(siteconfig)
+            form.load()
+
+            self.assertEqual(form['automatic_api_token_expiration'].value(),
+                             10)
+
+    def test_save_with_none(self) -> None:
+        """Test AuthenticationSettingsForm.save with API tokens
+        set to never expire
+        """
+        siteconfig_settings = {
+            'client_token_expiration': 10,
+        }
+
+        with self.siteconfig_settings(siteconfig_settings):
+            siteconfig = SiteConfiguration.objects.get_current()
+            form = AuthenticationSettingsForm(siteconfig, data={
+                'auth_backend': 'builtin',
+                'automatic_api_token_expiration_0': '20',
+                'automatic_api_token_expiration_1': '',
+            })
+
+            self.assertTrue(form.is_valid())
+
+            form.save()
+
+            self.assertIsNone(
+                siteconfig.get('client_token_expiration'))
+
+    def test_save_with_date(self) -> None:
+        """Test AuthenticationSettingsForm.save with API tokens
+        expiration set
+        """
+        siteconfig_settings = {
+            'client_token_expiration': None,
+        }
+
+        with self.siteconfig_settings(siteconfig_settings):
+            siteconfig = SiteConfiguration.objects.get_current()
+            form = AuthenticationSettingsForm(siteconfig, data={
+                'auth_backend': 'builtin',
+                'automatic_api_token_expiration_0': '20',
+                'automatic_api_token_expiration_1': '1',
+            })
+
+            self.assertTrue(form.is_valid())
+
+            form.save()
+
+            self.assertEqual(
+                siteconfig.get('client_token_expiration'),
+                20)
