Repository: PrefectHQ/prefect

Issue:
Raise error if no RUNNER token is provided to agent
## Description
If an agent is started without a token (e.g. `prefect agent start` without `--token`) it will default to using the `cloud.agent.auth_token` (RUNNER) in config https://github.com/PrefectHQ/prefect/blob/master/src/prefect/agent/agent.py#L39. If that is not set then an empty string is passed into the Client initialization and in the case of a provided empty token it defaults to `cloud.auth_token` (USER) token https://github.com/PrefectHQ/prefect/blob/master/src/prefect/client/client.py#L86. 

This was allowing the state to be updated to `Submitted` in cloud but upon creating the job the RUNNER token was once again asked for https://github.com/PrefectHQ/prefect/blob/master/src/prefect/agent/local/agent.py#L82 in populating the config for the job. 

This was throwing a `Malformed Authorization header` because the header that was provided was an empty Bearer.




## Expected Behavior
The agent should raise an error on start if it does not have a RUNNER token.




## Reproduction
Run the Local Agent without a token and run a flow :)



## Environment
N/A

cc @bnaul @cicdw 




Discussion:

Resolving patch:
diff --git a/src/prefect/agent/agent.py b/src/prefect/agent/agent.py
--- a/src/prefect/agent/agent.py
+++ b/src/prefect/agent/agent.py
@@ -8,6 +8,7 @@
 from prefect.client import Client
 from prefect.serialization import state
 from prefect.engine.state import Submitted
+from prefect.utilities.exceptions import AuthorizationError
 from prefect.utilities.graphql import with_args
 
 
@@ -36,7 +37,11 @@ class Agent:
     """
 
     def __init__(self) -> None:
-        self.client = Client(api_token=config.cloud.agent.get("auth_token"))
+
+        token = config.cloud.agent.get("auth_token")
+
+        self.client = Client(api_token=token)
+        self._verify_token(token)
 
         logger = logging.getLogger("agent")
         logger.setLevel(logging.DEBUG)
@@ -50,6 +55,27 @@ def __init__(self) -> None:
 
         self.logger = logger
 
+    def _verify_token(self, token: str) -> None:
+        """
+        Checks whether a token with a `RUNNER` scope was provided
+
+        Args:
+            - token (str): The provided agent token to verify
+
+        Raises:
+            - AuthorizationError: if token is empty or does not have a RUNNER role
+        """
+        if not token:
+            raise AuthorizationError("No agent API token provided.")
+
+        # Check if RUNNER role
+        result = self.client.graphql(query="query { authInfo { apiTokenScope } }")
+        if (
+            not result.data  # type: ignore
+            or result.data.authInfo.apiTokenScope != "RUNNER"  # type: ignore
+        ):
+            raise AuthorizationError("Provided token does not have a RUNNER scope.")
+
     def start(self) -> None:
         """
         The main entrypoint to the agent. This function loops and constantly polls for


Test patch:
diff --git a/tests/agent/test_agent.py b/tests/agent/test_agent.py
--- a/tests/agent/test_agent.py
+++ b/tests/agent/test_agent.py
@@ -9,12 +9,12 @@
 from prefect.utilities.graphql import GraphQLResult
 
 
-def test_agent_init():
+def test_agent_init(runner_token):
     agent = Agent()
     assert agent
 
 
-def test_agent_config_options():
+def test_agent_config_options(runner_token):
     with set_temporary_config({"cloud.agent.auth_token": "TEST_TOKEN"}):
         agent = Agent()
         assert agent.client.get_auth_token() == "TEST_TOKEN"
@@ -22,229 +22,231 @@ def test_agent_config_options():
 
 
 def test_agent_fails_no_auth_token():
-    with set_temporary_config({"cloud.agent.auth_token": None}):
-        with pytest.raises(AuthorizationError):
-            agent = Agent()
-            agent.query_tenant_id()
+    with pytest.raises(AuthorizationError):
+        agent = Agent()
+        agent.query_tenant_id()
 
 
-def test_query_tenant_id(monkeypatch):
-    with set_temporary_config({"cloud.agent.auth_token": "token"}):
-        post = MagicMock(
-            return_value=MagicMock(
-                json=MagicMock(return_value=dict(data=dict(tenant=[dict(id="id")])))
+def test_agent_fails_no_runner_token(monkeypatch):
+    post = MagicMock(
+        return_value=MagicMock(
+            json=MagicMock(
+                return_value=dict(data=dict(authInfo=MagicMock(apiTokenScope="USER")))
             )
         )
-        session = MagicMock()
-        session.return_value.post = post
-        monkeypatch.setattr("requests.Session", session)
+    )
+    session = MagicMock()
+    session.return_value.post = post
+    monkeypatch.setattr("requests.Session", session)
 
+    with pytest.raises(AuthorizationError):
         agent = Agent()
-        tenant_id = agent.query_tenant_id()
-        assert tenant_id == "id"
+        agent.query_tenant_id()
 
 
-def test_query_tenant_id_not_found(monkeypatch):
-    with set_temporary_config({"cloud.agent.auth_token": "token"}):
-        post = MagicMock(
-            return_value=MagicMock(
-                json=MagicMock(return_value=dict(data=dict(tenant=[])))
-            )
+def test_query_tenant_id(monkeypatch, runner_token):
+    post = MagicMock(
+        return_value=MagicMock(
+            json=MagicMock(return_value=dict(data=dict(tenant=[dict(id="id")])))
         )
-        session = MagicMock()
-        session.return_value.post = post
-        monkeypatch.setattr("requests.Session", session)
+    )
+    session = MagicMock()
+    session.return_value.post = post
+    monkeypatch.setattr("requests.Session", session)
 
-        agent = Agent()
-        tenant_id = agent.query_tenant_id()
-        assert not tenant_id
-
-
-def test_query_flow_runs(monkeypatch):
-    with set_temporary_config({"cloud.agent.auth_token": "token"}):
-        gql_return = MagicMock(
-            return_value=MagicMock(
-                data=MagicMock(
-                    getRunsInQueue=MagicMock(flow_run_ids=["id"]),
-                    flow_run=[{"id": "id"}],
-                )
-            )
-        )
-        client = MagicMock()
-        client.return_value.graphql = gql_return
-        monkeypatch.setattr("prefect.agent.agent.Client", client)
-
-        agent = Agent()
-        flow_runs = agent.query_flow_runs(tenant_id="id")
-        assert flow_runs == [{"id": "id"}]
+    agent = Agent()
+    tenant_id = agent.query_tenant_id()
+    assert tenant_id == "id"
 
 
-def test_update_states_passes_empty(monkeypatch):
-    with set_temporary_config({"cloud.agent.auth_token": "token"}):
-        gql_return = MagicMock(
-            return_value=MagicMock(
-                data=MagicMock(set_flow_run_state=None, set_task_run_state=None)
-            )
-        )
-        client = MagicMock()
-        client.return_value.graphql = gql_return
-        monkeypatch.setattr("prefect.agent.agent.Client", client)
+def test_query_tenant_id_not_found(monkeypatch, runner_token):
+    post = MagicMock(
+        return_value=MagicMock(json=MagicMock(return_value=dict(data=dict(tenant=[]))))
+    )
+    session = MagicMock()
+    session.return_value.post = post
+    monkeypatch.setattr("requests.Session", session)
 
-        agent = Agent()
-        assert not agent.update_states(flow_runs=[])
+    agent = Agent()
+    tenant_id = agent.query_tenant_id()
+    assert not tenant_id
 
 
-def test_update_states_passes_no_task_runs(monkeypatch):
-    with set_temporary_config({"cloud.agent.auth_token": "token"}):
-        gql_return = MagicMock(
-            return_value=MagicMock(
-                data=MagicMock(set_flow_run_state=None, set_task_run_state=None)
+def test_query_flow_runs(monkeypatch, runner_token):
+    gql_return = MagicMock(
+        return_value=MagicMock(
+            data=MagicMock(
+                getRunsInQueue=MagicMock(flow_run_ids=["id"]), flow_run=[{"id": "id"}]
             )
         )
-        client = MagicMock()
-        client.return_value.graphql = gql_return
-        monkeypatch.setattr("prefect.agent.agent.Client", client)
-
-        agent = Agent()
-        assert not agent.update_states(
-            flow_runs=[
-                GraphQLResult(
-                    {
-                        "id": "id",
-                        "serialized_state": Scheduled().serialize(),
-                        "version": 1,
-                        "task_runs": [],
-                    }
-                )
-            ]
-        )
+    )
+    client = MagicMock()
+    client.return_value.graphql = gql_return
+    monkeypatch.setattr("prefect.agent.agent.Client", client)
 
+    agent = Agent()
+    flow_runs = agent.query_flow_runs(tenant_id="id")
+    assert flow_runs == [{"id": "id"}]
 
-def test_update_states_passes_task_runs(monkeypatch):
-    with set_temporary_config({"cloud.agent.auth_token": "token"}):
-        gql_return = MagicMock(
-            return_value=MagicMock(
-                data=MagicMock(set_flow_run_state=None, set_task_run_state=None)
-            )
-        )
-        client = MagicMock()
-        client.return_value.graphql = gql_return
-        monkeypatch.setattr("prefect.agent.agent.Client", client)
 
-        agent = Agent()
-        assert not agent.update_states(
-            flow_runs=[
-                GraphQLResult(
-                    {
-                        "id": "id",
-                        "serialized_state": Scheduled().serialize(),
-                        "version": 1,
-                        "task_runs": [
-                            GraphQLResult(
-                                {
-                                    "id": "id",
-                                    "version": 1,
-                                    "serialized_state": Scheduled().serialize(),
-                                }
-                            )
-                        ],
-                    }
-                )
-            ]
+def test_update_states_passes_empty(monkeypatch, runner_token):
+    gql_return = MagicMock(
+        return_value=MagicMock(
+            data=MagicMock(set_flow_run_state=None, set_task_run_state=None)
         )
+    )
+    client = MagicMock()
+    client.return_value.graphql = gql_return
+    monkeypatch.setattr("prefect.agent.agent.Client", client)
 
-
-def test_deploy_flows_passes_base_agent():
-    with set_temporary_config({"cloud.agent.auth_token": "token"}):
-        agent = Agent()
-        assert not agent.deploy_flows([])
+    agent = Agent()
+    assert not agent.update_states(flow_runs=[])
 
 
-def test_agent_connect(monkeypatch):
-    with set_temporary_config({"cloud.agent.auth_token": "token"}):
-        post = MagicMock(
-            return_value=MagicMock(
-                json=MagicMock(return_value=dict(data=dict(tenant=[dict(id="id")])))
-            )
+def test_update_states_passes_no_task_runs(monkeypatch, runner_token):
+    gql_return = MagicMock(
+        return_value=MagicMock(
+            data=MagicMock(set_flow_run_state=None, set_task_run_state=None)
         )
-        session = MagicMock()
-        session.return_value.post = post
-        monkeypatch.setattr("requests.Session", session)
+    )
+    client = MagicMock()
+    client.return_value.graphql = gql_return
+    monkeypatch.setattr("prefect.agent.agent.Client", client)
 
-        agent = Agent()
-        assert agent.agent_connect() == "id"
+    agent = Agent()
+    assert not agent.update_states(
+        flow_runs=[
+            GraphQLResult(
+                {
+                    "id": "id",
+                    "serialized_state": Scheduled().serialize(),
+                    "version": 1,
+                    "task_runs": [],
+                }
+            )
+        ]
+    )
 
 
-def test_agent_connect_no_tenant_id(monkeypatch):
-    with set_temporary_config({"cloud.agent.auth_token": "token"}):
-        post = MagicMock(
-            return_value=MagicMock(
-                json=MagicMock(return_value=dict(data=dict(tenant=[dict(id=None)])))
-            )
+def test_update_states_passes_task_runs(monkeypatch, runner_token):
+    gql_return = MagicMock(
+        return_value=MagicMock(
+            data=MagicMock(set_flow_run_state=None, set_task_run_state=None)
         )
-        session = MagicMock()
-        session.return_value.post = post
-        monkeypatch.setattr("requests.Session", session)
+    )
+    client = MagicMock()
+    client.return_value.graphql = gql_return
+    monkeypatch.setattr("prefect.agent.agent.Client", client)
 
-        agent = Agent()
-        with pytest.raises(ConnectionError):
-            assert agent.agent_connect()
-
-
-def test_agent_process(monkeypatch):
-    with set_temporary_config({"cloud.agent.auth_token": "token"}):
-        gql_return = MagicMock(
-            return_value=MagicMock(
-                data=MagicMock(
-                    set_flow_run_state=None,
-                    set_task_run_state=None,
-                    getRunsInQueue=MagicMock(flow_run_ids=["id"]),
-                    flow_run=[
+    agent = Agent()
+    assert not agent.update_states(
+        flow_runs=[
+            GraphQLResult(
+                {
+                    "id": "id",
+                    "serialized_state": Scheduled().serialize(),
+                    "version": 1,
+                    "task_runs": [
                         GraphQLResult(
                             {
                                 "id": "id",
-                                "serialized_state": Scheduled().serialize(),
                                 "version": 1,
-                                "task_runs": [
-                                    GraphQLResult(
-                                        {
-                                            "id": "id",
-                                            "version": 1,
-                                            "serialized_state": Scheduled().serialize(),
-                                        }
-                                    )
-                                ],
+                                "serialized_state": Scheduled().serialize(),
                             }
                         )
                     ],
-                )
+                }
             )
+        ]
+    )
+
+
+def test_deploy_flows_passes_base_agent(runner_token):
+    agent = Agent()
+    assert not agent.deploy_flows([])
+
+
+def test_agent_connect(monkeypatch, runner_token):
+    post = MagicMock(
+        return_value=MagicMock(
+            json=MagicMock(return_value=dict(data=dict(tenant=[dict(id="id")])))
         )
-        client = MagicMock()
-        client.return_value.graphql = gql_return
-        monkeypatch.setattr("prefect.agent.agent.Client", client)
+    )
+    session = MagicMock()
+    session.return_value.post = post
+    monkeypatch.setattr("requests.Session", session)
 
-        # Assert it doesn't return everything but all functions are called properly
-        agent = Agent()
-        assert agent.agent_process("id")
-
-
-def test_agent_process_no_runs_found(monkeypatch):
-    with set_temporary_config({"cloud.agent.auth_token": "token"}):
-        gql_return = MagicMock(
-            return_value=MagicMock(
-                data=MagicMock(
-                    set_flow_run_state=None,
-                    set_task_run_state=None,
-                    getRunsInQueue=MagicMock(flow_run_ids=["id"]),
-                    flow_run=[],
-                )
+    agent = Agent()
+    assert agent.agent_connect() == "id"
+
+
+def test_agent_connect_no_tenant_id(monkeypatch, runner_token):
+    post = MagicMock(
+        return_value=MagicMock(
+            json=MagicMock(return_value=dict(data=dict(tenant=[dict(id=None)])))
+        )
+    )
+    session = MagicMock()
+    session.return_value.post = post
+    monkeypatch.setattr("requests.Session", session)
+
+    agent = Agent()
+    with pytest.raises(ConnectionError):
+        assert agent.agent_connect()
+
+
+def test_agent_process(monkeypatch, runner_token):
+    gql_return = MagicMock(
+        return_value=MagicMock(
+            data=MagicMock(
+                set_flow_run_state=None,
+                set_task_run_state=None,
+                getRunsInQueue=MagicMock(flow_run_ids=["id"]),
+                flow_run=[
+                    GraphQLResult(
+                        {
+                            "id": "id",
+                            "serialized_state": Scheduled().serialize(),
+                            "version": 1,
+                            "task_runs": [
+                                GraphQLResult(
+                                    {
+                                        "id": "id",
+                                        "version": 1,
+                                        "serialized_state": Scheduled().serialize(),
+                                    }
+                                )
+                            ],
+                        }
+                    )
+                ],
             )
         )
-        client = MagicMock()
-        client.return_value.graphql = gql_return
-        monkeypatch.setattr("prefect.agent.agent.Client", client)
+    )
+    client = MagicMock()
+    client.return_value.graphql = gql_return
+    monkeypatch.setattr("prefect.agent.agent.Client", client)
 
-        # Assert it doesn't return everything but all functions are called properly
-        agent = Agent()
-        assert not agent.agent_process("id")
+    # Assert it doesn't return everything but all functions are called properly
+    agent = Agent()
+    assert agent.agent_process("id")
+
+
+def test_agent_process_no_runs_found(monkeypatch, runner_token):
+    gql_return = MagicMock(
+        return_value=MagicMock(
+            data=MagicMock(
+                set_flow_run_state=None,
+                set_task_run_state=None,
+                getRunsInQueue=MagicMock(flow_run_ids=["id"]),
+                flow_run=[],
+            )
+        )
+    )
+    client = MagicMock()
+    client.return_value.graphql = gql_return
+    monkeypatch.setattr("prefect.agent.agent.Client", client)
+
+    # Assert it doesn't return everything but all functions are called properly
+    agent = Agent()
+    assert not agent.agent_process("id")
diff --git a/tests/agent/test_k8s_agent.py b/tests/agent/test_k8s_agent.py
--- a/tests/agent/test_k8s_agent.py
+++ b/tests/agent/test_k8s_agent.py
@@ -12,7 +12,7 @@
 from prefect.utilities.graphql import GraphQLResult
 
 
-def test_k8s_agent_init(monkeypatch):
+def test_k8s_agent_init(monkeypatch, runner_token):
     k8s_config = MagicMock()
     monkeypatch.setattr("kubernetes.config", k8s_config)
 
@@ -21,7 +21,7 @@ def test_k8s_agent_init(monkeypatch):
     assert agent.batch_client
 
 
-def test_k8s_agent_config_options(monkeypatch):
+def test_k8s_agent_config_options(monkeypatch, runner_token):
     k8s_config = MagicMock()
     monkeypatch.setattr("kubernetes.config", k8s_config)
 
@@ -33,7 +33,7 @@ def test_k8s_agent_config_options(monkeypatch):
         assert agent.batch_client
 
 
-def test_k8s_agent_deploy_flows(monkeypatch):
+def test_k8s_agent_deploy_flows(monkeypatch, runner_token):
     k8s_config = MagicMock()
     monkeypatch.setattr("kubernetes.config", k8s_config)
 
@@ -43,27 +43,24 @@ def test_k8s_agent_deploy_flows(monkeypatch):
         "kubernetes.client.BatchV1Api", MagicMock(retrurn_value=batch_client)
     )
 
-    with set_temporary_config({"cloud.agent.auth_token": "token"}):
-        agent = KubernetesAgent()
-        agent.deploy_flows(
-            flow_runs=[
-                GraphQLResult(
-                    {
-                        "flow": GraphQLResult(
-                            {
-                                "storage": Docker(
-                                    registry_url="test",
-                                    image_name="name",
-                                    image_tag="tag",
-                                ).serialize(),
-                                "id": "id",
-                            }
-                        ),
-                        "id": "id",
-                    }
-                )
-            ]
-        )
+    agent = KubernetesAgent()
+    agent.deploy_flows(
+        flow_runs=[
+            GraphQLResult(
+                {
+                    "flow": GraphQLResult(
+                        {
+                            "storage": Docker(
+                                registry_url="test", image_name="name", image_tag="tag"
+                            ).serialize(),
+                            "id": "id",
+                        }
+                    ),
+                    "id": "id",
+                }
+            )
+        ]
+    )
 
     assert agent.batch_client.create_namespaced_job.called
     assert (
@@ -75,7 +72,7 @@ def test_k8s_agent_deploy_flows(monkeypatch):
     )
 
 
-def test_k8s_agent_deploy_flows_continues(monkeypatch):
+def test_k8s_agent_deploy_flows_continues(monkeypatch, runner_token):
     k8s_config = MagicMock()
     monkeypatch.setattr("kubernetes.config", k8s_config)
 
@@ -85,45 +82,42 @@ def test_k8s_agent_deploy_flows_continues(monkeypatch):
         "kubernetes.client.BatchV1Api", MagicMock(retrurn_value=batch_client)
     )
 
-    with set_temporary_config({"cloud.agent.auth_token": "token"}):
-        agent = KubernetesAgent()
-        agent.deploy_flows(
-            flow_runs=[
-                GraphQLResult(
-                    {
-                        "flow": GraphQLResult(
-                            {"storage": Local().serialize(), "id": "id"}
-                        ),
-                        "id": "id",
-                    }
-                )
-            ]
-        )
+    agent = KubernetesAgent()
+    agent.deploy_flows(
+        flow_runs=[
+            GraphQLResult(
+                {
+                    "flow": GraphQLResult({"storage": Local().serialize(), "id": "id"}),
+                    "id": "id",
+                }
+            )
+        ]
+    )
 
     assert not agent.batch_client.create_namespaced_job.called
 
 
-def test_k8s_agent_replace_yaml(monkeypatch):
+def test_k8s_agent_replace_yaml(monkeypatch, runner_token):
     k8s_config = MagicMock()
     monkeypatch.setattr("kubernetes.config", k8s_config)
 
     monkeypatch.setenv("IMAGE_PULL_SECRETS", "my-secret")
 
-    with set_temporary_config({"cloud.agent.auth_token": "token"}):
-        flow_run = GraphQLResult(
-            {
-                "flow": GraphQLResult(
-                    {
-                        "storage": Docker(
-                            registry_url="test", image_name="name", image_tag="tag"
-                        ).serialize(),
-                        "id": "id",
-                    }
-                ),
-                "id": "id",
-            }
-        )
+    flow_run = GraphQLResult(
+        {
+            "flow": GraphQLResult(
+                {
+                    "storage": Docker(
+                        registry_url="test", image_name="name", image_tag="tag"
+                    ).serialize(),
+                    "id": "id",
+                }
+            ),
+            "id": "id",
+        }
+    )
 
+    with set_temporary_config({"cloud.agent.auth_token": "token"}):
         agent = KubernetesAgent()
         job = agent.replace_job_spec_yaml(flow_run)
 
@@ -147,76 +141,75 @@ def test_k8s_agent_replace_yaml(monkeypatch):
         )
 
 
-def test_k8s_agent_replace_yaml_no_pull_secrets(monkeypatch):
+def test_k8s_agent_replace_yaml_no_pull_secrets(monkeypatch, runner_token):
     k8s_config = MagicMock()
     monkeypatch.setattr("kubernetes.config", k8s_config)
 
-    with set_temporary_config({"cloud.agent.auth_token": "token"}):
-        flow_run = GraphQLResult(
-            {
-                "flow": GraphQLResult(
-                    {
-                        "storage": Docker(
-                            registry_url="test", image_name="name", image_tag="tag"
-                        ).serialize(),
-                        "id": "id",
-                    }
-                ),
-                "id": "id",
-            }
-        )
+    flow_run = GraphQLResult(
+        {
+            "flow": GraphQLResult(
+                {
+                    "storage": Docker(
+                        registry_url="test", image_name="name", image_tag="tag"
+                    ).serialize(),
+                    "id": "id",
+                }
+            ),
+            "id": "id",
+        }
+    )
 
-        agent = KubernetesAgent()
-        job = agent.replace_job_spec_yaml(flow_run)
+    agent = KubernetesAgent()
+    job = agent.replace_job_spec_yaml(flow_run)
 
-        assert not job["spec"]["template"]["spec"]["imagePullSecrets"][0]["name"]
+    assert not job["spec"]["template"]["spec"]["imagePullSecrets"][0]["name"]
 
 
-def test_k8s_agent_generate_deployment_yaml(monkeypatch):
+def test_k8s_agent_generate_deployment_yaml(monkeypatch, runner_token):
     k8s_config = MagicMock()
     monkeypatch.setattr("kubernetes.config", k8s_config)
 
-    with set_temporary_config({"cloud.agent.auth_token": "token"}):
-        agent = KubernetesAgent()
-        deployment = agent.generate_deployment_yaml(
-            token="test_token",
-            api="test_api",
-            namespace="test_namespace",
-            resource_manager_enabled=True,
-        )
+    agent = KubernetesAgent()
+    deployment = agent.generate_deployment_yaml(
+        token="test_token",
+        api="test_api",
+        namespace="test_namespace",
+        resource_manager_enabled=True,
+    )
 
-        deployment = yaml.safe_load(deployment)
+    deployment = yaml.safe_load(deployment)
 
-        agent_env = deployment["spec"]["template"]["spec"]["containers"][0]["env"]
-        resource_manager_env = deployment["spec"]["template"]["spec"]["containers"][1][
-            "env"
-        ]
+    agent_env = deployment["spec"]["template"]["spec"]["containers"][0]["env"]
+    resource_manager_env = deployment["spec"]["template"]["spec"]["containers"][1][
+        "env"
+    ]
 
-        assert agent_env[0]["value"] == "test_token"
-        assert agent_env[1]["value"] == "test_api"
-        assert agent_env[2]["value"] == "test_namespace"
+    assert agent_env[0]["value"] == "test_token"
+    assert agent_env[1]["value"] == "test_api"
+    assert agent_env[2]["value"] == "test_namespace"
 
-        assert resource_manager_env[0]["value"] == "test_token"
-        assert resource_manager_env[1]["value"] == "test_api"
-        assert resource_manager_env[3]["value"] == "test_namespace"
+    assert resource_manager_env[0]["value"] == "test_token"
+    assert resource_manager_env[1]["value"] == "test_api"
+    assert resource_manager_env[3]["value"] == "test_namespace"
 
 
-def test_k8s_agent_generate_deployment_yaml_no_resource_manager(monkeypatch):
+def test_k8s_agent_generate_deployment_yaml_no_resource_manager(
+    monkeypatch, runner_token
+):
     k8s_config = MagicMock()
     monkeypatch.setattr("kubernetes.config", k8s_config)
 
-    with set_temporary_config({"cloud.agent.auth_token": "token"}):
-        agent = KubernetesAgent()
-        deployment = agent.generate_deployment_yaml(
-            token="test_token", api="test_api", namespace="test_namespace"
-        )
+    agent = KubernetesAgent()
+    deployment = agent.generate_deployment_yaml(
+        token="test_token", api="test_api", namespace="test_namespace"
+    )
 
-        deployment = yaml.safe_load(deployment)
+    deployment = yaml.safe_load(deployment)
 
-        agent_env = deployment["spec"]["template"]["spec"]["containers"][0]["env"]
+    agent_env = deployment["spec"]["template"]["spec"]["containers"][0]["env"]
 
-        assert agent_env[0]["value"] == "test_token"
-        assert agent_env[1]["value"] == "test_api"
-        assert agent_env[2]["value"] == "test_namespace"
+    assert agent_env[0]["value"] == "test_token"
+    assert agent_env[1]["value"] == "test_api"
+    assert agent_env[2]["value"] == "test_namespace"
 
-        assert len(deployment["spec"]["template"]["spec"]["containers"]) == 1
+    assert len(deployment["spec"]["template"]["spec"]["containers"]) == 1
diff --git a/tests/agent/test_local_agent.py b/tests/agent/test_local_agent.py
--- a/tests/agent/test_local_agent.py
+++ b/tests/agent/test_local_agent.py
@@ -9,7 +9,7 @@
 from prefect.utilities.graphql import GraphQLResult
 
 
-def test_local_agent_init(monkeypatch):
+def test_local_agent_init(monkeypatch, runner_token):
     api = MagicMock()
     monkeypatch.setattr("prefect.agent.local.agent.docker.APIClient", api)
 
@@ -17,7 +17,7 @@ def test_local_agent_init(monkeypatch):
     assert agent
 
 
-def test_local_agent_config_options(monkeypatch):
+def test_local_agent_config_options(monkeypatch, runner_token):
     api = MagicMock()
     monkeypatch.setattr("prefect.agent.local.agent.docker.APIClient", api)
     monkeypatch.setattr("prefect.agent.local.agent.platform", "osx")
@@ -30,7 +30,7 @@ def test_local_agent_config_options(monkeypatch):
         assert api.call_args[1]["base_url"] == "unix://var/run/docker.sock"
 
 
-def test_local_agent_daemon_url_responds_to_system(monkeypatch):
+def test_local_agent_daemon_url_responds_to_system(monkeypatch, runner_token):
     api = MagicMock()
     monkeypatch.setattr("prefect.agent.local.agent.docker.APIClient", api)
     monkeypatch.setattr("prefect.agent.local.agent.platform", "win32")
@@ -43,7 +43,7 @@ def test_local_agent_daemon_url_responds_to_system(monkeypatch):
         assert api.call_args[1]["base_url"] == "npipe:////./pipe/docker_engine"
 
 
-def test_local_agent_config_options_populated(monkeypatch):
+def test_local_agent_config_options_populated(monkeypatch, runner_token):
     api = MagicMock()
     monkeypatch.setattr("prefect.agent.local.agent.docker.APIClient", api)
 
@@ -55,43 +55,41 @@ def test_local_agent_config_options_populated(monkeypatch):
         assert api.call_args[1]["base_url"] == "url"
 
 
-def test_local_agent_no_pull(monkeypatch):
+def test_local_agent_no_pull(monkeypatch, runner_token):
     api = MagicMock()
     monkeypatch.setattr("prefect.agent.local.agent.docker.APIClient", api)
 
-    with set_temporary_config({"cloud.agent.auth_token": "token"}):
+    agent = LocalAgent()
+    assert not agent.no_pull
+
+    agent = LocalAgent(no_pull=True)
+    assert agent.no_pull
+
+    with context(no_pull=True):
         agent = LocalAgent()
-        assert not agent.no_pull
+        assert agent.no_pull
 
+    with context(no_pull=False):
         agent = LocalAgent(no_pull=True)
         assert agent.no_pull
 
-        with context(no_pull=True):
-            agent = LocalAgent()
-            assert agent.no_pull
-
-        with context(no_pull=False):
-            agent = LocalAgent(no_pull=True)
-            assert agent.no_pull
-
-        with context(no_pull=False):
-            agent = LocalAgent(no_pull=False)
-            assert not agent.no_pull
+    with context(no_pull=False):
+        agent = LocalAgent(no_pull=False)
+        assert not agent.no_pull
 
 
-def test_local_agent_ping(monkeypatch):
+def test_local_agent_ping(monkeypatch, runner_token):
     api = MagicMock()
     api.ping.return_value = True
     monkeypatch.setattr(
         "prefect.agent.local.agent.docker.APIClient", MagicMock(return_value=api)
     )
 
-    with set_temporary_config({"cloud.agent.auth_token": "token"}):
-        agent = LocalAgent()
-        assert api.ping.called
+    agent = LocalAgent()
+    assert api.ping.called
 
 
-def test_local_agent_ping_exception(monkeypatch):
+def test_local_agent_ping_exception(monkeypatch, runner_token):
     api = MagicMock()
     api.ping.return_value = True
     api.ping.side_effect = Exception()
@@ -99,12 +97,11 @@ def test_local_agent_ping_exception(monkeypatch):
         "prefect.agent.local.agent.docker.APIClient", MagicMock(return_value=api)
     )
 
-    with set_temporary_config({"cloud.agent.auth_token": "token"}):
-        with pytest.raises(Exception):
-            agent = LocalAgent()
+    with pytest.raises(Exception):
+        agent = LocalAgent()
 
 
-def test_populate_env_vars(monkeypatch):
+def test_populate_env_vars(monkeypatch, runner_token):
     api = MagicMock()
     monkeypatch.setattr("prefect.agent.local.agent.docker.APIClient", api)
 
@@ -127,7 +124,7 @@ def test_populate_env_vars(monkeypatch):
         assert env_vars == expected_vars
 
 
-def test_local_agent_deploy_flows(monkeypatch):
+def test_local_agent_deploy_flows(monkeypatch, runner_token):
 
     api = MagicMock()
     api.ping.return_value = True
@@ -136,38 +133,33 @@ def test_local_agent_deploy_flows(monkeypatch):
         "prefect.agent.local.agent.docker.APIClient", MagicMock(return_value=api)
     )
 
-    with set_temporary_config({"cloud.agent.auth_token": "token"}):
-        agent = LocalAgent()
-        agent.deploy_flows(
-            flow_runs=[
-                GraphQLResult(
-                    {
-                        "flow": GraphQLResult(
-                            {
-                                "storage": Docker(
-                                    registry_url="test",
-                                    image_name="name",
-                                    image_tag="tag",
-                                ).serialize()
-                            }
-                        ),
-                        "id": "id",
-                    }
-                )
-            ]
-        )
-
-        assert api.pull.called
-        assert api.create_container.called
-        assert api.start.called
-
-        assert (
-            api.create_container.call_args[1]["command"] == "prefect execute cloud-flow"
-        )
-        assert api.start.call_args[1]["container"] == "container_id"
-
-
-def test_local_agent_deploy_flows_storage_continues(monkeypatch):
+    agent = LocalAgent()
+    agent.deploy_flows(
+        flow_runs=[
+            GraphQLResult(
+                {
+                    "flow": GraphQLResult(
+                        {
+                            "storage": Docker(
+                                registry_url="test", image_name="name", image_tag="tag"
+                            ).serialize()
+                        }
+                    ),
+                    "id": "id",
+                }
+            )
+        ]
+    )
+
+    assert api.pull.called
+    assert api.create_container.called
+    assert api.start.called
+
+    assert api.create_container.call_args[1]["command"] == "prefect execute cloud-flow"
+    assert api.start.call_args[1]["container"] == "container_id"
+
+
+def test_local_agent_deploy_flows_storage_continues(monkeypatch, runner_token):
 
     api = MagicMock()
     api.ping.return_value = True
@@ -176,23 +168,19 @@ def test_local_agent_deploy_flows_storage_continues(monkeypatch):
         "prefect.agent.local.agent.docker.APIClient", MagicMock(return_value=api)
     )
 
-    with set_temporary_config({"cloud.agent.auth_token": "token"}):
-        agent = LocalAgent()
-        agent.deploy_flows(
-            flow_runs=[
-                GraphQLResult(
-                    {
-                        "flow": GraphQLResult({"storage": Local().serialize()}),
-                        "id": "id",
-                    }
-                )
-            ]
-        )
+    agent = LocalAgent()
+    agent.deploy_flows(
+        flow_runs=[
+            GraphQLResult(
+                {"flow": GraphQLResult({"storage": Local().serialize()}), "id": "id"}
+            )
+        ]
+    )
 
-        assert not api.pull.called
+    assert not api.pull.called
 
 
-def test_local_agent_deploy_flows_no_pull(monkeypatch):
+def test_local_agent_deploy_flows_no_pull(monkeypatch, runner_token):
 
     api = MagicMock()
     api.ping.return_value = True
@@ -201,27 +189,24 @@ def test_local_agent_deploy_flows_no_pull(monkeypatch):
         "prefect.agent.local.agent.docker.APIClient", MagicMock(return_value=api)
     )
 
-    with set_temporary_config({"cloud.agent.auth_token": "token"}):
-        agent = LocalAgent(no_pull=True)
-        agent.deploy_flows(
-            flow_runs=[
-                GraphQLResult(
-                    {
-                        "flow": GraphQLResult(
-                            {
-                                "storage": Docker(
-                                    registry_url="test",
-                                    image_name="name",
-                                    image_tag="tag",
-                                ).serialize()
-                            }
-                        ),
-                        "id": "id",
-                    }
-                )
-            ]
-        )
-
-        assert not api.pull.called
-        assert api.create_container.called
-        assert api.start.called
+    agent = LocalAgent(no_pull=True)
+    agent.deploy_flows(
+        flow_runs=[
+            GraphQLResult(
+                {
+                    "flow": GraphQLResult(
+                        {
+                            "storage": Docker(
+                                registry_url="test", image_name="name", image_tag="tag"
+                            ).serialize()
+                        }
+                    ),
+                    "id": "id",
+                }
+            )
+        ]
+    )
+
+    assert not api.pull.called
+    assert api.create_container.called
+    assert api.start.called
diff --git a/tests/agent/test_nomad_agent.py b/tests/agent/test_nomad_agent.py
--- a/tests/agent/test_nomad_agent.py
+++ b/tests/agent/test_nomad_agent.py
@@ -8,12 +8,12 @@
 from prefect.utilities.graphql import GraphQLResult
 
 
-def test_nomad_agent_init():
+def test_nomad_agent_init(runner_token):
     agent = NomadAgent()
     assert agent
 
 
-def test_nomad_agent_config_options():
+def test_nomad_agent_config_options(runner_token):
     with set_temporary_config({"cloud.agent.auth_token": "TEST_TOKEN"}):
         agent = NomadAgent()
         assert agent
@@ -21,59 +21,53 @@ def test_nomad_agent_config_options():
         assert agent.logger
 
 
-def test_nomad_agent_deploy_flows(monkeypatch):
+def test_nomad_agent_deploy_flows(monkeypatch, runner_token):
     requests = MagicMock()
     monkeypatch.setattr("prefect.agent.nomad.agent.requests", requests)
 
-    with set_temporary_config({"cloud.agent.auth_token": "token"}):
-        agent = NomadAgent()
-        agent.deploy_flows(
-            flow_runs=[
-                GraphQLResult(
-                    {
-                        "flow": GraphQLResult(
-                            {
-                                "storage": Docker(
-                                    registry_url="test",
-                                    image_name="name",
-                                    image_tag="tag",
-                                ).serialize(),
-                                "id": "id",
-                            }
-                        ),
-                        "id": "id",
-                    }
-                )
-            ]
-        )
+    agent = NomadAgent()
+    agent.deploy_flows(
+        flow_runs=[
+            GraphQLResult(
+                {
+                    "flow": GraphQLResult(
+                        {
+                            "storage": Docker(
+                                registry_url="test", image_name="name", image_tag="tag"
+                            ).serialize(),
+                            "id": "id",
+                        }
+                    ),
+                    "id": "id",
+                }
+            )
+        ]
+    )
 
     assert requests.post.called
     assert requests.post.call_args[1]["json"]
 
 
-def test_nomad_agent_deploy_flows_continues(monkeypatch):
+def test_nomad_agent_deploy_flows_continues(monkeypatch, runner_token):
     requests = MagicMock()
     monkeypatch.setattr("prefect.agent.nomad.agent.requests", requests)
 
-    with set_temporary_config({"cloud.agent.auth_token": "token"}):
-        agent = NomadAgent()
-        agent.deploy_flows(
-            flow_runs=[
-                GraphQLResult(
-                    {
-                        "flow": GraphQLResult(
-                            {"storage": Local().serialize(), "id": "id"}
-                        ),
-                        "id": "id",
-                    }
-                )
-            ]
-        )
+    agent = NomadAgent()
+    agent.deploy_flows(
+        flow_runs=[
+            GraphQLResult(
+                {
+                    "flow": GraphQLResult({"storage": Local().serialize(), "id": "id"}),
+                    "id": "id",
+                }
+            )
+        ]
+    )
 
     assert not requests.post.called
 
 
-def test_nomad_agent_replace_yaml():
+def test_nomad_agent_replace_yaml(runner_token):
     with set_temporary_config({"cloud.agent.auth_token": "token"}):
         flow_run = GraphQLResult(
             {
diff --git a/tests/cli/test_agent.py b/tests/cli/test_agent.py
--- a/tests/cli/test_agent.py
+++ b/tests/cli/test_agent.py
@@ -19,7 +19,7 @@ def test_agent_help():
     assert "Manage Prefect agents." in result.output
 
 
-def test_agent_start(monkeypatch):
+def test_agent_start_fails_no_token(monkeypatch):
     start = MagicMock()
     monkeypatch.setattr("prefect.agent.local.LocalAgent.start", start)
 
@@ -28,10 +28,10 @@ def test_agent_start(monkeypatch):
 
     runner = CliRunner()
     result = runner.invoke(agent, ["start"])
-    assert result.exit_code == 0
+    assert result.exit_code == 1
 
 
-def test_agent_start_token(monkeypatch):
+def test_agent_start_token(monkeypatch, runner_token):
     start = MagicMock()
     monkeypatch.setattr("prefect.agent.local.LocalAgent.start", start)
 
@@ -43,7 +43,7 @@ def test_agent_start_token(monkeypatch):
     assert result.exit_code == 0
 
 
-def test_agent_start_local_context_vars(monkeypatch):
+def test_agent_start_local_context_vars(monkeypatch, runner_token):
     start = MagicMock()
     monkeypatch.setattr("prefect.agent.local.LocalAgent.start", start)
 
diff --git a/tests/conftest.py b/tests/conftest.py
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -81,7 +81,7 @@ def executor(request, _switch):
     return _switch(request.param)
 
 
-@pytest.fixture
+@pytest.fixture()
 def patch_post(monkeypatch):
     """
     Patches `prefect.client.Client.post()` (and `graphql()`) to return the specified response.
@@ -99,3 +99,8 @@ def patch(response):
         return post
 
     return patch
+
+
+@pytest.fixture()
+def runner_token(monkeypatch):
+    monkeypatch.setattr("prefect.agent.agent.Agent._verify_token", MagicMock())