fcm-v1-token
v1.0.0
Published
Get an FCM v1 OAuth access token from a Firebase service account (cached ~1h, auto-refreshed) and send push notifications. CLI + library for Firebase Cloud Messaging HTTP v1 API.
Downloads
162
Maintainers
Readme
🔥 fcm-v1-token
Get an FCM v1 OAuth access token from a Firebase service account — and send push notifications — in seconds.
A tiny CLI + Node.js library that turns a Firebase service account key into a short‑lived OAuth 2.0 access token for the Firebase Cloud Messaging (FCM) HTTP v1 API. The token is cached for ~1 hour and auto‑refreshed, so you can send push notifications repeatedly without ever generating it by hand.
npx fcm-v1-tokenWhy fcm-v1-token?
The legacy FCM server key is deprecated. The modern FCM HTTP v1 API requires a short‑lived OAuth 2.0 Bearer token generated from a service account, and that token expires after 1 hour. Generating it by hand (sign a JWT, exchange it at oauth2.googleapis.com/token, remember to refresh) is fiddly.
fcm-v1-token does it for you — from the terminal or from code:
- 🔑 Any input — a service account file path, pasted JSON, or a credentials object
- 🧠 Remembers your service account between runs — set it once, never paste it again
- 🖥️ Interactive CLI — run it and pick what to do (get token / send push)
- 🔁 Caches the token ~1 hour and auto‑refreshes it before expiry
- 📋 Copies the token to your clipboard automatically (and prints it copy‑friendly)
- 📤 Sends push via the FCM v1 API with a one‑line helper
- 📦 Zero config, one dependency (
google-auth-library), works withnpx
Table of contents
- Install
- Quick start (CLI)
- CLI commands
- Use as a library
- How it works
- API reference
- Where is my account stored?
- Security
- FAQ
- License
Install
Run instantly, no install:
npx fcm-v1-tokenOr install globally to get the fcm-v1-token / fcm-token / fcmt commands:
npm install -g fcm-v1-tokenOr add it to a project as a library:
npm install fcm-v1-tokenQuick start (CLI)
Just run it. The first time, it asks for your service account (file path, pasted JSON, or auto‑detect). After that it remembers it.
Pick Get access token and you get a color‑coded token that's already on your clipboard:
Prefer scripting? Print just the token:
# Print the raw token using your saved account
fcm-token token --raw
# Capture it into a shell variable
TOKEN=$(fcm-token token --raw)
# Use it directly against the FCM v1 API
curl -X POST \
-H "Authorization: Bearer $(fcm-token token --raw)" \
-H "Content-Type: application/json" \
-d '{"message":{"token":"<device-token>","notification":{"title":"Hi","body":"Hello"}}}' \
"https://fcm.googleapis.com/v1/projects/<your-project-id>/messages:send"CLI commands
fcm-token Interactive menu (remembers your last account)
fcm-token token [source] Print an access token
fcm-token send [source] Send a test push (interactive prompts)
fcm-token use <source> Set / replace the saved service account
fcm-token whoami Show the saved service account
fcm-token forget Remove the saved service account
fcm-token --help Show help[source] is optional — if omitted, the saved account is used. It can be a file path (./service-account.json) or JSON content ('{"type":"service_account",...}').
Options for token:
| Flag | Description |
| --- | --- |
| --raw | Print only the token — no color, no clipboard (for scripting) |
| --json | Print full info (token, projectId, expiresAt) as JSON |
| --no-copy | Do not copy the token to the clipboard |
Set
NO_COLOR=1to disable colors. The short aliasfcmtworks everywhere, andnpx fcm-v1-tokenruns the same tool without installing.
Use as a library
Get an access token
const { getAccessToken } = require('fcm-v1-token');
const { token, projectId, expiresAt } = await getAccessToken('./service-account.json');
// Call getAccessToken() as often as you like — while valid, the same cached
// token is returned; it auto-refreshes when it expires (~1 hour).Send a push notification (FCM v1)
const { sendPush } = require('fcm-v1-token');
await sendPush('./service-account.json', {
token: '<device-registration-token>',
notification: { title: 'Hello', body: 'World' },
data: { orderId: '123' },
});Send to a topic:
await sendPush('./service-account.json', {
topic: 'news',
notification: { title: 'Breaking', body: 'Something happened' },
});Validate only (do not deliver):
await sendPush(serviceAccount, message, { dryRun: true });Pass content or an object instead of a file path
// From an environment variable (recommended for servers)
const sa = JSON.parse(process.env.FIREBASE_SERVICE_ACCOUNT);
await getAccessToken(sa);
// Or a pasted JSON string
await getAccessToken('{"type":"service_account", ...}');How it works
- Reads your service account's
client_emailandprivate_key. - Signs a JWT and exchanges it at
https://oauth2.googleapis.com/tokenfor an OAuth 2.0 access token, scoped tohttps://www.googleapis.com/auth/firebase.messaging. - Caches the token in memory and auto‑refreshes it before the ~1‑hour expiry (powered by
google-auth-library). - Uses the token as a
Bearercredential againsthttps://fcm.googleapis.com/v1/projects/<project-id>/messages:send.
No secrets ever leave your machine — the token is created and used locally.
API reference
| Function | Returns |
| --- | --- |
| getAccessToken(source) | { token, projectId, clientEmail, expiresAt } |
| sendPush(source, message, options?) | FCM v1 response object |
| loadServiceAccount(source) | validated service account object |
| saveLastAccount(sa, source) | persist an account for later runs → config path |
| loadLastAccount() | saved config, or null |
| savedSource(saved) | a usable source from a saved config |
| forgetLastAccount() | remove the saved account → boolean |
| clearCache() | clear the in‑memory token cache |
source— a file path (string), pasted JSON content (string), or a credentials object.options—{ projectId?, dryRun? }.
TypeScript types are bundled (src/index.d.ts).
Where is my account stored?
~/.fcm-v1-token/config.json (permissions 0600).
- If you passed a file path, only the absolute path is stored — your key stays in the file.
- If you pasted JSON content, that content is stored in the config file (there is no file to re‑read).
Run fcm-token forget to remove it.
🔒 Security
A service account private key grants access to your entire Firebase project. Treat it like a password:
- Never commit it to git — add
service-account*.jsonto.gitignore. - Never paste it into a chat, issue, or screenshot. If a key is ever exposed, rotate it immediately in the Google Cloud Console → Service Accounts (delete the leaked key, create a new one).
- On servers, prefer an environment variable or a secret manager over a file on disk.
FAQ
How do I get an FCM v1 API access token?
Run npx fcm-v1-token, choose your service account once, and pick Get access token — or in code, await getAccessToken('./service-account.json'). The token is a standard Google OAuth 2.0 Bearer token valid for ~1 hour.
How long does an FCM v1 access token last? About 1 hour (3600 seconds). This tool caches it and refreshes it automatically, so you never hit an expired‑token error while sending push.
How do I send an FCM v1 push notification in Node.js?
Use sendPush(source, message) — see Send a push notification. It posts to messages:send with the right Authorization: Bearer header for you.
Do I still need the legacy FCM server key? No. The legacy server key is deprecated. The HTTP v1 API uses OAuth 2.0 tokens from a service account, which is exactly what this tool produces.
Where do I get the service account JSON?
Firebase Console → Project settings → Service accounts → Generate new private key. That downloaded JSON is your source.
Does it work without installing?
Yes — npx fcm-v1-token runs it directly. Install globally only if you want the short fcmt command always available.
Which platforms are supported for clipboard copy?
macOS (pbcopy), Windows (clip), and Linux (xclip). If none is available, the token is still printed — copy it manually.
License
Keywords: FCM v1 access token · Firebase Cloud Messaging HTTP v1 API · OAuth 2.0 bearer token · service account to access token · send FCM push notification Node.js · firebase-admin alternative · FCM token CLI
