diff --git a/lower-constraints.txt b/lower-constraints.txt
index f8a6cde90d..88c75cf47b 100644
--- a/lower-constraints.txt
+++ b/lower-constraints.txt
@@ -95,7 +95,7 @@ python-heatclient==1.10.0
 python-ironic-inspector-client==1.5.0
 python-ironicclient==2.3.0
 python-karborclient==0.6.0
-python-keystoneclient==3.8.0
+python-keystoneclient==3.15.0
 python-mimeparse==1.6.0
 python-mistralclient==3.1.0
 python-muranoclient==0.8.2
diff --git a/openstackclient/identity/v3/role.py b/openstackclient/identity/v3/role.py
index 2828a34928..58a76f8a65 100644
--- a/openstackclient/identity/v3/role.py
+++ b/openstackclient/identity/v3/role.py
@@ -31,13 +31,18 @@ LOG = logging.getLogger(__name__)
 
 
 def _add_identity_and_resource_options_to_parser(parser):
-    domain_or_project = parser.add_mutually_exclusive_group()
-    domain_or_project.add_argument(
+    system_or_domain_or_project = parser.add_mutually_exclusive_group()
+    system_or_domain_or_project.add_argument(
+        '--system',
+        metavar='<system>',
+        help=_('Include <system> (all)'),
+    )
+    system_or_domain_or_project.add_argument(
         '--domain',
         metavar='<domain>',
         help=_('Include <domain> (name or ID)'),
     )
-    domain_or_project.add_argument(
+    system_or_domain_or_project.add_argument(
         '--project',
         metavar='<project>',
         help=_('Include <project> (name or ID)'),
@@ -62,7 +67,14 @@ def _add_identity_and_resource_options_to_parser(parser):
 def _process_identity_and_resource_options(parsed_args,
                                            identity_client_manager):
     kwargs = {}
-    if parsed_args.user and parsed_args.domain:
+    if parsed_args.user and parsed_args.system:
+        kwargs['user'] = common.find_user(
+            identity_client_manager,
+            parsed_args.user,
+            parsed_args.user_domain,
+        ).id
+        kwargs['system'] = parsed_args.system
+    elif parsed_args.user and parsed_args.domain:
         kwargs['user'] = common.find_user(
             identity_client_manager,
             parsed_args.user,
@@ -83,6 +95,13 @@ def _process_identity_and_resource_options(parsed_args,
             parsed_args.project,
             parsed_args.project_domain,
         ).id
+    elif parsed_args.group and parsed_args.system:
+        kwargs['group'] = common.find_group(
+            identity_client_manager,
+            parsed_args.group,
+            parsed_args.group_domain,
+        ).id
+        kwargs['system'] = parsed_args.system
     elif parsed_args.group and parsed_args.domain:
         kwargs['group'] = common.find_group(
             identity_client_manager,
@@ -109,8 +128,8 @@ def _process_identity_and_resource_options(parsed_args,
 
 
 class AddRole(command.Command):
-    _description = _("Adds a role assignment to a user or group on a domain "
-                     "or project")
+    _description = _("Adds a role assignment to a user or group on the "
+                     "system, a domain, or a project")
 
     def get_parser(self, prog_name):
         parser = super(AddRole, self).get_parser(prog_name)
@@ -381,7 +400,7 @@ class ListRole(command.Lister):
 
 
 class RemoveRole(command.Command):
-    _description = _("Removes a role assignment from domain/project : "
+    _description = _("Removes a role assignment from system/domain/project : "
                      "user/group")
 
     def get_parser(self, prog_name):
diff --git a/openstackclient/identity/v3/role_assignment.py b/openstackclient/identity/v3/role_assignment.py
index a362adb07d..9c2f3d249e 100644
--- a/openstackclient/identity/v3/role_assignment.py
+++ b/openstackclient/identity/v3/role_assignment.py
@@ -55,17 +55,22 @@ class ListRoleAssignment(command.Lister):
             help=_('Group to filter (name or ID)'),
         )
         common.add_group_domain_option_to_parser(parser)
-        domain_or_project = parser.add_mutually_exclusive_group()
-        domain_or_project.add_argument(
+        system_or_domain_or_project = parser.add_mutually_exclusive_group()
+        system_or_domain_or_project.add_argument(
             '--domain',
             metavar='<domain>',
             help=_('Domain to filter (name or ID)'),
         )
-        domain_or_project.add_argument(
+        system_or_domain_or_project.add_argument(
             '--project',
             metavar='<project>',
             help=_('Project to filter (name or ID)'),
         )
+        system_or_domain_or_project.add_argument(
+            '--system',
+            metavar='<system>',
+            help=_('Filter based on system role assignments'),
+        )
         common.add_project_domain_option_to_parser(parser)
         common.add_inherited_option_to_parser(parser)
         parser.add_argument(
@@ -85,7 +90,8 @@ class ListRoleAssignment(command.Lister):
 
     def _as_tuple(self, assignment):
         return (assignment.role, assignment.user, assignment.group,
-                assignment.project, assignment.domain, assignment.inherited)
+                assignment.project, assignment.domain, assignment.system,
+                assignment.inherited)
 
     def take_action(self, parsed_args):
         identity_client = self.app.client_manager.identity
@@ -117,6 +123,10 @@ class ListRoleAssignment(command.Lister):
                     auth_ref.user_id
                 )
 
+        system = None
+        if parsed_args.system:
+            system = parsed_args.system
+
         domain = None
         if parsed_args.domain:
             domain = common.find_domain(
@@ -149,7 +159,9 @@ class ListRoleAssignment(command.Lister):
 
         include_names = True if parsed_args.names else False
         effective = True if parsed_args.effective else False
-        columns = ('Role', 'User', 'Group', 'Project', 'Domain', 'Inherited')
+        columns = (
+            'Role', 'User', 'Group', 'Project', 'Domain', 'System', 'Inherited'
+        )
 
         inherited_to = 'projects' if parsed_args.inherited else None
         data = identity_client.role_assignments.list(
@@ -157,6 +169,7 @@ class ListRoleAssignment(command.Lister):
             user=user,
             group=group,
             project=project,
+            system=system,
             role=role,
             effective=effective,
             os_inherit_extension_inherited_to=inherited_to,
@@ -174,14 +187,24 @@ class ListRoleAssignment(command.Lister):
                 else:
                     setattr(assignment, 'project', scope['project']['id'])
                 assignment.domain = ''
+                assignment.system = ''
             elif 'domain' in scope:
                 if include_names:
                     setattr(assignment, 'domain', scope['domain']['name'])
                 else:
                     setattr(assignment, 'domain', scope['domain']['id'])
                 assignment.project = ''
-
+                assignment.system = ''
+            elif 'system' in scope:
+                # NOTE(lbragstad): If, or when, keystone supports role
+                # assignments on subsets of a system, this will have to evolve
+                # to handle that case instead of hardcoding to the entire
+                # system.
+                setattr(assignment, 'system', 'all')
+                assignment.domain = ''
+                assignment.project = ''
             else:
+                assignment.system = ''
                 assignment.domain = ''
                 assignment.project = ''
 
diff --git a/openstackclient/identity/v3/token.py b/openstackclient/identity/v3/token.py
index effb9e3525..1933ecad65 100644
--- a/openstackclient/identity/v3/token.py
+++ b/openstackclient/identity/v3/token.py
@@ -192,6 +192,12 @@ class IssueToken(command.ShowOne):
             data['user_id'] = auth_ref.user_id
         if auth_ref.domain_id:
             data['domain_id'] = auth_ref.domain_id
+        if auth_ref.system_scoped:
+            # NOTE(lbragstad): This could change in the future when, or if,
+            # keystone supports the ability to scope to a subset of the entire
+            # deployment system. When that happens, this will have to relay
+            # scope information and IDs like we do for projects and domains.
+            data['system'] = 'all'
         return zip(*sorted(six.iteritems(data)))
 
 
diff --git a/openstackclient/tests/unit/identity/v3/test_role_assignment.py b/openstackclient/tests/unit/identity/v3/test_role_assignment.py
index 835837e608..bff6c56df1 100644
--- a/openstackclient/tests/unit/identity/v3/test_role_assignment.py
+++ b/openstackclient/tests/unit/identity/v3/test_role_assignment.py
@@ -34,6 +34,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
         'Group',
         'Project',
         'Domain',
+        'System',
         'Inherited',
     )
 
@@ -95,6 +96,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
 
         self.role_assignments_mock.list.assert_called_with(
             domain=None,
+            system=None,
             group=None,
             effective=False,
             role=None,
@@ -110,12 +112,14 @@ class TestRoleAssignmentList(TestRoleAssignment):
             '',
             identity_fakes.project_id,
             '',
+            '',
             False
         ), (identity_fakes.role_id,
             '',
             identity_fakes.group_id,
             identity_fakes.project_id,
             '',
+            '',
             False
             ),)
         self.assertEqual(datalist, tuple(data))
@@ -143,6 +147,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
         verifylist = [
             ('user', identity_fakes.user_name),
             ('group', None),
+            ('system', None),
             ('domain', None),
             ('project', None),
             ('role', None),
@@ -159,6 +164,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
 
         self.role_assignments_mock.list.assert_called_with(
             domain=None,
+            system=None,
             user=self.users_mock.get(),
             group=None,
             project=None,
@@ -174,12 +180,14 @@ class TestRoleAssignmentList(TestRoleAssignment):
             '',
             '',
             identity_fakes.domain_id,
+            '',
             False
         ), (identity_fakes.role_id,
             identity_fakes.user_id,
             '',
             identity_fakes.project_id,
             '',
+            '',
             False
             ),)
         self.assertEqual(datalist, tuple(data))
@@ -207,6 +215,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
         verifylist = [
             ('user', None),
             ('group', identity_fakes.group_name),
+            ('system', None),
             ('domain', None),
             ('project', None),
             ('role', None),
@@ -223,6 +232,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
 
         self.role_assignments_mock.list.assert_called_with(
             domain=None,
+            system=None,
             group=self.groups_mock.get(),
             effective=False,
             project=None,
@@ -238,12 +248,14 @@ class TestRoleAssignmentList(TestRoleAssignment):
             identity_fakes.group_id,
             '',
             identity_fakes.domain_id,
+            '',
             False
         ), (identity_fakes.role_id,
             '',
             identity_fakes.group_id,
             identity_fakes.project_id,
             '',
+            '',
             False
             ),)
         self.assertEqual(datalist, tuple(data))
@@ -271,6 +283,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
         verifylist = [
             ('user', None),
             ('group', None),
+            ('system', None),
             ('domain', identity_fakes.domain_name),
             ('project', None),
             ('role', None),
@@ -287,6 +300,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
 
         self.role_assignments_mock.list.assert_called_with(
             domain=self.domains_mock.get(),
+            system=None,
             group=None,
             effective=False,
             project=None,
@@ -302,12 +316,14 @@ class TestRoleAssignmentList(TestRoleAssignment):
             '',
             '',
             identity_fakes.domain_id,
+            '',
             False
         ), (identity_fakes.role_id,
             '',
             identity_fakes.group_id,
             '',
             identity_fakes.domain_id,
+            '',
             False
             ),)
         self.assertEqual(datalist, tuple(data))
@@ -335,6 +351,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
         verifylist = [
             ('user', None),
             ('group', None),
+            ('system', None),
             ('domain', None),
             ('project', identity_fakes.project_name),
             ('role', None),
@@ -351,6 +368,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
 
         self.role_assignments_mock.list.assert_called_with(
             domain=None,
+            system=None,
             group=None,
             effective=False,
             project=self.projects_mock.get(),
@@ -366,12 +384,14 @@ class TestRoleAssignmentList(TestRoleAssignment):
             '',
             identity_fakes.project_id,
             '',
+            '',
             False
         ), (identity_fakes.role_id,
             '',
             identity_fakes.group_id,
             identity_fakes.project_id,
             '',
+            '',
             False
             ),)
         self.assertEqual(datalist, tuple(data))
@@ -398,6 +418,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
         verifylist = [
             ('user', None),
             ('group', None),
+            ('system', None),
             ('domain', None),
             ('project', None),
             ('role', None),
@@ -416,6 +437,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
 
         self.role_assignments_mock.list.assert_called_with(
             domain=None,
+            system=None,
             user=self.users_mock.get(),
             group=None,
             project=self.projects_mock.get(),
@@ -431,6 +453,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
             '',
             identity_fakes.project_id,
             '',
+            '',
             False
         ),)
         self.assertEqual(datalist, tuple(data))
@@ -456,6 +479,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
         verifylist = [
             ('user', None),
             ('group', None),
+            ('system', None),
             ('domain', None),
             ('project', None),
             ('role', None),
@@ -472,6 +496,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
 
         self.role_assignments_mock.list.assert_called_with(
             domain=None,
+            system=None,
             group=None,
             effective=True,
             project=None,
@@ -487,12 +512,14 @@ class TestRoleAssignmentList(TestRoleAssignment):
             '',
             identity_fakes.project_id,
             '',
+            '',
             False
         ), (identity_fakes.role_id,
             identity_fakes.user_id,
             '',
             '',
             identity_fakes.domain_id,
+            '',
             False
             ),)
         self.assertEqual(tuple(data), datalist)
@@ -520,6 +547,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
         verifylist = [
             ('user', None),
             ('group', None),
+            ('system', None),
             ('domain', None),
             ('project', None),
             ('role', None),
@@ -536,6 +564,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
 
         self.role_assignments_mock.list.assert_called_with(
             domain=None,
+            system=None,
             group=None,
             effective=False,
             project=None,
@@ -551,12 +580,14 @@ class TestRoleAssignmentList(TestRoleAssignment):
             '',
             identity_fakes.project_id,
             '',
+            '',
             True
         ), (identity_fakes.role_id,
             identity_fakes.user_id,
             '',
             '',
             identity_fakes.domain_id,
+            '',
             True
             ),)
         self.assertEqual(datalist, tuple(data))
@@ -584,6 +615,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
         verifylist = [
             ('user', None),
             ('group', None),
+            ('system', None),
             ('domain', None),
             ('project', None),
             ('role', None),
@@ -602,6 +634,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
 
         self.role_assignments_mock.list.assert_called_with(
             domain=None,
+            system=None,
             group=None,
             effective=False,
             project=None,
@@ -610,7 +643,9 @@ class TestRoleAssignmentList(TestRoleAssignment):
             os_inherit_extension_inherited_to=None,
             include_names=True)
 
-        collist = ('Role', 'User', 'Group', 'Project', 'Domain', 'Inherited')
+        collist = (
+            'Role', 'User', 'Group', 'Project', 'Domain', 'System', 'Inherited'
+        )
         self.assertEqual(columns, collist)
 
         datalist1 = ((
@@ -620,12 +655,14 @@ class TestRoleAssignmentList(TestRoleAssignment):
             '@'.join([identity_fakes.project_name,
                       identity_fakes.domain_name]),
             '',
+            '',
             False
         ), (identity_fakes.role_name,
             '@'.join([identity_fakes.user_name, identity_fakes.domain_name]),
             '',
             '',
             identity_fakes.domain_name,
+            '',
             False
             ),)
         self.assertEqual(tuple(data), datalist1)
@@ -648,6 +685,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
         verifylist = [
             ('user', None),
             ('group', None),
+            ('system', None),
             ('domain', None),
             ('project', None),
             ('role', identity_fakes.ROLE_2['name']),
@@ -664,6 +702,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
 
         self.role_assignments_mock.list.assert_called_with(
             domain=None,
+            system=None,
             user=None,
             group=None,
             project=None,
@@ -679,6 +718,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
             '',
             '',
             identity_fakes.domain_id,
+            '',
             False
         ),)
         self.assertEqual(datalist, tuple(data))
diff --git a/releasenotes/notes/implement-system-scope-4c3c47996f98deac.yaml b/releasenotes/notes/implement-system-scope-4c3c47996f98deac.yaml
new file mode 100644
index 0000000000..6ad3d8244d
--- /dev/null
+++ b/releasenotes/notes/implement-system-scope-4c3c47996f98deac.yaml
@@ -0,0 +1,8 @@
+---
+features:
+  - |
+    Added support for system-scope. This includes support for the ability to
+    generate system-scoped tokens using ``system_scope: all`` in ``cloud.yaml``
+    or ``OS_SYSTEM_SCOPE=all`` in an environment variable. Support is also
+    included for managing role assignments on the system using ``--system``
+    when adding and removing roles.
diff --git a/requirements.txt b/requirements.txt
index bfee36b687..c5795fd5ed 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -12,6 +12,6 @@ osc-lib>=1.8.0 # Apache-2.0
 oslo.i18n>=3.15.3 # Apache-2.0
 oslo.utils>=3.33.0 # Apache-2.0
 python-glanceclient>=2.8.0 # Apache-2.0
-python-keystoneclient>=3.8.0 # Apache-2.0
+python-keystoneclient>=3.15.0 # Apache-2.0
 python-novaclient>=9.1.0 # Apache-2.0
 python-cinderclient>=3.3.0 # Apache-2.0