diff --git a/examples/firebase-auth-firestore/.env.example b/examples/firebase-auth-firestore/.env.example index 91a70315add..cf5f9c83499 100644 --- a/examples/firebase-auth-firestore/.env.example +++ b/examples/firebase-auth-firestore/.env.example @@ -1,5 +1,5 @@ # Set the SERVICE_ACCOUNT value to be the minified contents of Admin SDK JSON file for your Firebase project SERVICE_ACCOUNT={"type":"service_account","project_id": "your-project-id","private_key_id": "123123123123asdf123123","private_key": "-----BEGIN PRIVATE KEY-----\nLongStringOfAlphanumericCharactersWillBeHere=\n-----END PRIVATE KEY-----\n","client_email": "firebase-adminsdk-123abc@your-project--id.iam.gserviceaccount.com","client_id": "123455678901234567890","auth_uri": "https://accounts.google.com/o/oauth2/auth","token_uri": "https://oauth2.googleapis.com/token","auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs","client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-qh94c%40your-project-id.iam.gserviceaccount.com"} -# Set the CLIENT_CONFIG value to be the JSON stringified value of the client credentials for your Firebase project -CLIENT_CONFIG={"apiKey":"abcdefg_1234567890","authDomain":"your-project-id.firebaseapp.com","projectId":"your-project-id","storageBucket":"your-project-id.appspot.com","messagingSenderId":"1234567890","appId":"1:1234567890:web:01234567890abc"} +# Set the API_KEY value to the Web API Key, which can be obtained on the project settings page in your Firebase admin console +API_KEY="abcdefg_1234567890" diff --git a/examples/firebase-auth-firestore/README.md b/examples/firebase-auth-firestore/README.md index ea7701a80bb..5f205d98117 100644 --- a/examples/firebase-auth-firestore/README.md +++ b/examples/firebase-auth-firestore/README.md @@ -19,8 +19,8 @@ To run it, you need to either: 1. [Create a Firebase Project](https://console.firebase.google.com) 2. Enable Auth (with email) and Firestore 3. Add a Web App -4. Get the [admin-sdk](https://firebase.google.com/docs/admin/setup#initialize-sdk) and [client-sdk credentials](https://firebase.google.com/docs/web/learn-more#config-object) -5. Save them to SERVICE_ACCOUNT and CLIENT_CONFIG in the `.env`-file +4. Get the [admin-sdk](https://firebase.google.com/docs/admin/setup#initialize-sdk) and [Web API Key](https://firebase.google.com/docs/reference/rest/auth) +5. Save them to SERVICE_ACCOUNT and API_KEY in the `.env`-file ### 2. Use the Firebase emulators diff --git a/examples/firebase-auth-firestore/app/server/auth.server.ts b/examples/firebase-auth-firestore/app/server/auth.server.ts index 5b939399af7..4b597df4649 100644 --- a/examples/firebase-auth-firestore/app/server/auth.server.ts +++ b/examples/firebase-auth-firestore/app/server/auth.server.ts @@ -1,6 +1,5 @@ import type { Session } from "@remix-run/node"; import { redirect } from "@remix-run/node"; -import { signInWithEmailAndPassword } from "firebase/auth"; import type { UserRecord } from "firebase-admin/auth"; import { destroySession, getSession } from "~/sessions"; @@ -29,12 +28,7 @@ export const requireAuth = async (request: Request): Promise => { }; export const signIn = async (email: string, password: string) => { - const { user } = await signInWithEmailAndPassword( - auth.client, - email, - password - ); - const idToken = await user.getIdToken(); + const { idToken } = await auth.signInWithPassword(email, password); const expiresIn = 1000 * 60 * 60 * 24 * 7; // 1 week const sessionCookie = await auth.server.createSessionCookie(idToken, { expiresIn, diff --git a/examples/firebase-auth-firestore/app/server/firebase-rest.server.ts b/examples/firebase-auth-firestore/app/server/firebase-rest.server.ts new file mode 100644 index 00000000000..67691ea3eb2 --- /dev/null +++ b/examples/firebase-auth-firestore/app/server/firebase-rest.server.ts @@ -0,0 +1,69 @@ +interface RestError { + error: { + code: number; + message: string; + errors: any[]; + }; +} + +export const isError = (input: unknown): input is RestError => + !!input && typeof input === "object" && "error" in input; + +// https://firebase.google.com/docs/reference/rest/auth#section-sign-in-email-password +interface SignInWithPasswordResponse extends Response { + json(): Promise< + | RestError + | { + /** + * A Firebase Auth ID token for the authenticated user. + */ + idToken: string; + /** + * The email for the authenticated user. + */ + email: string; + /** + * A Firebase Auth refresh token for the authenticated user. + */ + refreshToken: string; + /** + * The number of seconds in which the ID token expires. + */ + expiresIn: string; + /** + * The uid of the authenticated user. + */ + localId: string; + /** + * Whether the email is for an existing account. + */ + registered: boolean; + } + >; +} + +export const signInWithPassword = async ( + body: { + email: string; + password: string; + returnSecureToken: true; + }, + restConfig: { + apiKey: string; + domain: string; + } +) => { + const response: SignInWithPasswordResponse = await fetch( + `${restConfig!.domain}/v1/accounts:signInWithPassword?key=${ + restConfig!.apiKey + }`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(body), + } + ); + return response.json(); +}; diff --git a/examples/firebase-auth-firestore/app/server/firebase.server.ts b/examples/firebase-auth-firestore/app/server/firebase.server.ts index 45fa4ea7c85..0d77155a690 100644 --- a/examples/firebase-auth-firestore/app/server/firebase.server.ts +++ b/examples/firebase-auth-firestore/app/server/firebase.server.ts @@ -3,39 +3,29 @@ import { initializeApp as initializeServerApp, cert as serverCert, } from "firebase-admin/app"; -import { - getApps as getClientApps, - initializeApp as initializeClientApp, -} from "firebase/app"; import { getAuth as getServerAuth } from "firebase-admin/auth"; -import { getAuth as getClientAuth, connectAuthEmulator } from "firebase/auth"; -if (getClientApps().length === 0) { - let config, - useEmulator = false; - if (process.env.NODE_ENV === "development" && !process.env.CLIENT_CONFIG) { - console.warn( - "Missing CLIENT_CONFIG environment variable, using local emulator" - ); - config = { +import * as firebaseRest from "./firebase-rest.server"; + +const getRestConfig = (): { + apiKey: string; + domain: string; +} => { + if (process.env.NODE_ENV === "development" && !process.env.API_KEY) { + return { apiKey: "fake-api-key", - projectId: "remix-emulator", + domain: "http://localhost:9099/identitytoolkit.googleapis.com", }; - useEmulator = true; - } else if (!process.env.CLIENT_CONFIG) { - throw new Error("Missing CLIENT_CONFIG environment variable, "); + } else if (!process.env.API_KEY) { + throw new Error("Missing API_KEY environment variable"); } else { - try { - config = JSON.parse(process.env.CLIENT_CONFIG); - } catch { - throw Error("Invalid CLIENT_CONFIG environment variable"); - } - } - initializeClientApp(config); - if (useEmulator) { - connectAuthEmulator(getClientAuth(), "http://localhost:9099"); + return { + apiKey: process.env.API_KEY, + domain: "https://identitytoolkit.googleapis.com", + }; } -} +}; +const restConfig = getRestConfig(); if (getServerApps().length === 0) { let config; @@ -64,7 +54,24 @@ if (getServerApps().length === 0) { initializeServerApp(config); } +const signInWithPassword = async (email: string, password: string) => { + const signInResponse = await firebaseRest.signInWithPassword( + { + email, + password, + returnSecureToken: true, + }, + restConfig + ); + + if (firebaseRest.isError(signInResponse)) { + throw new Error(signInResponse.error.message); + } + + return signInResponse; +}; + export const auth = { server: getServerAuth(), - client: getClientAuth(), + signInWithPassword, }; diff --git a/examples/firebase-auth-firestore/package.json b/examples/firebase-auth-firestore/package.json index adcd4dfd2b1..f1e385121df 100644 --- a/examples/firebase-auth-firestore/package.json +++ b/examples/firebase-auth-firestore/package.json @@ -11,7 +11,6 @@ "@remix-run/node": "1.5.1", "@remix-run/react": "1.5.1", "@remix-run/serve": "1.5.1", - "firebase": "^9.6.10", "firebase-admin": "^10.0.2", "react": "^17.0.2", "react-dom": "^17.0.2"