// Follow this setup guide to integrate the Deno language server with your editor:
// https://deno.land/manual/getting_started/setup_your_environment
// This enables autocomplete, go to definition, etc.
import { default as axiod } from "https://deno.land/x/axiod@0.26.2/mod.ts";
import * as djwt from "https://deno.land/x/djwt@v2.2/mod.ts";
const FIREBASE_PROJECT_ID="your-project-id"
const FIREBASE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n"
const FIREBASE_CLIENT_EMAIL="...@....iam.gserviceaccount.com"
export async function sendFirebaseMessageToDevice(
now: Date,
deviceToken: string,
payload: MessagingPayload
) {
const url = "https://fcm.googleapis.com/v1/projects/:project/messages:send".replace(
":project",
assertDefined(Deno.env.get("FIREBASE_PROJECT_ID"), "FIREBASE_PROJECT_ID")
);
const authToken = await getGoogleAccessToken(now);
return axiod.post(
url,
{
message: {
...payload,
token: deviceToken,
},
},
{
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${authToken}`,
},
}
);
}
async function getGoogleAccessToken(now: Date) {
const jwt = await djwt.create(
{ alg: "RS256", typ: "JWT" },
{
iss: Deno.env.get("FIREBASE_CLIENT_EMAIL"),
scope: "https://www.googleapis.com/auth/firebase.messaging",
aud: "https://oauth2.googleapis.com/token",
exp: now.setSeconds(3600) / 1000,
iat: now.getTime() / 1000,
},
assertDefined(Deno.env.get("FIREBASE_PRIVATE_KEY"), "FIREBASE_PRIVATE_KEY")
);
const token = await getOAuthToken(jwt);
return token.access_token;
}
async function getOAuthToken(jwt: string) {
const response = await axiod.post<{
access_token: string;
expires_in: number;
token_type: string;
}>(
"https://oauth2.googleapis.com/token",
`grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=${jwt}`,
{
headers: { "Content-Type": "application/x-www-form-urlencoded" },
}
);
return response.data;
}
export function isDefined<T>(value: T | undefined): value is NonNullable<T> {
return value !== undefined && value !== null;
}
export function assertDefined<T>(value: T | null | undefined, description: string): T {
if (isDefined(value)) {
return value;
}
throw new Error(`Expected defined value for "${description}", received ${value} instead.`);
}
export interface MessagingPayload {
/**
* The data message payload.
*/
data?: DataMessagePayload;
/**
* The notification message payload.
*/
notification?: NotificationMessagePayload;
}
export interface DataMessagePayload {
[key: string]: string;
}
/**
* Interface representing an FCM legacy API notification message payload.
* Notification messages let developers send up to 4KB of predefined
* key-value pairs. Accepted keys are outlined below.
*
* See {@link https://firebase.google.com/docs/cloud-messaging/send-message | Build send requests}
* for code samples and detailed documentation.
*/
export interface NotificationMessagePayload {
/**
* Identifier used to replace existing notifications in the notification drawer.
*
* If not specified, each request creates a new notification.
*
* If specified and a notification with the same tag is already being shown,
* the new notification replaces the existing one in the notification drawer.
*
* **Platforms:** Android
*/
tag?: string;
/**
* The notification's body text.
*
* **Platforms:** iOS, Android, Web
*/
body?: string;
/**
* The notification's icon.
*
* **Android:** Sets the notification icon to `myicon` for drawable resource
* `myicon`. If you don't send this key in the request, FCM displays the
* launcher icon specified in your app manifest.
*
* **Web:** The URL to use for the notification's icon.
*
* **Platforms:** Android, Web
*/
icon?: string;
/**
* The value of the badge on the home screen app icon.
*
* If not specified, the badge is not changed.
*
* If set to `0`, the badge is removed.
*
* **Platforms:** iOS
*/
badge?: string;
/**
* The notification icon's color, expressed in `#rrggbb` format.
*
* **Platforms:** Android
*/
color?: string;
/**
* The sound to be played when the device receives a notification. Supports
* "default" for the default notification sound of the device or the filename of a
* sound resource bundled in the app.
* Sound files must reside in `/res/raw/`.
*
* **Platforms:** Android
*/
sound?: string;
/**
* The notification's title.
*
* **Platforms:** iOS, Android, Web
*/
title?: string;
/**
* The key to the body string in the app's string resources to use to localize
* the body text to the user's current localization.
*
* **iOS:** Corresponds to `loc-key` in the APNs payload. See
* {@link https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/PayloadKeyReference.html |
* Payload Key Reference} and
* {@link https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CreatingtheNotificationPayload.html#//apple_ref/doc/uid/TP40008194-CH10-SW9 |
* Localizing the Content of Your Remote Notifications} for more information.
*
* **Android:** See
* {@link http://developer.android.com/guide/topics/resources/string-resource.html | String Resources}
* for more information.
*
* **Platforms:** iOS, Android
*/
bodyLocKey?: string;
/**
* Variable string values to be used in place of the format specifiers in
* `body_loc_key` to use to localize the body text to the user's current
* localization.
*
* The value should be a stringified JSON array.
*
* **iOS:** Corresponds to `loc-args` in the APNs payload. See
* {@link https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/PayloadKeyReference.html |
* Payload Key Reference} and
* {@link https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CreatingtheNotificationPayload.html#//apple_ref/doc/uid/TP40008194-CH10-SW9 |
* Localizing the Content of Your Remote Notifications} for more information.
*
* **Android:** See
* {@link http://developer.android.com/guide/topics/resources/string-resource.html#FormattingAndStyling |
* Formatting and Styling} for more information.
*
* **Platforms:** iOS, Android
*/
bodyLocArgs?: string;
/**
* Action associated with a user click on the notification. If specified, an
* activity with a matching Intent Filter is launched when a user clicks on the
* notification.
*
* * **Platforms:** Android
*/
clickAction?: string;
/**
* The key to the title string in the app's string resources to use to localize
* the title text to the user's current localization.
*
* **iOS:** Corresponds to `title-loc-key` in the APNs payload. See
* {@link https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/PayloadKeyReference.html |
* Payload Key Reference} and
* {@link https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CreatingtheNotificationPayload.html#//apple_ref/doc/uid/TP40008194-CH10-SW9 |
* Localizing the Content of Your Remote Notifications} for more information.
*
* **Android:** See
* {@link http://developer.android.com/guide/topics/resources/string-resource.html | String Resources}
* for more information.
*
* **Platforms:** iOS, Android
*/
titleLocKey?: string;
/**
* Variable string values to be used in place of the format specifiers in
* `title_loc_key` to use to localize the title text to the user's current
* localization.
*
* The value should be a stringified JSON array.
*
* **iOS:** Corresponds to `title-loc-args` in the APNs payload. See
* {@link https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/PayloadKeyReference.html |
* Payload Key Reference} and
* {@link https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CreatingtheNotificationPayload.html#//apple_ref/doc/uid/TP40008194-CH10-SW9 |
* Localizing the Content of Your Remote Notifications} for more information.
*
* **Android:** See
* {@link http://developer.android.com/guide/topics/resources/string-resource.html#FormattingAndStyling |
* Formatting and Styling} for more information.
*
* **Platforms:** iOS, Android
*/
titleLocArgs?: string;
[key: string]: string | undefined;
}
// To invoke:
// curl -i --location --request POST 'http://localhost:54321/functions/v1/' \
// --header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0' \
// --header 'Content-Type: application/json' \
// --data '{"name":"Functions"}'