1. Overview
In this codelab, you'll learn how to use
AngularFire
to create web applications by implementing and deploying a chat client using Firebase products and services.
What you'll learn
- Build a web app using Angular and Firebase.
- Sync data using Cloud Firestore and Cloud Storage for Firebase.
- Authenticate your users using Firebase Authentication.
- Deploy your web app on Firebase Hosting.
- Send notifications with Firebase Cloud Messaging.
- Collect your web app's performance data.
What you'll need
- The IDE/text editor of your choice, such as
WebStorm
,
Atom
,
Sublime
, or
VS Code
- The package manager
npm
, which typically comes with
Node.js
- A terminal/console
- A browser of your choice, such as Chrome
- The codelab's sample code (See the next step of the codelab for how to get the code.)
2. Get the sample code
Clone the codelab's
GitHub repository
from the command line:
git clone https://github.com/firebase/codelab-friendlychat-web
Alternatively, if you do not have git installed, you can
download the repository as a ZIP file
.
Import the starter app
Using your IDE, open or import the ??
angularfire-start
directory from the cloned repository. This ??
angularfire-start
directory contains the starting code for the codelab, which will be a fully functional chat web app.
3. Create and set up a Firebase project
Create a Firebase project
- Sign in to
Firebase
.
- In the Firebase console, click
Add Project
, and then name your Firebase project
FriendlyChat
. Remember the project ID for your Firebase project.
- Uncheck
Enable Google Analytics for this project
- Click
Create Project
.
The application that you're going to build uses Firebase products that are available for web apps:
- Firebase Authentication
to easily allow your users to sign into your app.
- Cloud Firestore
to save structured data on the cloud and get instant notification when data changes.
- Cloud Storage for Firebase
to save files in the cloud.
- Firebase Hosting
to host and serve your assets.
- Firebase Cloud Messaging
to send push notifications and display browser popup notifications.
- Firebase Performance Monitoring
to collect user performance data for your app.
Some of these products need special configuration or need to be enabled using the Firebase console.
Add a Firebase web app to the project
- Click the web icon
to create a new Firebase web app.
- Register the app with the nickname
Friendly Chat
, then check the box next to
Also set up Firebase Hosting for this app
. Click
Register app
.
- On the next step, you'll see a configuration object. Copy just the JS object (not the surrounding HTML) into
firebase-config.js
Enable Google
sign-in for Firebase Authentication
To allow users to sign in to the web app with their Google accounts, you'll use the
Google
sign-in method.
You'll need to enable
Google
sign-in:
- In the Firebase console, locate the
Build
section in the left panel.
- Click
Authentication
, then click the
Sign-in method
tab (or
click here
to go directly there).
- Enable the
Google
sign-in provider, then click
Save
.
- Set the public-facing name of your app to
Friendly Chat
and choose a
Project support email
from the dropdown menu.
- Configure your OAuth consent screen in the
Google Cloud Console
and add a logo:
Enable Cloud Firestore
The web app uses
Cloud Firestore
to save chat messages and receive new chat messages.
You'll need to enable Cloud Firestore:
- In the Firebase console's
Build
section, click
Firestore Database
.
- Click
Create database
in the Cloud Firestore pane.
- Select the
Start in test mode
option, then click
Next
after reading the disclaimer about the security rules.
Test mode ensures that you can freely write to the database during development. You'll make our database more secure later on in this codelab.
- Set the location where your Cloud Firestore data is stored. You can leave this as the default or choose a region close to you. Click
Done
to provision Firestore.
Enable Cloud Storage
The web app uses Cloud Storage for Firebase to store, upload, and share pictures.
You'll need to enable Cloud Storage:
- In the Firebase console's
Build
section, click
Storage
.
- If there's no
Get Started
button, it means that Cloud storage is already enabled, and you don't need to follow the steps below.
- Click
Get Started
.
- Read the disclaimer about security rules for your Firebase project, then click
Next
.
With the default security rules, any authenticated user can write anything to Cloud Storage. You'll make our storage more secure later in this codelab.
- The Cloud Storage location is preselected with the same region you chose for your Cloud Firestore database. Click
Done
to complete the setup.
4. Install the Firebase command-line interface
The Firebase command-line interface (CLI) allows you to use Firebase Hosting to serve your web app locally, as well as to deploy your web app to your Firebase project.
- Install the CLI by running the following npm command:
npm -g install firebase-tools
- Verify that the CLI has been installed correctly by running the following command:
firebase --version
Make sure that the version of the Firebase CLI is v4.1.0 or later.
- Authorize the Firebase CLI by running the following command:
firebase login
You've set up the web app template to pull your app's configuration for Firebase Hosting from your app's local directory (the repository that you cloned earlier in the codelab). But to pull the configuration, you need to associate your app with your Firebase project.
- Make sure that your command line is accessing your app's local
angularfire-start
directory.
- Associate your app with your Firebase project by running the following command:
firebase use --add
- When prompted, select your
Project ID
, then give your Firebase project an alias.
An alias is useful if you have multiple environments (production, staging, etc). However, for this codelab, let's just use the alias of
default
.
- Follow the remaining instructions on your command line.
5. Install AngularFire
Before running the project, make sure you have the Angular CLI and AngularFire set up.
- In a console, run the following command:
npm install -g @angular/cli
- Then, in a console from the
angularfire-start
directory, run the following Angular CLI command:
ng add @angular/fire
This will install all the necessary dependencies for your project.
- When prompted, select the features that were set up in the Firebase Console (
ng deploy -- hosting
,
Authentication
,
Firestore
,
Cloud Functions (callable)
,
Cloud Messaging
,
Cloud Storage
), and follow the prompts on the console.
6. Run the starter app locally
Now that you have imported and configured your project, you are ready to run the web app for the first time.
- In a console from the
angularfire-start
directory, run the following Firebase CLI command:
firebase emulators:start
- Your command line should display the following response:
? hosting: Local server: http://localhost:5000
You're using the
Firebase Hosting
emulator to serve our app locally. The web app should now be available from
http://localhost:5000
. All the files that are located under the
src
subdirectory are served.
- Using your browser, open your app at
http://localhost:5000
.
You should see your FriendlyChat app's UI, which is not (yet!) functioning:
The app cannot do anything right now, but with your help, it will soon! You've only laid out the UI for you so far.
Let's now build a real-time chat!
7. Import and configure Firebase
You'll need to configure the Firebase SDK to tell it which Firebase project you're using.
- Go to your
Project settings in the Firebase console
- In the "Your apps" card, select the nickname of the app for which you need a config object.
- Select "Config" from the Firebase SDK snippet pane.
You will find that an environment file
/angularfire-start/src/environments/environment.ts
was generated for you.
- Copy the config object snippet, then add it to
angularfire-start/src/firebase-config.js
.
export const environment = {
firebase: {
apiKey: "API_KEY",
authDomain: "PROJECT_ID.firebaseapp.com",
databaseURL: "https://PROJECT_ID.firebaseio.com",
projectId: "PROJECT_ID",
storageBucket: "PROJECT_ID.appspot.com",
messagingSenderId: "SENDER_ID",
appId: "APP_ID",
measurementId: "G-MEASUREMENT_ID",
},
};
Import AngularFire
You will find that the featues you've selected in the console were automatically routed in the
/angularfire-start/src/app/app.module.ts
file. This allows your app to use Firebase features and functionalities. However, to develop in a local environment, you need to connect them to use the Emulator suite.
- In
/angularfire-start/src/app/app.module.ts
, find the
imports
section, and modify the provided functions to connect to the Emulator suite in non-production environments.
// ...
import { provideAuth,getAuth, connectAuthEmulator } from '@angular/fire/auth';
import { provideFirestore,getFirestore, connectFirestoreEmulator } from '@angular/fire/firestore';
import { provideFunctions,getFunctions, connectFunctionsEmulator } from '@angular/fire/functions';
import { provideMessaging,getMessaging } from '@angular/fire/messaging';
import { provideStorage,getStorage, connectStorageEmulator } from '@angular/fire/storage';
// ...
provideFirebaseApp(() => initializeApp(environment.firebase)),
provideAuth(() => {
const auth = getAuth();
if (location.hostname === 'localhost') {
connectAuthEmulator(auth, 'http://127.0.0.1:9099', { disableWarnings: true });
}
return auth;
}),
provideFirestore(() => {
const firestore = getFirestore();
if (location.hostname === 'localhost') {
connectFirestoreEmulator(firestore, '127.0.0.1', 8080);
}
return firestore;
}),
provideFunctions(() => {
const functions = getFunctions();
if (location.hostname === 'localhost') {
connectFunctionsEmulator(functions, '127.0.0.1', 5001);
}
return functions;
}),
provideStorage(() => {
const storage = getStorage();
if (location.hostname === 'localhost') {
connectStorageEmulator(storage, '127.0.0.1', 5001);
}
return storage;
}),
provideMessaging(() => {
return getMessaging();
}),
// ...
During this codelab, you're going to use Firebase Authentication, Cloud Firestore, Cloud Storage, Cloud Messaging, and Performance Monitoring, so you're importing all of their libraries. In your future apps, make sure that you're only importing the parts of Firebase that you need, to shorten the load time of your app.
8. Set up user sign-in
AngularFire should now be ready to use since it's imported and initialized in
app.module.ts
. You're now going to implement user sign-in using
Firebase Authentication
.
Authenticate your users with Google Sign-In
In the app, when a user clicks the
Sign in with Google
button, the
login
function is triggered. (You already set that up for you!) For this codelab, you want to authorize Firebase to use Google as the identity provider. You'll use a popup, but
several other methods
are available from Firebase.
- In the
angularfire-start
directory, in the subdirectory
/src/app/services/
, open
chat.service.ts
.
- Find the function
login
.
- Replace the entire function with the following code.
// Signs-in Friendly Chat.
login() {
signInWithPopup(this.auth, this.provider).then((result) => {
const credential = GoogleAuthProvider.credentialFromResult(result);
this.router.navigate(['/', 'chat']);
return credential;
})
}
The
logout
function is triggered when the user clicks the
Log out
button.
- Go back to the file
src/app/services/chat.service.ts
.
- Find the function
logout
.
- Replace the entire function with the following code.
// Logout of Friendly Chat.
logout() {
signOut(this.auth).then(() => {
this.router.navigate(['/', 'login'])
console.log('signed out');
}).catch((error) => {
console.log('sign out error: ' + error);
})
}
Track the authentication state
To update our UI accordingly, you need a way to check if the user is logged in or logged out. With Firebase Authentication, you can retrieve observable on the user state that will be triggered each time the authentication state changes.
- Go back to the file
src/app/services/chat.service.ts
.
- Find the variable assignment
user$
.
- Replace the entire assignment with the following code.
// Observable user
user$ = user(this.auth);
The code above calls the AngularFire function
user
which returns an observable user. It will trigger each time the authentication state changes (when the user signs in or signs out). It's at this point that you'll update the UI to redirect, display the user in the header nav, and so on. All of these UI parts have already been implemented.
Test logging into the app
- If your app is still being served, refresh your app in the browser. Otherwise, run
firebase emulators:start
on the command line to start serving the app from
http://localhost:5000
, and then open it in your browser.
- Log in to the app using the sign-in button and your Google account. If you see an error message stating
auth/operation-not-allowed
, check to make sure that you enabled Google Sign-in as an authentication provider in the Firebase console.
- After logging in, your profile picture and user name should be displayed:
9. Write messages to Cloud Firestore
In this section, you'll write some data to Cloud Firestore so that you can populate the app's UI. This can be done manually with the
Firebase console
, but you'll do it in the app itself to demonstrate a basic Cloud Firestore write.
Data model
Cloud Firestore data is split into collections, documents, fields, and subcollections. You will store each message of the chat as a document in a top-level collection called
messages
.
Add messages to Cloud Firestore
To store the chat messages that are written by users, you'll use
Cloud Firestore
.
In this section, you'll add the functionality for users to write new messages to your database. A user clicking the
SEND
button will trigger the code snippet below. It adds a message object with the contents of the message fields to your Cloud Firestore instance in the
messages
collection. The
add()
method adds a new document with an automatically generated ID to the collection.
- Go back to the file
src/app/services/chat.service.ts
.
- Find the function
addMessage
.
- Replace the entire function with the following code.
// Adds a text or image message to Cloud Firestore.
addMessage = async(textMessage: string | null, imageUrl: string | null): Promise<void | DocumentReference<DocumentData>> => {
let data: any;
try {
this.user$.subscribe(async (user) =>
{
if(textMessage && textMessage.length > 0) {
data = await addDoc(collection(this.firestore, 'messages'), {
name: user?.displayName,
text: textMessage,
profilePicUrl: user?.photoURL,
timestamp: serverTimestamp(),
uid: user?.uid
})}
else if (imageUrl && imageUrl.length > 0) {
data = await addDoc(collection(this.firestore, 'messages'), {
name: user?.displayName,
imageUrl: imageUrl,
profilePicUrl: user?.photoURL,
timestamp: serverTimestamp(),
uid: user?.uid
});
}
return data;
}
);
}
catch(error) {
console.error('Error writing new message to Firebase Database', error);
return;
}
}
Test sending messages
- If your app is still being served, refresh your app in the browser. Otherwise, run
firebase emulators:start
on the command line to start serving the app from
http://localhost:5000
, and then open it in your browser.
- After logging in, enter a message such as "Hey there!", and then click
SEND
. This will write the message into Cloud Firestore. However,
you won't yet see the data in your actual web app
because you still need to implement
retrieving
the data (the next section of the codelab).
- You can see the newly added message in your Firebase Console. Open your Emulator suite UI. Under the
Build
section click
Firestore Database
(or click
here
and you should see the
messages
collection with your newly added message:
10. Read messages
Synchronize
messages
To read messages in the app, you'll need to add an observable that will trigger when data changes and then create a UI element that shows new messages.
You'll add code that listens for newly added messages from the app. In this code, you'll retrieve the snapshot of the
messages
collection. You'll only display the last 12 messages of the chat to avoid displaying a very long history upon loading.
- Go back to the file
src/app/services/chat.service.ts
.
- Find the function
loadMessages
.
- Replace the entire function with the following code.
// Loads chat message history and listens for upcoming ones.
loadMessages = () => {
// Create the query to load the last 12 messages and listen for new ones.
const recentMessagesQuery = query(collection(this.firestore, 'messages'), orderBy('timestamp', 'desc'), limit(12));
// Start listening to the query.
return collectionData(recentMessagesQuery);
}
To listen to messages in the database, you create a query on a collection by using the
collection
function to specify which collection the data that you want to listen to is in. In the code above, you're listening to the changes within the
messages
collection, which is where the chat messages are stored. You're also applying a limit by only listening to the last 12 messages using
limit(12)
and ordering the messages by date using
orderBy('timestamp', 'desc')
to get the 12 newest messages.
The
collectionData
function uses snapshots under the hood. The callback function will be triggered when there are any changes to documents that match the query. This could be if a message gets deleted, modified, or added. You can read more about this in the
Cloud Firestore documentation
.
Test synchronizing messages
- If your app is still being served, refresh your app in the browser. Otherwise, run
firebase emulators:start
on the command line to start serving the app from
http://localhost:5000
, and then open it in your browser.
- The messages that you created earlier in the database should be displayed in the FriendlyChat UI (see below). Feel free to write new messages; they should appear instantly.
- (Optional)
You can try manually deleting, modifying, or adding new messages directly in the
Firestore
section of the Emulator suite; any changes should be reflected in the UI.
Congratulations! You are reading Cloud Firestore documents in your app!
11. Send images
You'll now add a feature that shares images.
While Cloud Firestore is good for storing structured data, Cloud Storage is better suited for storing files.
Cloud Storage for Firebase
is a file/blob storage service, and you'll use it to store any images that a user shares using our app.
Save images to Cloud Storage
For this codelab, you've already added for you a button that triggers a file picker dialog. After selecting a file, the
saveImageMessage
function is called, and you can get a reference to the selected file. The
saveImageMessage
function accomplishes the following:
- Creates a "placeholder" chat message in the chat feed, so that users see a "Loading" animation while you upload the image.
- Uploads the image file to Cloud Storage to this path:
/<uid>/<file_name>
- Generates a publicly readable URL for the image file.
- Updates the chat message with the newly uploaded image file's URL in lieu of the temporary loading image.
Now you'll add the functionality to send an image:
- Go back to the file
src/chat.service.ts
.
- Find the function
saveImageMessage
.
- Replace the entire function with the following code.
// Saves a new message containing an image in Firebase.
// This first saves the image in Firebase storage.
saveImageMessage = async(file: any) => {
try {
// 1 - You add a message with a loading icon that will get updated with the shared image.
const messageRef = await this.addMessage(null, this.LOADING_IMAGE_URL);
// 2 - Upload the image to Cloud Storage.
const filePath = `${this.auth.currentUser?.uid}/${file.name}`;
const newImageRef = ref(this.storage, filePath);
const fileSnapshot = await uploadBytesResumable(newImageRef, file);
// 3 - Generate a public URL for the file.
const publicImageUrl = await getDownloadURL(newImageRef);
// 4 - Update the chat message placeholder with the image's URL.
messageRef ?
await updateDoc(messageRef,{
imageUrl: publicImageUrl,
storageUri: fileSnapshot.metadata.fullPath
}): null;
} catch (error) {
console.error('There was an error uploading a file to Cloud Storage:', error);
}
}
Test sending images
- If your app is still being served, refresh your app in the browser. Otherwise, run
firebase emulators:start
on the command line to start serving the app from
http://localhost:5000
, and then open it in your browser.
- After logging in, click the image upload button on the bottom left
and select an image file using the file picker. If you're looking for an image, feel free to use this
nice picture of a coffee cup
.
- A new message should appear in the app's UI with your selected image:
If you try adding an image while not signed in, you should see an error telling you that you must sign in to add images.
12. Show notifications
You'll now add support for browser notifications. The app will notify users when new messages are posted in the chat.
Firebase Cloud Messaging
(FCM) is a cross-platform messaging solution that lets you reliably deliver messages and notifications at no cost.
Add the FCM service worker
The web app needs a
service worker
that will receive and display web notifications.
The messaging provider should have already been set up when AngularFire was added, make sure that the following code exists in the imports section of
/angularfire-start/src/app/app.module.ts
provideMessaging(() => {
return getMessaging();
}),
The service worker simply needs to load and initialize the Firebase Cloud Messaging SDK, which will take care of displaying notifications.
Get FCM device tokens
When notifications have been enabled on a device or browser, you'll be given a
device token
. This device token is what you use to send a notification to a particular device or particular browser.
When the user signs in, you call the
saveMessagingDeviceToken
function. That's where you'll get the
FCM device token
from the browser and save it to Cloud Firestore.
- Find the function
saveMessagingDeviceToken
.
- Replace the entire function with the following code.
// Saves the messaging device token to Cloud Firestore.
saveMessagingDeviceToken= async () => {
try {
const currentToken = await getToken(this.messaging);
if (currentToken) {
console.log('Got FCM device token:', currentToken);
// Saving the Device Token to Cloud Firestore.
const tokenRef = doc(this.firestore, 'fcmTokens', currentToken);
await setDoc(tokenRef, { uid: this.auth.currentUser?.uid });
// This will fire when a message is received while the app is in the foreground.
// When the app is in the background, firebase-messaging-sw.js will receive the message instead.
onMessage(this.messaging, (message) => {
console.log(
'New foreground notification from Firebase Messaging!',
message.notification
);
});
} else {
// Need to request permissions to show notifications.
this.requestNotificationsPermissions();
}
} catch(error) {
console.error('Unable to get messaging token.', error);
};
}
However, this code won't work initially. For your app to be able to retrieve the device token, the user needs to grant your app permission to show notifications (the next step of the codelab).
Request permissions to show notifications
When the user has not yet granted your app permission to show notifications, you won't be given a device token. In this case, you call the
requestPermission()
method, which will display a browser dialog asking for this permission (
in supported browsers
).
- Go back to the file
src/app/services/chat.service.ts
.
- Find the function
requestNotificationsPermissions
.
- Replace the entire function with the following code.
// Requests permissions to show notifications.
requestNotificationsPermissions = async () => {
console.log('Requesting notifications permission...');
const permission = await Notification.requestPermission();
if (permission === 'granted') {
console.log('Notification permission granted.');
// Notification permission granted.
await this.saveMessagingDeviceToken();
} else {
console.log('Unable to get permission to notify.');
}
}
Get your device token
- If your app is still being served, refresh your app in the browser. Otherwise, run
firebase emulators:start
on the command line to start serving the app from
http://localhost:5000
, and then open it in your browser.
- After logging in, the notifications permission dialog should appear:
- Click
Allow
.
- Open the JavaScript console of your browser. You should see the following message:
Got FCM device token: cWL6w:APA91bHP...4jDPL_A-wPP06GJp1OuekTaTZI5K2Tu
- Copy your device token. You'll need it for the next stage of the codelab.
Send a notification to your device
Now that you have your device token, you can send a notification.
- Open the
Cloud Messaging tab of the Firebase console
.
- Click "New Notification"
- Enter a notification title and notification text.
- On the right side of the screen, click "send a test message"
- Enter the device token you copied from the JavaScript console of your browser, then click the plus ("+") sign
- Click "test"
If your app is in the foreground, you'll see the notification in the JavaScript console.
If your app is in the background, a notification should appear in your browser, as in this example:
13. Cloud Firestore security rules
View
database security rules
Cloud Firestore uses a specific
rules language
to define access rights, security, and data validations.
When setting up the Firebase project at the beginning of this codelab, you chose to use "Test mode" default security rules so that you didn't restrict access to the datastore. In the
Firebase console
, in the
Database
section's
Rules
tab, you can view and modify these rules.
Right now, you should see the default rules, which do not restrict access to the datastore. This means that any user can read and write to any collections in your datastore.
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write;
}
}
}
You'll update the rules to restrict things by using the following rules:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Messages:
// - Anyone can read.
// - Authenticated users can add and edit messages.
// - Validation: Check name is same as auth token and text length below 300 char or that imageUrl is a URL.
// - Deletes are not allowed.
match /messages/{messageId} {
allow read;
allow create, update: if request.auth != null
&& request.resource.data.name == request.auth.token.name
&& (request.resource.data.text is string
&& request.resource.data.text.size() <= 300
|| request.resource.data.imageUrl is string
&& request.resource.data.imageUrl.matches('https?://.*'));
allow delete: if false;
}
// FCM Tokens:
// - Anyone can write their token.
// - Reading list of tokens is not allowed.
match /fcmTokens/{token} {
allow read: if false;
allow write;
}
}
}
The security rules should update automatically to your Emulator suite.
View Cloud Storage security rules
Cloud Storage for Firebase uses a specific
rules language
to define access rights, security, and data validations.
When setting up the Firebase project at the beginning of this codelab, you chose to use the default Cloud Storage security rule that only allows authenticated users to use Cloud Storage. In the
Firebase console
, in the
Storage
section's
Rules
tab, you can view and modify rules. You should see the default rule which allows any signed-in user to read and write any files in your storage bucket.
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, write: if request.auth != null;
}
}
}
You'll update the rules to do the following:
- Allow each user to write only to their own specific folders
- Allow anyone to read from Cloud Storage
- Make sure that the files uploaded are images
- Restrict the size of the images that can be uploaded to maximum 5 MB
This can be implemented using the following rules:
rules_version = '2';
// Returns true if the uploaded file is an image and its size is below the given number of MB.
function isImageBelowMaxSize(maxSizeMB) {
return request.resource.size < maxSizeMB * 1024 * 1024
&& request.resource.contentType.matches('image/.*');
}
service firebase.storage {
match /b/{bucket}/o {
match /{userId}/{messageId}/{fileName} {
allow write: if request.auth != null && request.auth.uid == userId && isImageBelowMaxSize(5);
allow read;
}
}
}
14. Deploy your app using Firebase Hosting
Firebase offers a
hosting service
to serve your assets and web apps. You can deploy your files to Firebase Hosting using the Firebase CLI. Before deploying, you need to specify in your
firebase.json
file which local files should be deployed. For this codelab, you've already done this for you because this step was required to serve our files during this codelab. The hosting settings are specified under the
hosting
attribute:
{
// If you went through the "Cloud Firestore Security Rules" step.
"firestore": {
"rules": "firestore.rules"
},
// If you went through the "Storage Security Rules" step.
"storage": {
"rules": "storage.rules"
},
"hosting": {
"public": "./public"
}
}
These settings tell the CLI that you want to deploy all files in the
./public
directory (
"public": "./public"
).
- Make sure that your command line is accessing your app's local
angularfire-start
directory.
- Deploy your files to your Firebase project by running the following command:
ng deploy
Then select the Firebase option, and follow the prompts in the command line.
- The console should display the following:
=== Deploying to 'friendlychat-1234'...
i deploying firestore, storage, hosting
i storage: checking storage.rules for compilation errors...
? storage: rules file storage.rules compiled successfully
i firestore: checking firestore.rules for compilation errors...
? firestore: rules file firestore.rules compiled successfully
i storage: uploading rules storage.rules...
i firestore: uploading rules firestore.rules...
i hosting[friendlychat-1234]: beginning deploy...
i hosting[friendlychat-1234]: found 8 files in ./public
? hosting[friendlychat-1234]: file upload complete
? storage: released rules storage.rules to firebase.storage/friendlychat-1234.appspot.com
? firestore: released rules firestore.rules to cloud.firestore
i hosting[friendlychat-1234]: finalizing version...
? hosting[friendlychat-1234]: version finalized
i hosting[friendlychat-1234]: releasing new version...
? hosting[friendlychat-1234]: release complete
? Deploy complete!
Project Console: https://console.firebase.google.com/project/friendlychat-1234/overview
Hosting URL: https://friendlychat-1234.firebaseapp.com
- Visit your web app that's now fully hosted on a global CDN using Firebase Hosting at two of your very own Firebase subdomains:
https://<firebase-projectId>.firebaseapp.com
https://<firebase-projectId>.web.app
Alternatively, you can run
firebase open hosting:site
in the command line.
Visit the documentation to learn more about
how Firebase Hosting works
.
Go to your project's Firebase console
Hosting
section to view useful hosting information and tools, including the history of your deploys, the functionality to roll back to previous versions of your app, and the workflow to set up a custom domain.
15. Congratulations!
You've used Firebase to build a real-time chat web application!
What you've covered
- Firebase Authentication
- Cloud Firestore
- Firebase SDK for Cloud Storage
- Firebase Cloud Messaging
- Firebase Performance Monitoring
- Firebase Hosting
Next steps
Learn more
16. [Optional] Enforce with App Check
Firebase
App Check
helps secure your services from unwanted traffic and helps protect your backend from abuse. In this step, you'll add credentials validation and block unauthorized clients with App Check and
reCAPTCHA Enterprise
.
First, you'll need to enable App Check and reCaptcha.
- In the Cloud console, find and select
reCaptcha Enterprise
under Security.
- Enable the service as prompted, and click
Create Key
.
- Input a display name as prompted, and select
Website
as your platform type.
- Add your deployed URLs to the
Domain list
, and make sure that the "Use checkbox challenge" option is
unselected
.
- Click
Create Key
, and store the generated key somewhere for safekeeping. You will need it later in this step.
- In the Firebase console, locate the
Build
section in the left panel.
- Click
App Check
, then click the
Sign-in method
tab to navigate to
App Check
.
- Click
Register
and enter your reCaptcha Enterprise key when prompted, then click
Save
.
- In the APIs View, select
Storage
and click
Enforce
. Do the same for
Cloud Firestore
.
App Check should now be enforced! Refresh your app and try to view or send chat messages. You should get the error message:
Uncaught Error in snapshot listener: FirebaseError: [code=permission-denied]: Missing or insufficient permissions.
This means App Check is blocking unvalidated requests by default. Now let's add validation to your app.
Navigate to your
environment.ts
file and add
reCAPTCHAEnterpriseKey
to the
environment
object.
export const environment = {
firebase: {
apiKey: 'API_KEY',
authDomain: 'PROJECT_ID.firebaseapp.com',
databaseURL: 'https://PROJECT_ID.firebaseio.com',
projectId: 'PROJECT_ID',
storageBucket: 'PROJECT_ID.appspot.com',
messagingSenderId: 'SENDER_ID',
appId: 'APP_ID',
measurementId: 'G-MEASUREMENT_ID',
},
reCAPTCHAEnterpriseKey: {
key: "Replace with your recaptcha enterprise site key"
},
};
Replace the value of
key
with your reCaptcha Enterprise token.
Then, navigate to
app.module.ts
file and add the following imports:
import { getApp } from '@angular/fire/app';
import {
ReCaptchaEnterpriseProvider,
initializeAppCheck,
provideAppCheck,
} from '@angular/fire/app-check';
In the same
app.module.ts
file, add the following global variable declaration:
declare global {
var FIREBASE_APPCHECK_DEBUG_TOKEN: boolean;
}
@NgModule({ ...
In imports, add initialization of App Check with
ReCaptchaEnterpriseProvider
, and set
isTokenAutoRefreshEnabled
to
true
to allow tokens to auto-refresh.
imports: [
BrowserModule,
AppRoutingModule,
CommonModule,
FormsModule,
provideFirebaseApp(() => initializeApp(environment.firebase)),
provideAppCheck(() => {
const appCheck = initializeAppCheck(getApp(), {
provider: new ReCaptchaEnterpriseProvider(
environment.reCAPTCHAEnterpriseKey.key
),
isTokenAutoRefreshEnabled: true,
});
if (location.hostname === 'localhost') {
self.FIREBASE_APPCHECK_DEBUG_TOKEN = true;
}
return appCheck;
}),
To allow local testing, set
self.FIREBASE_APPCHECK_DEBUG_TOKEN
to
true
. When you refresh your app in
localhost
, this will log a debug token in the console similar to:
App Check debug token: CEFC0C76-7891-494B-B764-349BDFD00D00. You will need to add it to your app's App Check settings in the Firebase console for it to work.
Now, go to the
Apps View
of App Check in the Firebase console.
Click the overflow menu, and select
Manage debug tokens
.
Then, click
Add debug token
and paste the debug token from your console as prompted.
Navigate to the
chat.service.ts
file, and add the following import:
import { AppCheck } from '@angular/fire/app-check';
In the same
chat.service.ts
file, inject App Check alongside the other Firebase services.
export class ChatService {
appCheck: AppCheck = inject(AppCheck);
...
Congratulations! App Check should now be working in your app.