diff --git a/services/tunnelbroker/src/notifs/fcm/token.rs b/services/tunnelbroker/src/notifs/fcm/token.rs
--- a/services/tunnelbroker/src/notifs/fcm/token.rs
+++ b/services/tunnelbroker/src/notifs/fcm/token.rs
@@ -9,6 +9,13 @@
 use tokio::sync::RwLock;
 use tracing::debug;
 
+#[derive(Debug, Clone, Deserialize)]
+struct FCMAccessTokenResponse {
+  access_token: String,
+  token_type: String,
+  expires_in: u64,
+}
+
 #[derive(Debug, Clone, Deserialize)]
 struct FCMAccessToken {
   access_token: String,
@@ -65,4 +72,21 @@
     let token = jsonwebtoken::encode(&header, &payload, &encoding_key)?;
     Ok(token)
   }
+
+  async fn get_fcm_access_token(
+    &self,
+    jwt_token: String,
+  ) -> Result<FCMAccessTokenResponse, Error> {
+    let response = reqwest::Client::new()
+      .post(self.config.token_uri.clone())
+      .form(&[
+        ("grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer"),
+        ("assertion", &jwt_token),
+      ])
+      .send()
+      .await?;
+
+    let access_token = response.json::<FCMAccessTokenResponse>().await?;
+    Ok(access_token)
+  }
 }