Firebase Authentication

Firebase Authentication

Firebase Authentication provides an end-to-end identity solution for your web applications that supports authentication using passwords, phone numbers, popular identity providers like Google, Facebook, GitHub, and more. Firebase Authentication also handles user sessions, allowing your application to remain secure while still being easy to integrate with.

Authentication for a web app is an important function that is often difficult to implement correctly. For this reason, it is often a good idea to use a third-party authentication service like Firebase Authentication. Firebase Authentication provides a secure and easy-to-use authentication system that can be integrated into your web app with just a few lines of code. In general, do not try to implement your own authentication system unless you have a very good reason to do so.

Security Skills

In web application development, understanding key security concepts is crucial. These include the difference between authentication and authorization, the function and use of JSON Web Tokens (JWTs), and how to handle and store sensitive information.

Authentication vs Authorization

Authentication and Authorization are two sides of the same coin, both crucial in securing web applications, but they serve different purposes.

  • Authentication: the process of verifying who a user is. When a user logs into your application, they provide credentials such as a username and password. The authentication process checks these credentials and, if they match a record in your user database, the user is authenticated.

  • Authorization: determines what an authenticated user is allowed to do. Once a user is authenticated, the authorization process checks their permissions to determine what resources they can access or actions they can perform.

JSON Web Tokens (JWTs)

JSON Web Tokens (JWTs) are an open standard for securely transmitting information between parties as a JSON object. They are often used for authentication and information exchange.

A JWT is divided into three parts: a header, a payload, and a signature. The header typically contains the type of token and the algorithm used for encryption. The payload contains the claims or pieces of information being passed. The signature is computed from the header, the payload, and a secret key.

JWTs are commonly used in Firebase Authentication to help manage user sessions. When a user logs in, Firebase Authentication returns a JWT that is stored in the browser's local storage. This JWT is then sent with every request to the server. The server can then verify the JWT and use the information it contains to determine if the user is authenticated and authorized to access the requested resource.

Example of a (non-functioning) JWT:

{
  "header": {
    "alg": "HS256",
    "typ": "JWT"
  },
  "payload": {
    "iss": "https://github.com",
    "sub": "1234567890",
    "aud": "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit",
    "exp": 1609459200,
    "iat": 1609455600,
    "auth_time": 1609455600,
    "user_id": "abcdefghijk",
    "firebase": {
      "identities": {
        "github.com": ["1234567890"]
      },
      "sign_in_provider": "github.com"
    }
  },
  "signature": "abcdefgHIJKLMNOP1234567890"
}

JWTs are often used in place of session cookies. The main advantage of JWTs is that they are stateless, meaning that the server does not need to keep track of user sessions. This makes them ideal for use in REST APIs. You will learn more about REST APIs in week 11.

Handling and Storing Sensitive Information

Handling and storing sensitive information securely is a crucial part of web application development. Firebase Authentication helps with this by securely managing and storing user credentials.

Encryption

Encryption is the process of converting data into a format that is unreadable without a decryption key. Encryption is used to protect sensitive information like user passwords and personal data. For example, in Firebase Authentication, user passwords are securely encrypted before they are stored.

Access Controls

Access controls determine who has access to what data. Firebase Authentication uses access controls to ensure that only authenticated and authorized users can access certain data. This is critical for maintaining the privacy and security of user information.

🔐

Always remember, storing sensitive information in plain text is a big no-no. Always use encryption and proper access controls!

SSO with OAuth

Single Sign-On (SSO) is a user authentication process that allows a user to use one set of login credentials to access multiple applications. OAuth is an open standard for access delegation that is commonly used in SSO systems.

Firebase Authentication supports SSO with OAuth, allowing users to sign in with their Google, Facebook, X, or GitHub accounts. This not only improves the user experience by allowing users to sign in with their existing accounts, but also enhances security by reducing the risk of password reuse. OAuth 2.0 is the industry-standard protocol for authorization.

Here is an example of the steps of an authorization request:

OAuth 2.0 Authorization Request Oh-Auth - Abusing OAuth to take over millions of accounts (opens in a new tab)

React Skills

{ children } prop

In React, children is a special prop that is used to pass components as data to other components. This allows you to create component structures in JSX and makes your components more reusable.

The children prop can be used to pass elements directly in between the opening and closing tags of a parent component, just like you might with a regular HTML element. The parent component can then render these children by including {children} in its return statement.

Here's a simple example of how you could use the children prop:

// A "Card" component that wraps content in a div with some styling
function Card({ children }) {
  return <div className="border">{children}</div>;
}
 
// Using the "Card" component
export default function Page() {
  return (
    <Card>
      <h1>Hello, world!</h1>
      <p>Welcome to my app.</p>
    </Card>
  );
}

In this example, MyApp is passing two elements (an <h1> and a <p>) as children into the Card component. The Card component is then rendering these children inside a styled <div>. This makes the Card component reusable with different content.

React Context

React context is a feature provided by the React library that allows you to pass data through the component tree without having to pass props manually at every level. It is particularly useful when you want to share some global data with many components, such as user information, theme settings, or language preferences.

Authentication is a good example of when to use React context. When a user logs in, you want to make sure that all components have access to the user's information. React context allows you to do this without having to pass the user information as props to every component.

The context system in React consists of three main parts:

  • createContext: Creates a new context object.
  • Context.Provider: A React component that allows consuming components to subscribe to context changes.
  • useContext: Allows you to consume the context data.

Here's a simple example of how you could use context in a functional component with the useContext Hook:

import { useContext } from "react";
 
// Create a Context
const MyContext = React.createContext();
 
// Create a component that uses the Context
function MyComponent() {
  const contextValue = useContext(MyContext);
  return <div>{contextValue}</div>;
}
 
// Use the Context Provider to allow MyComponent to consume the context data
function MyApp() {
  return (
    <MyContext.Provider value="Hello from context!">
      <MyComponent />
    </MyContext.Provider>
  );
}
 
export default MyApp;

In this example, MyComponent is able to consume the value from the context without having to receive it as a prop.

The context API is a powerful feature, but that doesn't mean it should be used everywhere. Here's some guidance on when and when not to use it:

  • When to Use Context: Use context when you have global data that many components share.
  • When Not to Use Context: Avoid using context for low-level or component-specific state. For small or medium-sized applications, it might be better to stick with prop passing. Also, remember that every Context Provider re-render causes all its consumers to re-render too, which might affect performance.
🌏

You can think of React Context as a kind of global variable for your React application. It allows you to store data that you want to be accessible to many different parts of your application, without having to pass the data around manually via props.

Custom Hooks

Custom hooks, as the name suggests, are user-defined hooks that allow you to extract component logic into reusable functions. They are JavaScript functions whose names are prefixed with the word use.

Custom hooks are a mechanism to reuse stateful logic (such as setting up a subscription and remembering the current value), not state itself. Each time you use a custom hook, all state and effects inside of it are fully isolated.

You can write custom hooks that cover a wide range of use cases like form validation, animation, and data fetching. For example, you could write a custom hook that fetches data from an API and returns the data and a loading state. You could then use this hook in multiple components to fetch data from the same API.

Here's a simple example of how you could create and use a custom hook:

import { useState } from "react";
 
// A custom hook that manages state for a counter
function useCounter() {
  const [count, setCount] = useState(0);
  const increment = () => {
    setCount((prevCount) => prevCount + 1);
  };
  return { count, increment };
}
 
// A component that uses the custom hook
function CounterComponent() {
  const { count, increment } = useCounter();
 
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={increment}>Increment</button>
    </div>
  );
}
 
export default CounterComponent;

In this example, useCounter is a custom hook that encapsulates the logic for a counter. The CounterComponent uses this custom hook and doesn't need to worry about maintaining its own state or implementing an increment function.

Custom Hooks, Context, and Authentication

When combined, custom hooks, context, and authentication can be a powerful pattern for managing user sessions in a React application. Here's how they can work together:

  • Context: You can create a context to hold the user's authentication status and information. This context can be provided at the top level of your application so that all components in your application tree can access the user's authentication status.

  • Custom Hook: You can create a custom hook to consume the authentication context. This hook can return the user's authentication status and any user information stored in the context. It can also return functions to log in, log out, sign up, etc., that update the context. By encapsulating this logic in a custom hook, you can easily access and manipulate the user's authentication status from any component.

import { useContext, useState } from "react";
 
// Create an Auth context
const AuthContext = React.createContext();
 
// Create a custom hook that uses the Auth context
function useAuth() {
  const context = useContext(AuthContext);
  if (!context) {
    throw new Error("useAuth must be used within an AuthProvider");
  }
  return context;
}
 
// Create a Provider component for the Auth context
function AuthProvider({ children }) {
  const [currentUser, setCurrentUser] = useState(null);
 
  const login = (username, password) => {
    // Logic to authenticate user and set current user
  };
 
  const logout = () => {
    // Logic to log out user and unset current user
  };
 
  // The value provided will be available to all components in the tree
  const value = {
    currentUser,
    login,
    logout,
  };
 
  return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}
 
export { useAuth, AuthProvider };

This is a simplified example and a real-world application would include more complex logic for handling authentication, error states, and asynchronous actions. However, this pattern of using custom hooks and context can be a clean and efficient way to manage user authentication in a React application.

Next.js Skills

Environment Variables

Environment variables are a fundamental part of developing with Node.js, allowing your app to behave differently based on the environment you want them to run in. They are essentially external variables that are defined outside your application, and can be brought into your application and used whenever required.

Environment variables can store various types of data, like API keys, database connections, or any other sensitive data that you do not want to expose in your codebase. This is crucial for maintaining the security of your application and allows for flexibility, as the values of these variables can change depending on the environment (development, testing, production, etc).

Using Environment Variables in Next.js

In a Next.js project, environment variables are typically stored in .env.local files. These are simple text files that hold key-value pairs in the format KEY=VALUE. Next.js has built-in support for loading environment variables from these .env files into process.env.

🚫

The .env.local files are used to store environment variables that shouldn't be committed to the Git repository. The file .gitignore is used to ignore these files when committing to Git. This is important because you don't want to expose sensitive information like API keys in your Git repository.

Let's say you have the following environment variables stored in your .env.local file.

DATABASE_API_KEY=MySuperSecretApiKey
NEXT_PUBLIC_ANALYTICS_ID=abcdefghijk

You can then access this in your Next.js code using process.env. Environment variables prefixed with NEXT_PUBLIC_ are available on both the server and the client. Other environment variables are only available on the server.

// server code
const apiKey = process.env.DATABASE_API_KEY;
console.log("The API key is", apiKey);
 
// server or client code
const analyticsId = process.env.NEXT_PUBLIC_ANALYTICS_ID;
console.log("The analytics ID is", analyticsId);

When deploying your Next.js app to Vercel, you can set environment variables in the project settings.

layout.js

A crucial part of any application is the layout - the UI that's shared across multiple pages. Utilizing layouts can improve your application's efficiency by preserving state, remaining interactive, and preventing unnecessary re-renders during navigation. In Next.js, layouts can be defined using a special layout.js file and can even be nested for more complex UI structures.

Layouts in Next.js are defined by exporting a React component from a layout.js file. This component should accept a children prop, which will be either a nested layout or a page during rendering.

export default function MyLayout({ children }) {
  return (
    <div>
      <header>My Shared Header</header>
      {children}
      <footer>My Shared Footer</footer>
    </div>
  );
}

In this example, the MyLayout component defines a layout with a shared header and footer. The {children} placeholder is where child layouts or pages will be rendered.

A layout.js file is optional and can be located on any level with page.js.

Firebase Skills

Firebase is a powerful platform developed by Google that provides a variety of services for building web and mobile applications. It offers a suite of cloud-based tools, which includes a realtime database, user authentication, analytics, hosting, and much more. Firebase simplifies the process of building and scaling applications, allowing developers to focus more on creating fantastic user experiences.

Firebase provides a Backend-as-a-Service (BaaS), which means that you don't have to manage servers or write APIs. Firebase's services are accessible via APIs, which you can call directly from your client apps.

Firebase SDK

The Firebase Software Development Kit (SDK) is a library that allows developers to easily interact with Firebase's services within their applications. You can install the Firebase SDK into your project using npm or yarn, or by including it directly in your HTML for web projects.

To install the Firebase SDK in a JavaScript project, you can use the following npm command:

npm install firebase

Once installed, you can import Firebase into your files. See the assignment for more details.

Firebase Authentication

Firebase Authentication provides a complete authentication solution for your application. It supports authentication using passwords, phone numbers, popular federated identity providers like Google, Facebook, and Twitter, and more.

Firebase Authentication also seamlessly integrates with other Firebase services and provides utilities for user management, secure password reset, account merging, and so on.

In the assignment, you will learn how to use Firebase Authentication to manage user sessions.

🗒️ Summary

  • Firebase Authentication offers a comprehensive identity solution for web apps, supporting multiple authentication methods including passwords, phone numbers, and identity providers like Google, Facebook, and GitHub.
  • Firebase Authentication is beneficial due to its secure, user-friendly system that can be integrated into a web app with just a few lines of code.
  • Key security concepts include understanding the distinction between authentication and authorization, the use of JSON Web Tokens (JWTs), and the management and storage of sensitive data.
  • Authentication verifies a user's identity, while authorization checks an authenticated user's permissions to determine their access privileges.
  • JWTs are an open standard for securely transmitting information between parties as a JSON object, often used for authentication and information exchange. JWTs are stateless and ideal for use in REST APIs.
  • Firebase Authentication ensures secure management and storage of user credentials. Sensitive information must always be encrypted and proper access controls followed.
  • Single Sign-On (SSO) with OAuth allows a user to use one set of login credentials to access multiple applications. Firebase Authentication supports SSO with OAuth.
  • Firebase SDK is a library that allows developers to interact with Firebase's services. Firebase Authentication can be used to create new users and manage user sessions.
  • React concepts include the use of the children prop to pass components as data, React context for passing data through the component tree, and custom hooks for reusing component logic.
  • Custom hooks, context, and authentication can be combined to effectively manage user sessions in a React application.
  • In Next.js, environment variables are a key part of development, allowing the app to behave differently based on the environment. Layouts in Next.js can be defined using a layout.js file and can be nested for complex UI structures.

📚 Knowledge Check

What is Firebase Authentication?

A library for building user interfaces

A web application development framework

An identity solution for web applications

A database management system

What is the main function of a JWT in Firebase Authentication?

To style the web app

To manage user sessions

To create animations

To make API calls

Which of the following is not a part of a JWT?

Header

Payload

Signature

Footer

In React, what is the `children` prop used for?

To pass components as data to other components

To style components

To make API calls

To manage state

What is the purpose of React Context?

To style components

To pass data through the component tree without having to pass props manually at every level

To make API calls

To manage animations

What are custom hooks in React?

A type of styling method

User-defined hooks that allow you to extract component logic into reusable functions

A type of API call

A type of component

In Next.js, where are environment variables typically stored?

.env.local files

Database

API server

Cloud storage

What is a layout in Next.js?

A type of database

The UI that's shared across multiple pages

An environment variable

A type of API call

What is Firebase SDK?

A library that allows developers to interact with Firebase's services

A type of database

A type of environment variable

A layout in Next.js

What is Firebase?

A library for building user interfaces

A type of environment variable

A platform that provides a variety of services for building web and mobile applications

A layout in Next.js

🌐 Community Events App

Firebase Authentication has now been added to Week 8 of the Community Events app. Read through the code to see how only authenticated users can add a new event.

📖 Further Reading