How to set up Firebase authentication with Magic
Magic allows developers to use their own identity providers for authentication. A user can log in using providers such as Firebase and then link their accounts to a Magic wallet.
Firebase is a platform developed by Google for creating mobile and web applications. It provides a variety of tools and services to help developers build high-quality apps, including authentication.
In this guide, we'll walk through integrating Firebase authentication with Magic, allowing users to log in with Google and associate their accounts to a Magic wallet. This example uses a Next.js web app, but the principles apply to any JavaScript framework.
#Project prerequisites
To follow along with this guide, you’ll need four things:
- A Magic Publishable API Key
- A Magic Secret API Key
- A Firebase project
- A web client
You can get your Publishable and Secret API Key’s from your Magic Dashboard.
You can create your Firebase project from the Firebase console.
We’ll use the make-scoped-magic-app
CLI tool to bootstrap a Next.js app with Magic authentication already baked into the client. You’re welcome to use your own client, but this tutorial and its accompanying code snippets assume the output of the make-scoped-magic-app
CLI as the starting point.
The make-scoped-magic-app
CLI tool is an easy way to bootstrap new projects with Magic. To generate your application, simply run the command below in the shell of your choice. Be sure to replace <YOUR_PUBLISHABLE_API_KEY>
with the Publishable API Key from your Magic Dashboard.
01npx make-scoped-magic-app \\
02 --template nextjs-dedicated-wallet \\
03 --network ethereum-sepolia \\
04 --login-methods Google \\
05 --publishable-api-key <YOUR_PUBLISHABLE_API_KEY>
#Install project dependencies
For this project, we’ll need to install a few additional dependencies including the firebase
SDK and Magic OIDC extension package.
Install the following dependences to your project:
01npm install firebase @magic-ext/oidc
#Create and configure Firebase project
Head to the Firebase console and create a new project. Once you have named and created a new project, you will need to add Firebase to your web application. Click on “Web”, add a nickname for your application, and click “Register App”.
In the next step, a configuration file for Firebase will be displayed. We will need to add this to our app as we will be using this for our login/logout flow. We won’t be using this exact code block that Firebase has provided; instead, we will be taking the firebaseConfig
values and adding them to our .env
file.
Replace the empty values below with the ones displayed in the firebaseConfig
:
01# Publishable API Key found in the Magic Dashboard
02NEXT_PUBLIC_MAGIC_API_KEY=<MAGIC_PUBLISHABLE_KEY>
03
04# The RPC URL for the blockchain network
05NEXT_PUBLIC_BLOCKCHAIN_NETWORK=ethereum-sepolia
06
07# Replace the values
08NEXT_PUBLIC_FIREBASE_API_KEY=
09NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=
10NEXT_PUBLIC_FIREBASE_PROJECT_ID=
11NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=
12NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=
13NEXT_PUBLIC_FIREBASE_APP_ID=
Next, create a directory inside of src/components
named firebase
. We can now create the Firebase config file. Create a config.ts
file and paste the following code block:
01import { getAuth } from 'firebase/auth';
02import { initializeApp, getApps } from 'firebase/app';
03
04// Load .env variables
05const firebaseConfig = {
06 apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
07 authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
08 projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
09 storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET,
10 messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
11 appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID,
12};
13
14const firebaseApp =
15 getApps().length === 0 ? initializeApp(firebaseConfig) : getApps()[0];
16
17export const firebaseAuth = getAuth(firebaseApp);
That’s it for the Firebase configuration! Lets move on to the next step.
#Configure Magic with OIDC
OIDC, or OpenID Connect, is an identity layer built on top of the OAuth 2.0 protocol. It allows clients to verify the identity of users based on the authentication performed by an external authorization server, as well as to obtain basic profile information about the user. In this guide, OIDC is used to link the authentication process between Firebase and Magic, providing a smooth and simple user experience.
In order to use Magic with a custom provider, we utilize the loginWithOIDC
method imported from the @magic-ext/oidc
package. The CLI generated the application and configured it to use an OAuth extension, which does not expose the loginWithOIDC
method, so we need to replace it with the OpenIdExtension
configuration. To do this, replace the code inside src/components/magic/MagicProvider.tsx
with the following:
01import { getChainId, getNetworkUrl } from '@/utils/network';
02import { OpenIdExtension } from '@magic-ext/oidc';
03import { Magic as MagicBase } from 'magic-sdk';
04import { ReactNode, createContext, useContext, useEffect, useMemo, useState } from 'react';
05const { Web3 } = require('web3');
06
07export type Magic = MagicBase<OpenIdExtension[]>;
08
09type MagicContextType = {
10 magic: Magic | null;
11 web3: typeof Web3 | null;
12};
13
14const MagicContext = createContext<MagicContextType>({
15 magic: null,
16 web3: null,
17});
18
19export const useMagic = () => useContext(MagicContext);
20
21const MagicProvider = ({ children }: { children: ReactNode }) => {
22 const [magic, setMagic] = useState<Magic | null>(null);
23 const [web3, setWeb3] = useState<typeof Web3 | null>(null);
24
25 useEffect(() => {
26 if (process.env.NEXT_PUBLIC_MAGIC_API_KEY) {
27 const magic = new MagicBase(process.env.NEXT_PUBLIC_MAGIC_API_KEY as string, {
28 network: {
29 rpcUrl: getNetworkUrl(),
30 chainId: getChainId(),
31 },
32 extensions: [new OpenIdExtension()],
33 });
34
35 setMagic(magic);
36 setWeb3(new Web3((magic as any).rpcProvider));
37 }
38 }, []);
39
40 const value = useMemo(() => {
41 return {
42 magic,
43 web3,
44 };
45 }, [magic, web3]);
46
47 return <MagicContext.Provider value={value}>{children}</MagicContext.Provider>;
48};
49
50export default MagicProvider;
#Get provider ID from Magic
We need a way to retrieve the provider ID for Firebase. Magic provides an endpoint for developers to send a POST request that returns a payload containing the provider ID. To do that, we’ll need to add our Magic secret key to the headers, along with a body containing the following values:
issuer
: The URL of the token issueraudience
: The identifier of the audience, often the same as the IdP client ID to which the token is issueddisplay_name
: A human-readable identifier for the entitysandbox_mode
: When true, provider does not enforce the expiry claim during ID token validation, which can be useful for testing environments. This defaults to false.
Attaining these values vary across different providers, but for Firebase, we will only need to get the audience (which in this case is available as NEXT_PUBLIC_FIREBASE_PROJECT_ID
) and the display name, which can be found in the project settings in the Firebase console. The issuer will be the following URL with the project audience appended to it: https://securetoken.google.com/<AUDIENCE>
.
The payload should look similar to this:
01{
02 "issuer": "<https://securetoken.google.com/magic-test-22e91>",
03 "audience": "magic-test-22e91",
04 "display_name": "Magic Test",
05 "sandbox_mode": true
06}
We have everything we need to send the POST request to the Magic endpoint. In your terminal, paste the following code with your project’s values:
01curl --location '<https://api.magic.link/v1/api/magic_client/federated_idp>' \\
02--header 'X-Magic-Secret-Key: <MAGIC_SECRET_KEY>' \\
03--header 'Content-Type: application/json' \\
04--data '{
05 "issuer": "<https://securetoken.google.com/><AUDIENCE>",
06 "audience": "<AUDIENCE>",
07 "display_name": "<DISPLAY_NAME>",
08 "sandbox_mode": true
09}'
Check the returned object for the id
attribute. Save this in your .env
file as NEXT_PUBLIC_PROVIDER_ID
. We will be using that when we create our sign in flow using the loginWithOIDC
method!
#Add to client
Everything should be correctly set up on the authentication side; now we just need to modify the existing code to allow for logging users in and out. We'll update the code to handle user authentication, linking Firebase and Magic.
#Log in user
Navigate to src/components/magic/auth/Google.tsx
. We need to import a few things from Firebase, including the config we created earlier. Add the following imports to the root of the file:
01import {
02 GoogleAuthProvider,
03 signInWithPopup,
04} from 'firebase/auth';
05
06import { firebaseAuth } from '../../firebase/config';
Next, replace the login
function to include the signInWithPopup
method provided to us by the Firebase SDK. This authenticates a Firebase client using a popup-based OAuth authentication flow.
We then call the getIdToken
method on result.user
. This will return a deserialized JWT used to identify the user to a Firebase service.
To tie it all together, we call Magic’s loginWithOIDC
function and pass the provider ID along with the user access token. This ensures that once a user logs in using the Firebase/Google authentication, it will then link that user to a new or existing Magic wallet.
01const login = async () => {
02 setLoadingFlag('true');
03 const provider = new GoogleAuthProvider();
04
05 try {
06 const result = await signInWithPopup(firebaseAuth, provider);
07
08 if (!result || !result.user) {
09 throw new Error('Google sign in failed');
10 }
11
12 // Retrieve ID token from google sign in result
13 const accessToken = await result.user.getIdToken();
14
15 const DID = await magic?.openid.loginWithOIDC({
16 // This oidcToken comes from the identity provider
17 jwt: accessToken,
18 // This providerId from the POST request earlier
19 providerId: process.env.NEXT_PUBLIC_PROVIDER_ID ?? '',
20 });
21
22 const metadata = await magic?.user.getInfo()
23
24 setToken(DID ?? '');
25 saveUserInfo(DID ?? '', 'SOCIAL', metadata?.publicAddress ?? '');
26
27 console.log(metadata)
28 } catch (error) {
29 console.error('Error signing in with Google', error);
30 }
31};
#Log out user
Logging out from both Magic and Firebase is crucial for maintaining session security. When a user logs out, it's essential to clear their session from both the authentication provider and the wallet provider. This prevents unauthorized access and guarantees that the user's authentication tokens are invalidated, protecting potentially sensitive user information.
The CLI gives us a way to log a user out of Magic by default, but we need a way for the application to know to sign the user out of Firebase. To do this, we simply call the signOut
method provided by the Firebase SDK and pass in the firebaseAuth
config we exported from firebase/config.ts
.
When you generate a Magic application using the make-scoped-magic-app
CLI, logging a user out happens in both of the disconnect
functions in following two files:
src/components/magic/cards/UserInfoCard.tsx
src/components/magic/wallet-methods/Disconnect.tsx
Navigate to those paths and add the following imports to the root of the files:
01import { signOut } from 'firebase/auth';
02import { firebaseAuth } from '@/components/firebase/config';
Next, inside the disconnect
functions above the existing logout
function for Magic, add the Firebase signOut
function and pass in the firebaseAuth
so it looks like this:
01// Firebase sign out
02await signOut(firebaseAuth);
03// Magic sign out
04await logout(setToken, magic);
This ensures that when a Magic user logs out, they are also logged out of Firebase and the session is cleared.
That’s it for the integration and configuration! Now lets test out that it all works as expected.
#Testing it all out
Start the local development server and open your browser to http://localhost:3000. You will see a button labeled “Continue with Google”. Click the button and complete the Google authentication process. After signing in successfully, a card labeled “Wallet” will appear, displaying a wallet address and balance. This wallet is linked to the authenticated user, confirming that you have successfully integrated Magic with Firebase Auth.
#Next steps
You now know how to integrate Magic with Firebase and include the following features:
- Simple authentication with Google
- Automatic wallet creation for first-time users
- Ability to have Magic users interact with their wallets
- Ability to log in and log out of Firebase user sessions
Feel free to take a look at our final code solution. Take a look at the Firebase documentation for more information on what is possible with Magic and Firebase.