diff --git a/auth-next/custom-email-handler.js b/auth-next/custom-email-handler.js index 728baff8..5fcc1f71 100644 --- a/auth-next/custom-email-handler.js +++ b/auth-next/custom-email-handler.js @@ -28,8 +28,8 @@ function handleUserManagementQueryParams() { // Configure the Firebase SDK. // This is the minimum configuration required for the API to be used. const config = { - 'apiKey': "YOU_API_KEY" // Copy this key from the web initialization - // snippet found in the Firebase console. + 'apiKey': "YOUR_API_KEY" // Copy this key from the web initialization + // snippet found in the Firebase console. }; const app = initializeApp(config); const auth = getAuth(app); diff --git a/auth-next/email-link-auth.js b/auth-next/email-link-auth.js index 59ea2115..fde67663 100644 --- a/auth-next/email-link-auth.js +++ b/auth-next/email-link-auth.js @@ -19,7 +19,8 @@ function emailLinkActionCodeSettings() { installApp: true, minimumVersion: '12' }, - dynamicLinkDomain: 'example.page.link' + // The domain must be configured in Firebase Hosting and owned by the project. + linkDomain: 'custom-domain.com' }; // [END auth_email_link_actioncode_settings] } @@ -68,11 +69,13 @@ function emailLinkComplete() { .then((result) => { // Clear email from storage. window.localStorage.removeItem('emailForSignIn'); - // You can access the new user via result.user - // Additional user info profile not available via: - // result.additionalUserInfo.profile == null + // You can access the new user by importing getAdditionalUserInfo + // and calling it with result: + // getAdditionalUserInfo(result) + // You can access the user's profile via: + // getAdditionalUserInfo(result)?.profile // You can check if the user is new or existing: - // result.additionalUserInfo.isNewUser + // getAdditionalUserInfo(result)?.isNewUser }) .catch((error) => { // Some error occurred, you can inspect the code: error.code diff --git a/auth-next/email.js b/auth-next/email.js index b00551c8..26391f81 100644 --- a/auth-next/email.js +++ b/auth-next/email.js @@ -32,7 +32,7 @@ function signUpWithEmailPassword() { const auth = getAuth(); createUserWithEmailAndPassword(auth, email, password) .then((userCredential) => { - // Signed in + // Signed up const user = userCredential.user; // ... }) diff --git a/auth-next/link-multiple-accounts.js b/auth-next/link-multiple-accounts.js index 34c0db58..1938aafc 100644 --- a/auth-next/link-multiple-accounts.js +++ b/auth-next/link-multiple-accounts.js @@ -182,3 +182,61 @@ function unlink(providerId) { }); // [END auth_unlink_provider] } + +function accountExistsPopup(auth, facebookProvider, goToApp, promptUserForPassword, promptUserForSignInMethod, getProviderForProviderId) { + // [START account_exists_popup] + const { signInWithPopup, signInWithEmailAndPassword, linkWithCredential } = require("firebase/auth"); + + // User tries to sign in with Facebook. + signInWithPopup(auth, facebookProvider).catch((error) => { + // User's email already exists. + if (error.code === 'auth/account-exists-with-different-credential') { + // The pending Facebook credential. + const pendingCred = error.credential; + // The provider account's email address. + const email = error.customData.email; + + // Present the user with a list of providers they might have + // used to create the original account. + // Then, ask the user to sign in with the existing provider. + const method = promptUserForSignInMethod(); + + if (method === 'password') { + // TODO: Ask the user for their password. + // In real scenario, you should handle this asynchronously. + const password = promptUserForPassword(); + signInWithEmailAndPassword(auth, email, password).then((result) => { + return linkWithCredential(result.user, pendingCred); + }).then(() => { + // Facebook account successfully linked to the existing user. + goToApp(); + }); + return; + } + + // All other cases are external providers. + // Construct provider object for that provider. + // TODO: Implement getProviderForProviderId. + const provider = getProviderForProviderId(method); + // At this point, you should let the user know that they already have an + // account with a different provider, and validate they want to sign in + // with the new provider. + // Note: Browsers usually block popups triggered asynchronously, so in + // real app, you should ask the user to click on a "Continue" button + // that will trigger signInWithPopup(). + signInWithPopup(auth, provider).then((result) => { + // Note: Identity Platform doesn't control the provider's sign-in + // flow, so it's possible for the user to sign in with an account + // with a different email from the first one. + + // Link the Facebook credential. We have access to the pending + // credential, so we can directly call the link method. + linkWithCredential(result.user, pendingCred).then((userCred) => { + // Success. + goToApp(); + }); + }); + } +}); +// [END account_exists_popup] +} diff --git a/auth/link-multiple-accounts.js b/auth/link-multiple-accounts.js index 30f5a2ec..8eed77b8 100644 --- a/auth/link-multiple-accounts.js +++ b/auth/link-multiple-accounts.js @@ -166,3 +166,59 @@ function unlink(providerId) { }); // [END auth_unlink_provider] } + +function accountExistsPopup(facebookProvider, goToApp, promptUserForPassword, promptUserForSignInMethod, getProviderForProviderId) { + // [START account_exists_popup] + // User tries to sign in with Facebook. + auth.signInWithPopup(facebookProvider).catch((error) => { + // User's email already exists. + if (error.code === 'auth/account-exists-with-different-credential') { + // The pending Facebook credential. + const pendingCred = error.credential; + // The provider account's email address. + const email = error.email; + + // Present the user with a list of providers they might have + // used to create the original account. + // Then, ask the user to sign in with the existing provider. + const method = promptUserForSignInMethod(); + + if (method === 'password') { + // TODO: Ask the user for their password. + // In real scenario, you should handle this asynchronously. + const password = promptUserForPassword(); + auth.signInWithEmailAndPassword(email, password).then((result) => { + return result.user.linkWithCredential(pendingCred); + }).then(() => { + // Facebook account successfully linked to the existing user. + goToApp(); + }); + return; + } + + // All other cases are external providers. + // Construct provider object for that provider. + // TODO: Implement getProviderForProviderId. + const provider = getProviderForProviderId(method); + // At this point, you should let the user know that they already have an + // account with a different provider, and validate they want to sign in + // with the new provider. + // Note: Browsers usually block popups triggered asynchronously, so in + // real app, you should ask the user to click on a "Continue" button + // that will trigger signInWithPopup(). + auth.signInWithPopup(provider).then((result) => { + // Note: Identity Platform doesn't control the provider's sign-in + // flow, so it's possible for the user to sign in with an account + // with a different email from the first one. + + // Link the Facebook credential. We have access to the pending + // credential, so we can directly call the link method. + result.user.linkWithCredential(pendingCred).then((userCred) => { + // Success. + goToApp(); + }); + }); + } +}); +// [END account_exists_popup] +} diff --git a/auth/package.json b/auth/package.json index a6827027..b4b13f01 100644 --- a/auth/package.json +++ b/auth/package.json @@ -7,6 +7,7 @@ "license": "Apache 2.0", "dependencies": { "firebase": "^8.10.0", + "firebase-admin": "^12.0.0", "firebaseui": "^5.0.0" } } diff --git a/auth/service-worker-sessions.js b/auth/service-worker-sessions.js index babbf91e..05791463 100644 --- a/auth/service-worker-sessions.js +++ b/auth/service-worker-sessions.js @@ -162,3 +162,46 @@ function svcSignInEmail(email, password) { }); // [END auth_svc_sign_in_email] } + +function svcRedirectAdmin() { + const app = { use: (a) => {} }; + + // [START auth_svc_admin] + // Server side code. + const admin = require('firebase-admin'); + + // The Firebase Admin SDK is used here to verify the ID token. + admin.initializeApp(); + + function getIdToken(req) { + // Parse the injected ID token from the request header. + const authorizationHeader = req.headers.authorization || ''; + const components = authorizationHeader.split(' '); + return components.length > 1 ? components[1] : ''; + } + + function checkIfSignedIn(url) { + return (req, res, next) => { + if (req.url == url) { + const idToken = getIdToken(req); + // Verify the ID token using the Firebase Admin SDK. + // User already logged in. Redirect to profile page. + admin.auth().verifyIdToken(idToken).then((decodedClaims) => { + // User is authenticated, user claims can be retrieved from + // decodedClaims. + // In this sample code, authenticated users are always redirected to + // the profile page. + res.redirect('/profile'); + }).catch((error) => { + next(); + }); + } else { + next(); + } + }; + } + + // If a user is signed in, redirect to profile page. + app.use(checkIfSignedIn('/')); + // [END auth_svc_admin] +} diff --git a/firebaseserverapp-next/firebaseserverapp.js b/firebaseserverapp-next/firebaseserverapp.js new file mode 100644 index 00000000..d0ab338c --- /dev/null +++ b/firebaseserverapp-next/firebaseserverapp.js @@ -0,0 +1,27 @@ +// @ts-nocheck +// [START serverapp_auth] +import { initializeServerApp } from 'firebase/app'; +import { getAuth } from 'firebase/auth'; +import { headers } from 'next/headers'; +import { redirect } from 'next/navigation'; + +export default function MyServerComponent() { + + // Get relevant request headers (in Next.JS) + const authIdToken = headers().get('Authorization')?.split('Bearer ')[1]; + + // Initialize the FirebaseServerApp instance. + const serverApp = initializeServerApp(firebaseConfig, { authIdToken }); + + // Initialize Firebase Authentication using the FirebaseServerApp instance. + const auth = getAuth(serverApp); + + if (auth.currentUser) { + redirect('/profile'); + } + + // ... +} +// [END serverapp_auth] + +const firebaseConfig = {}; diff --git a/firebaseserverapp-next/package.json b/firebaseserverapp-next/package.json new file mode 100644 index 00000000..0836b0b2 --- /dev/null +++ b/firebaseserverapp-next/package.json @@ -0,0 +1,12 @@ +{ + "name": "firebaseserverapp-next", + "version": "1.0.0", + "scripts": { + "compile": "cp ../tsconfig.json.template ./tsconfig.json && tsc" + }, + "license": "Apache-2.0", + "dependencies": { + "firebase": "^10.0.0", + "next": "^14.1.3" + } +} diff --git a/firestore-next/test.firestore.js b/firestore-next/test.firestore.js index c961af9d..969c1654 100644 --- a/firestore-next/test.firestore.js +++ b/firestore-next/test.firestore.js @@ -920,7 +920,7 @@ describe("firestore", () => { // [START in_filter_with_array] const { query, where } = require("firebase/firestore"); - const q = query(citiesRef, where('regions', 'in', [['west_coast', 'east_coast']])); + const q = query(citiesRef, where('regions', 'in', [['west_coast'], ['east_coast']])); // [END in_filter_with_array] } }); diff --git a/firestore/test.firestore.js b/firestore/test.firestore.js index ec89172e..20032d36 100644 --- a/firestore/test.firestore.js +++ b/firestore/test.firestore.js @@ -893,7 +893,7 @@ describe("firestore", () => { // [START in_filter_with_array] citiesRef.where('regions', 'in', - [['west_coast', 'east_coast']]); + [['west_coast'], ['east_coast']]); // [END in_filter_with_array] }); diff --git a/messaging/service-worker.js b/messaging/service-worker.js index c0f875bb..1a202159 100644 --- a/messaging/service-worker.js +++ b/messaging/service-worker.js @@ -10,8 +10,9 @@ function initInSw() { // Give the service worker access to Firebase Messaging. // Note that you can only use Firebase Messaging here. Other Firebase libraries // are not available in the service worker. - importScripts('https://www.gstatic.com/firebasejs/8.10.1/firebase-app.js'); - importScripts('https://www.gstatic.com/firebasejs/8.10.1/firebase-messaging.js'); + // Replace 10.13.2 with latest version of the Firebase JS SDK. + importScripts('https://www.gstatic.com/firebasejs/10.13.2/firebase-app-compat.js'); + importScripts('https://www.gstatic.com/firebasejs/10.13.2/firebase-messaging-compat.js'); // Initialize the Firebase app in the service worker by passing in // your app's Firebase config object. diff --git a/remoteconfig-next/index.js b/remoteconfig-next/index.js index aeeb263a..287f3c99 100644 --- a/remoteconfig-next/index.js +++ b/remoteconfig-next/index.js @@ -14,6 +14,7 @@ function getInstance() { function setMinimumFetchTime() { const remoteConfig = getInstance(); // [START rc_set_minimum_fetch_time] + // The default and recommended production fetch interval for Remote Config is 12 hours remoteConfig.settings.minimumFetchIntervalMillis = 3600000; // [END rc_set_minimum_fetch_time] } diff --git a/snippets/auth-next/custom-email-handler/auth_handle_mgmt_query_params.js b/snippets/auth-next/custom-email-handler/auth_handle_mgmt_query_params.js index 6dec3399..d0a22696 100644 --- a/snippets/auth-next/custom-email-handler/auth_handle_mgmt_query_params.js +++ b/snippets/auth-next/custom-email-handler/auth_handle_mgmt_query_params.js @@ -23,8 +23,8 @@ document.addEventListener('DOMContentLoaded', () => { // Configure the Firebase SDK. // This is the minimum configuration required for the API to be used. const config = { - 'apiKey': "YOU_API_KEY" // Copy this key from the web initialization - // snippet found in the Firebase console. + 'apiKey': "YOUR_API_KEY" // Copy this key from the web initialization + // snippet found in the Firebase console. }; const app = initializeApp(config); const auth = getAuth(app); diff --git a/snippets/auth-next/email-link-auth/auth_email_link_actioncode_settings.js b/snippets/auth-next/email-link-auth/auth_email_link_actioncode_settings.js index a1480a11..010b5c09 100644 --- a/snippets/auth-next/email-link-auth/auth_email_link_actioncode_settings.js +++ b/snippets/auth-next/email-link-auth/auth_email_link_actioncode_settings.js @@ -19,6 +19,7 @@ const actionCodeSettings = { installApp: true, minimumVersion: '12' }, - dynamicLinkDomain: 'example.page.link' + // The domain must be configured in Firebase Hosting and owned by the project. + linkDomain: 'custom-domain.com' }; // [END auth_email_link_actioncode_settings_modular] \ No newline at end of file diff --git a/snippets/auth-next/email-link-auth/email_link_complete.js b/snippets/auth-next/email-link-auth/email_link_complete.js index b42b9af9..dbe1143d 100644 --- a/snippets/auth-next/email-link-auth/email_link_complete.js +++ b/snippets/auth-next/email-link-auth/email_link_complete.js @@ -26,11 +26,13 @@ if (isSignInWithEmailLink(auth, window.location.href)) { .then((result) => { // Clear email from storage. window.localStorage.removeItem('emailForSignIn'); - // You can access the new user via result.user - // Additional user info profile not available via: - // result.additionalUserInfo.profile == null + // You can access the new user by importing getAdditionalUserInfo + // and calling it with result: + // getAdditionalUserInfo(result) + // You can access the user's profile via: + // getAdditionalUserInfo(result)?.profile // You can check if the user is new or existing: - // result.additionalUserInfo.isNewUser + // getAdditionalUserInfo(result)?.isNewUser }) .catch((error) => { // Some error occurred, you can inspect the code: error.code diff --git a/snippets/auth-next/email/auth_signup_password.js b/snippets/auth-next/email/auth_signup_password.js index 00aa9bd5..936b64ce 100644 --- a/snippets/auth-next/email/auth_signup_password.js +++ b/snippets/auth-next/email/auth_signup_password.js @@ -10,7 +10,7 @@ import { getAuth, createUserWithEmailAndPassword } from "firebase/auth"; const auth = getAuth(); createUserWithEmailAndPassword(auth, email, password) .then((userCredential) => { - // Signed in + // Signed up const user = userCredential.user; // ... }) diff --git a/snippets/auth-next/link-multiple-accounts/account_exists_popup.js b/snippets/auth-next/link-multiple-accounts/account_exists_popup.js new file mode 100644 index 00000000..2c667882 --- /dev/null +++ b/snippets/auth-next/link-multiple-accounts/account_exists_popup.js @@ -0,0 +1,61 @@ +// This snippet file was generated by processing the source file: +// ./auth-next/link-multiple-accounts.js +// +// To update the snippets in this file, edit the source and then run +// 'npm run snippets'. + + // [START account_exists_popup_modular] + import { signInWithPopup, signInWithEmailAndPassword, linkWithCredential } from "firebase/auth"; + + // User tries to sign in with Facebook. + signInWithPopup(auth, facebookProvider).catch((error) => { + // User's email already exists. + if (error.code === 'auth/account-exists-with-different-credential') { + // The pending Facebook credential. + const pendingCred = error.credential; + // The provider account's email address. + const email = error.customData.email; + + // Present the user with a list of providers they might have + // used to create the original account. + // Then, ask the user to sign in with the existing provider. + const method = promptUserForSignInMethod(); + + if (method === 'password') { + // TODO: Ask the user for their password. + // In real scenario, you should handle this asynchronously. + const password = promptUserForPassword(); + signInWithEmailAndPassword(auth, email, password).then((result) => { + return linkWithCredential(result.user, pendingCred); + }).then(() => { + // Facebook account successfully linked to the existing user. + goToApp(); + }); + return; + } + + // All other cases are external providers. + // Construct provider object for that provider. + // TODO: Implement getProviderForProviderId. + const provider = getProviderForProviderId(method); + // At this point, you should let the user know that they already have an + // account with a different provider, and validate they want to sign in + // with the new provider. + // Note: Browsers usually block popups triggered asynchronously, so in + // real app, you should ask the user to click on a "Continue" button + // that will trigger signInWithPopup(). + signInWithPopup(auth, provider).then((result) => { + // Note: Identity Platform doesn't control the provider's sign-in + // flow, so it's possible for the user to sign in with an account + // with a different email from the first one. + + // Link the Facebook credential. We have access to the pending + // credential, so we can directly call the link method. + linkWithCredential(result.user, pendingCred).then((userCred) => { + // Success. + goToApp(); + }); + }); + } +}); +// [END account_exists_popup_modular] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/in_filter_with_array.js b/snippets/firestore-next/test-firestore/in_filter_with_array.js index ebe6f18b..190e9dfe 100644 --- a/snippets/firestore-next/test-firestore/in_filter_with_array.js +++ b/snippets/firestore-next/test-firestore/in_filter_with_array.js @@ -7,5 +7,5 @@ // [START in_filter_with_array_modular] import { query, where } from "firebase/firestore"; -const q = query(citiesRef, where('regions', 'in', [['west_coast', 'east_coast']])); +const q = query(citiesRef, where('regions', 'in', [['west_coast'], ['east_coast']])); // [END in_filter_with_array_modular] \ No newline at end of file diff --git a/snippets/remoteconfig-next/index/rc_set_minimum_fetch_time.js b/snippets/remoteconfig-next/index/rc_set_minimum_fetch_time.js index 131976c5..77aa9f45 100644 --- a/snippets/remoteconfig-next/index/rc_set_minimum_fetch_time.js +++ b/snippets/remoteconfig-next/index/rc_set_minimum_fetch_time.js @@ -5,5 +5,6 @@ // 'npm run snippets'. // [START rc_set_minimum_fetch_time_modular] +// The default and recommended production fetch interval for Remote Config is 12 hours remoteConfig.settings.minimumFetchIntervalMillis = 3600000; // [END rc_set_minimum_fetch_time_modular] \ No newline at end of file