config.ts
export const FIREBASE_TOKEN = "FIREBASE_TOKEN";
export const FIREBASE_DATABASE = "FIREBASE_DATABASE";
export const FIREBASE_PROJECT_ID = "FIREBASE_PROJECT_ID";
export const FIREBASE_PROJECT_KEY = "FIREBASE_PROJECT_KEY";
export const FIREBASE_REFRESH_RATE = "FIREBASE_REFRESH_RATE";
let backgroundRefetchStarted = false;
const config = {
firebaseDb: Deno.env.get("FIREBASE_DATABASE") || "(default)",
host(project?: string) {
return `https://firestore.googleapis.com/v1/projects/${
project ?? this.projectID
}`;
},
get token() {
return Deno.env.get(FIREBASE_TOKEN);
},
get projectID() {
return Deno.env.get(FIREBASE_PROJECT_ID);
},
get projectKey() {
return Deno.env.get(FIREBASE_PROJECT_KEY);
},
get eventLog() {
return Deno.env.get("FIREBASE_EVENT_LOG") == "true";
},
};
const setProjectID = (id: string) => {
Deno.env.set(FIREBASE_PROJECT_ID, id);
};
const setProjectKey = (key: string) => {
Deno.env.set(FIREBASE_PROJECT_KEY, key);
};
const setToken = (token: string): string => {
Deno.env.set(FIREBASE_TOKEN, token);
return token;
};
const setDatabase = (db: string) => {
Deno.env.set(FIREBASE_DATABASE, db);
};
/*
* Login with your IAM account to establish user.
* Required GoogleService-Info.plist
*/
const setTokenFromServiceAccount = async () => {
const p = Deno.run({
cmd: ["gcloud", "auth", "application-default", "print-access-token"],
stdout: "piped",
stderr: "piped",
stdin: "piped",
});
const output = new TextDecoder().decode(await p.output());
await p.close();
const token = String(output).replace(/\\n/gm, "\n").replace("\n", "");
setToken(token);
return token;
};
/*
* Login with your email and password to establish iam user
*/
const setTokenFromEmailPassword = async (
params?: {
email?: string;
password?: string;
refreshToken?: string;
key?: string;
},
refresh?: boolean
) => {
const { email, key, refreshToken, password } = params ?? {};
let baseUrl = "";
let body = {};
if (typeof refreshToken !== "undefined") {
baseUrl = "securetoken.googleapis.com/v1/token";
body = {
refresh_token: refreshToken,
grant_type: "refresh_token",
};
} else {
baseUrl = "identitytoolkit.googleapis.com/v1/accounts:signInWithPassword";
body = {
email: email ?? Deno.env.get("FIREBASE_AUTH_EMAIL"),
password: password ?? Deno.env.get("FIREBASE_AUTH_PASSWORD"),
returnSecureToken: true,
};
}
const firebase = await fetch(
`https://${baseUrl}?key=${key ?? Deno.env.get(FIREBASE_PROJECT_KEY) ?? ""}`,
{
headers: {
contentType: "application/json",
},
method: "POST",
body: JSON.stringify(body),
}
);
const json = await firebase.json();
const token = json?.idToken;
token && setToken(token);
if (refresh) {
queueMicrotask(() => {
setRefetchBeforeExp({
expiresIn: Deno.env.get(FIREBASE_REFRESH_RATE) || json.expiresIn,
refreshToken: json.refreshToken,
});
});
}
return token;
};
type Token = {
expiresIn: number;
refreshToken: string;
};
// BACKGROUND PROCESS to refresh token
const setRefetchBeforeExp = async ({ expiresIn, refreshToken }: Token) => {
const expMS = (expiresIn / 60) * 60000;
if (!backgroundRefetchStarted) {
const p = Deno.run({
cmd: [
"deno",
"run",
"--allow-read",
"--allow-env",
"--unstable",
"--allow-net",
"--allow-run",
"--allow-write",
"./refresh.ts",
String(expMS),
refreshToken,
],
stdout: "piped",
stderr: "piped",
});
try {
const { code } = await p.status();
const rawOutput = await p.output();
const rawError = await p.stderrOutput();
let jsonOutput;
if (code === 0) {
jsonOutput = JSON.parse(new TextDecoder().decode(rawOutput));
if (jsonOutput && jsonOutput.accessToken) {
Deno.env.set(FIREBASE_TOKEN, jsonOutput.accessToken);
}
} else {
const errorString = new TextDecoder().decode(rawError);
console.error(errorString);
}
backgroundRefetchStarted = false;
} catch (e) {
console.error(e);
}
}
backgroundRefetchStarted = true;
};
export {
config,
setToken,
setDatabase,
setProjectID,
setProjectKey,
setRefetchBeforeExp,
setTokenFromServiceAccount,
setTokenFromEmailPassword,
};