diff --git a/shared/grpc_clients/src/identity/authenticated.rs b/shared/grpc_clients/src/identity/authenticated.rs
--- a/shared/grpc_clients/src/identity/authenticated.rs
+++ b/shared/grpc_clients/src/identity/authenticated.rs
@@ -19,6 +19,10 @@
   access_token: String,
 }
 
+pub struct ServicesAuthLayer {
+  services_token: String,
+}
+
 impl ToMetadataValueAscii for str {
   fn parse_to_ascii(&self) -> Result<MetadataValue<Ascii>, Status> {
     self.parse().map_err(|_: InvalidMetadataValue| {
@@ -38,9 +42,25 @@
   }
 }
 
+impl Interceptor for ServicesAuthLayer {
+  fn call(&mut self, mut request: Request<()>) -> Result<Request<()>, Status> {
+    request
+      .metadata_mut()
+      .insert("services_token", self.services_token.parse_to_ascii()?);
+
+    Ok(request)
+  }
+}
+
 pub type ChainedInterceptedAuthClient = AuthClient<
   InterceptedService<Channel, ChainedInterceptor<AuthLayer, CodeVersionLayer>>,
 >;
+pub type ChainedInterceptedServicesAuthClient = AuthClient<
+  InterceptedService<
+    Channel,
+    ChainedInterceptor<ServicesAuthLayer, CodeVersionLayer>,
+  >,
+>;
 
 pub async fn get_auth_client(
   url: &str,
@@ -68,3 +88,24 @@
 
   Ok(AuthClient::with_interceptor(channel, chained))
 }
+
+pub async fn get_services_auth_client(
+  url: &str,
+  services_token: String,
+  platform_metadata: PlatformMetadata,
+) -> Result<ChainedInterceptedServicesAuthClient, Error> {
+  use crate::get_grpc_service_channel;
+
+  let channel = get_grpc_service_channel(url).await?;
+
+  let auth_interceptor = ServicesAuthLayer { services_token };
+
+  let version_interceptor = CodeVersionLayer::from(platform_metadata);
+
+  let chained = ChainedInterceptor {
+    first: auth_interceptor,
+    second: version_interceptor,
+  };
+
+  Ok(AuthClient::with_interceptor(channel, chained))
+}