Login to continue
Please sign in to the app using one of the providers. You will have the option to delete your account anytime you want.
🔸 Route '/user/login' Unprotected
Firebase auth login flow takes place at the client side. After the user successfully logs in, we receive a user object from which we can get the token and send to our server action.
Google Sign In (user/login/GoogleSignIn.tsx)
const [loading, setLoading] = useState(false);
const [error, setError] = useState("");
const { logLoginEvent } = useAnalytics();
/**
* Handle google sign in flow.
*/
async function signInWithGoogle() {
//Reset loading and error states
setLoading(true);
setError("");
try {
//Initialize the login flow using popup window
const provider = new GoogleAuthProvider();
const credentials = await signInWithPopup(auth, provider);
//If login success, we can extract the JWT token from the credentials object
const token = await credentials.user.getIdToken();
//We now send the jwt token to the server to create a session cookie
const result = await loginAction({ idToken: token });
//If success, redirect to dashboard
if (result) {
logLoginEvent("Google"); //Send event to google analytics
redirect("/dashboard?notify=login_success");
}
} catch (error: any) {
//Display desired errors if needed and reset loading states
if (error.code) {
setError(`Login failed due to error code: "${error.code}"`);
//Remove the error message after 5 seconds
setTimeout(() => {
setError("");
}, 5000);
}
} finally {
//Reset loading state
setLoading(false);
}
}
We may now process this token in the server. We'll convert the token to a firebase auth cookie and save it at the users browser. Now whenever we receive a request from this user we'll also receive the cookie thus we can verify the auth state.
LoginAction (user/actions.tsx)
/**
* Get the token from the client and save it as a firebase cookie. If login succeeds, revalidate the whole router
*/
export async function loginAction({ idToken }: loginActionProps) {
try {
if (!idToken) return;
const result = await login(idToken);
if (result) {
revalidatePath("/");
return result;
}
} catch (error: any) {
console.log("loginAction Error: ", error?.message);
return undefined;
}
}
login (lib/firebase-auth-api.ts)
/**
* Sign a user in
* @param token jwt token string from client side.
* @returns
*/
export async function login(token: string) {
try {
//Verify the token and get user data
const decodedIdToken = await adminAuth.verifyIdToken(token);
if (!decodedIdToken) return;
//Create the cookie token string to save.
const sessionCookie = await adminAuth.createSessionCookie(token, {
expiresIn: appConfig.sessionCookieDuration,
});
//Create the cookie
cookies().set(appConfig.sessionCookieName, sessionCookie, {
maxAge: appConfig.sessionCookieDuration,
httpOnly: true,
secure: true,
});
//User logged in
return true;
} catch (error: any) {
console.log("Error logging in", error?.message);
return false;
}
}
In the actual file, you'll notice that login function also creates custom claims depending on the user email. This is a demonstration of how user roles can be defined. Visit the repo for further reading.