Firebase

Firebase

Firebase is a serverless platform for unified development of applications for mobile devices and for the Web.

Built using React JS, Redux, Firebase & Styled-Components

LinkedIn Clone

LinkedIn-demo

Built using React JS, Redux, Firebase & Styled-Components within 2 days.

Features and Fuctionality

  • Login using Google (Firebase Authentication)
  • Create a new post
  • Share photos and videos (React player for videos)
  • Like posts
  • Realtime update likes and posts
  • Auto authenticate user on refresh
  • Sign Out

Future Plans

  • Might add more login methods
  • Post deleting functionality
  • Add comments on post

NOTE: PLEASE LET ME KNOW IF YOU DISCOVERED ANY BUG OR YOU HAVE ANY SUGGESTIONS


Author: AKASI1
Source code: https://github.com/AKASI1/Linkedin-Clone

#react-native #javascript #firebase #redux 

Built using React JS, Redux, Firebase & Styled-Components
Quinten  Boehm

Quinten Boehm

1660444740

How to Build A Youtube Clone Website using React, Redux and Firebase

Youtube Clone (React + Redux + Firebase)

Screenshot (78) Screenshot (79) Screenshot (80)

Getting Started with Create React App

This project was bootstrapped with Create React App.

Available Scripts

In the project directory, you can run:

npm start

Runs the app in the development mode.
Open http://localhost:3000 to view it in your browser.

The page will reload when you make changes.
You may also see any lint errors in the console.

npm test

Launches the test runner in the interactive watch mode.
See the section about running tests for more information.

npm run build

Builds the app for production to the build folder.
It correctly bundles React in production mode and optimizes the build for the best performance.

The build is minified and the filenames include the hashes.
Your app is ready to be deployed!

See the section about deployment for more information.

npm run eject

Note: this is a one-way operation. Once you eject, you can't go back!

If you aren't satisfied with the build tool and configuration choices, you can eject at any time. This command will remove the single build dependency from your project.

Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except eject will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own.

You don't have to ever use eject. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it.

Learn More

You can learn more in the Create React App documentation.

To learn React, check out the React documentation.

Code Splitting

This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting

Analyzing the Bundle Size

This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size

Making a Progressive Web App

This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app

Advanced Configuration

This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration

Deployment

This section has moved here: https://facebook.github.io/create-react-app/docs/deployment

npm run build fails to minify

This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify


Author: Ashuu11
Source code: https://github.com/Ashuu11/Youtube_Clone_Ashu
License: MIT license
 

#javascript #react-native #redux #firebase 

How to Build A Youtube Clone Website using React, Redux and Firebase
Oda  Shields

Oda Shields

1660417020

Responsive Whatsapp Clone using React, Tailwindcss and Firebase

Whatsapp-v2-clone

Live Preview :

whatsapp v1 clone 

 

Screenshot-31

Built with:

- ReactJs Typescript
- TailwindCss
- Framer Motion
- Firebase

Make sure you have:

- Git
- Nodejs version 14 or higher (we recommend using nvm)
- yarn ( or npm )

Run it localy:

- Open terminal and clone the repo : 
    git clone https://github.com/IslemMedjahdi/whatsapp-v2-clone/
- then : 
    yarn install
- then : 
      create a .env file in the root folder then put your firebase configuration ( check firebase documentation to get started )
        REACT_APP_FIREBASE_API_KEY = 
        REACT_APP_FIREBASE_AUTH_DOMAIN = 
        REACT_APP_FIREBASE_PROJECT_ID = 
        REACT_APP_FIREBASE_STORAGE_BUCKET = 
        REACT_APP_FIREBASE_MESSAGING_SENDER_ID  = 
        REACT_APP_FIREBASE_APP_ID  = 
- then : 
      yarn start

Author: IslemMedjahdi
Source code: https://github.com/IslemMedjahdi/whatsapp-v2-clone

#react #typescript #javascript #firebase #tailwindcss 

Responsive Whatsapp Clone using React, Tailwindcss and Firebase

Firebase Notifications Handler For Flutter

  • Simple notifications handler which provides callbacks like onTap which really make it easy to handle notification taps and a lot more.

Screenshots

     

Getting Started

Step 1: Before you can add Firebase to your app, you need to create a Firebase project to connect to your application. Visit Understand Firebase Projects to learn more about Firebase projects.

Step 2: To use Firebase in your app, you need to register your app with your Firebase project. Registering your app is often called "adding" your app to your project.

Also, register a web app if using on the web. Follow on the screen instructions to initialize the project.

Add the latest version 'firebase-messaging' CDN from here in index.html. (Tested on version 8.6.1)

Step 3: Add a Firebase configuration file and the SDK's. (google-services)

Step 4: Lastly, add firebase_core as a dependency in your pubspec.yaml file. and call Firebase.initializeApp() in the main method as shown:

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  runApp(_MainApp());
}

Android

Add the default channel in AndroidManifest in the <application> tag. Pass the same in the channelId parameter in the FirebaseNotificationsHandler widget to enable custom sounds.

<meta-data
    android:name="com.google.firebase.messaging.default_notification_channel_id"
    android:value="Notifications" />

The android:value should be the same as the channel id in FirebaseNotificationsHandler. The default value for channel id is "Notifications".

Also, add this intent-filter in AndroidManifest in the <activity> tag with android:name=".MainActivity"

<intent-filter>
    <action android:name="FLUTTER_NOTIFICATION_CLICK" />
    <category android:name="android.intent.category.DEFAULT" />
</intent-filter>

Web

Provide the vapidKey in FirebaseNotificationsHandler from the cloud messaging settings by generating a new Web push certificate

Add this script tag in index.html after adding the firebase config script

<script>
if ("serviceWorker" in navigator) {
  window.addEventListener("load", function () {
    // navigator.serviceWorker.register("/flutter_service_worker.js");
    navigator.serviceWorker.register("/firebase-messaging-sw.js");
  });
}
</script>

Now, finally create a file firebase-messaging-sw.js in the web folder itself and paste the following contents. Add your own firebase app config here.

importScripts("https://www.gstatic.com/firebasejs/7.15.5/firebase-app.js");
importScripts("https://www.gstatic.com/firebasejs/7.15.5/firebase-messaging.js");

firebase.initializeApp(
    // YOUR FIREBASE CONFIG MAP HERE
);

const messaging = firebase.messaging();
messaging.setBackgroundMessageHandler(function (payload) {
    const promiseChain = clients
        .matchAll({
            type: "window",
            includeUncontrolled: true
        })
        .then(windowClients => {
            for (let i = 0; i < windowClients.length; i++) {
                const windowClient = windowClients[i];
                windowClient.postMessage(payload);
            }
        })
        .then(() => {
            return registration.showNotification("New Message");
        });
    return promiseChain;
});
self.addEventListener('notificationclick', function (event) {
    console.log('notification received: ', event)
});

Custom Sound

Adding custom notification sounds in Android

  • Add the audio file in android/app/src/main/res/raw/audio_file_here

Adding custom notification sounds in iOS

  • Add the audio file in Runner/Resources/audio_file_here

Usage

To use this plugin, add firebase_notifications_handler as a dependency in your pubspec.yaml file.

  dependencies:
    flutter:
      sdk: flutter
    firebase_notifications_handler:

First and foremost, import the widget.

import 'package:firebase_notifications_handler/firebase_notifications_handler.dart';

Wrap the FirebaseNotificationsHandler on a widget to enable your application to receive notifications. Typically wrap it on the screen, when you have all the initial setup done. (like on the home screen).

When the app launches, the splash screen typically loads all the stuff, initializes the users and sends to the home screen, then the onTap will trigger, and can be handled accordingly from the callback.

If wrapped on the material app, then you might push the user to the specified screen too early, before initializing the user or something that you need.

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return FirebaseNotificationsHandler(
      child: HomeScreen(),
    );
  }
}

Although, the widget automatically initializes the fcmToken, but if the FCM token is needed before the widget is built, then use the initializeFCMToken() function to initialize the token. Which will return the initialized token.

Also, keep in mind, when the widget is built, the onFCMTokenInitialize callback will also fire, with the same token.

There are multiple parameters that can be passed to the widget, some of them are shown.

FirebaseNotificationsHandler(
    onFCMTokenInitialize: (_, token) => fcmToken = token,
    onFCMTokenUpdate: (_, token) {
        fcmToken = token;
        // await User.updateFCM(token);
    },
    onTap: (navigatorState, appState, payload) {
        print("Notification tapped with $appState & payload $payload");

        final context = navigatorState.currentContext!;
        navigatorState.currentState!.pushNamed('newRouteName');
        // OR
        Navigator.pushNamed(context, 'newRouteName');
    },
    channelId: 'ChannelId',
    enableLogs: true,

    // ... and a lot more
),

You can check the remaining parameters here. They are fully documented and won't face an issue while using them

Trigger FCM Notification

You can use the in-built sendNotification static method on the FirebaseNotificationsHandler widget to trigger the notification.

await FirebaseNotificationsHandler.sendNotification(
  cloudMessagingServerKey: '<YOUR_CLOUD_MESSAGING_SERVER_KEY>',
  title: 'This is a test notification',
  body: 'This describes this notification',
  fcmTokens: [
    'fcmDeviceToken1',
    'fcmDeviceToken2',
  ],
  payload: {
    'key': 'value',
  },
);

OR

Send notification using REST API

To send FCM notification using REST API:

Make a POST request @https://fcm.googleapis.com/fcm/send

Also, add 2 headers:

Content-Type: application/json
Authorization: key=<SERVER_KEY_FROM_FIREBASE_CLOUD_MESSAGING>

You can find the server key from the cloud messaging settings in the firebase console.

The body is framed as follows:

{
      "to": "<FCM_TOKEN_HERE>",
      "registration_ids": [],

      "notification": {
            "title": "Title here",
            "body": "Body here",
            "image": "Image url here"
      },
      "data": {
            "click_action":"FLUTTER_NOTIFICATION_CLICK"
      }
}

You can pass all the fcm tokens in the "registration_ids" list if there are multiple users or only pass one fcm token in the "to" parameter for single user.

Add all the rest of the payload data in "data" field which will be provided in the onTap callback.

Sample Usage

import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_notifications_handler/firebase_notifications_handler.dart';
import 'package:flutter/material.dart';
import 'package:notifications_handler_demo/firebase_options.dart';
import 'package:notifications_handler_demo/screens/splash_screen.dart';
import 'package:notifications_handler_demo/utils/app_theme.dart';
import 'package:notifications_handler_demo/utils/globals.dart';
import 'package:notifications_handler_demo/utils/helpers.dart';
import 'package:notifications_handler_demo/utils/route_generator.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
  runApp(const _MainApp());
}

class _MainApp extends StatelessWidget {
  static const id = '_MainApp';

  const _MainApp({
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return FirebaseNotificationsHandler(
      defaultNavigatorKey: Globals.navigatorKey,
      onOpenNotificationArrive: (_, payload) {
        log(
          id,
          msg: "Notification received while app is open with payload $payload",
        );
      },
      onTap: (navigatorState, appState, payload) {
        showSnackBar('appState: $appState\npayload: $payload');
        log(
          id,
          msg: "Notification tapped with $appState & payload $payload",
        );
      },
      onFCMTokenInitialize: (_, token) => Globals.fcmToken = token,
      onFCMTokenUpdate: (_, token) => Globals.fcmToken = token,
      child: MaterialApp(
        debugShowCheckedModeBanner: false,
        title: 'FirebaseNotificationsHandler Demo',
        navigatorKey: Globals.navigatorKey,
        scaffoldMessengerKey: Globals.scaffoldMessengerKey,
        theme: AppTheme.lightTheme,
        darkTheme: AppTheme.darkTheme,
        onGenerateRoute: RouteGenerator.generateRoute,
        initialRoute: SplashScreen.id,
      ),
    );
  }
}

See the example directory for a complete sample app.

Created & Maintained By Rithik Bhandari

Use this package as a library

Depend on it

Run this command:

With Flutter:

 $ flutter pub add firebase_notifications_handler

This will add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get):

dependencies:
  firebase_notifications_handler: ^1.0.9

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

Import it

Now in your Dart code, you can use:

import 'package:firebase_notifications_handler/firebase_notifications_handler.dart'; 

example/lib/main.dart

import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_notifications_handler/firebase_notifications_handler.dart';
import 'package:flutter/material.dart';
import 'package:notifications_handler_demo/firebase_options.dart';
import 'package:notifications_handler_demo/screens/splash_screen.dart';
import 'package:notifications_handler_demo/utils/app_theme.dart';
import 'package:notifications_handler_demo/utils/globals.dart';
import 'package:notifications_handler_demo/utils/helpers.dart';
import 'package:notifications_handler_demo/utils/route_generator.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
  runApp(const _MainApp());
}

class _MainApp extends StatelessWidget {
  static const id = '_MainApp';

  const _MainApp({
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return FirebaseNotificationsHandler(
      defaultNavigatorKey: Globals.navigatorKey,
      onOpenNotificationArrive: (_, payload) {
        log(
          id,
          msg: "Notification received while app is open with payload $payload",
        );
      },
      onTap: (navigatorState, appState, payload) {
        showSnackBar('appState: $appState\npayload: $payload');
        log(
          id,
          msg: "Notification tapped with $appState & payload $payload",
        );
      },
      onFCMTokenInitialize: (_, token) => Globals.fcmToken = token,
      onFCMTokenUpdate: (_, token) => Globals.fcmToken = token,
      child: MaterialApp(
        debugShowCheckedModeBanner: false,
        title: 'FirebaseNotificationsHandler Demo',
        navigatorKey: Globals.navigatorKey,
        scaffoldMessengerKey: Globals.scaffoldMessengerKey,
        theme: AppTheme.lightTheme,
        darkTheme: AppTheme.darkTheme,
        onGenerateRoute: RouteGenerator.generateRoute,
        initialRoute: SplashScreen.id,
      ),
    );
  }
} 

Download Details:

Author: rithik-dev

Source Code: https://github.com/rithik-dev/firebase_notifications_handler

#firebase  #notification #flutter 

Firebase Notifications Handler For Flutter

Firebase Phone Auth Handler For Flutter

FirebasePhoneAuthHandler For Flutter

  • An easy-to-use firebase phone authentication package to easily send and verify OTP's with auto-fetch OTP support via SMS.
  • Supports OTP on web out of the box.

Screenshots

           

Getting Started

Step 1: Before you can add Firebase to your app, you need to create a Firebase project to connect to your application. Visit Understand Firebase Projects to learn more about Firebase projects.

Step 2: To use Firebase in your app, you need to register your app with your Firebase project. Registering your app is often called "adding" your app to your project.

Also, register a web app if using on the web. Follow on the screen instructions to initialize the project.

Add the latest version 'firebase-auth' CDN from here. (Tested on version 8.6.1)

Step 3: Add a Firebase configuration file and the SDK's. (google-services)

Step 4: When the basic setup is done, open the console and then the project and head over to Authentication from the left drawer menu.

Step 5: Click on Sign-in method next to the Users tab and enable Phone.

Step 6: Follow the additional configuration steps for the platforms to avoid any errors.

Step 7: IMPORTANT: Do not forget to enable the Android Device Verification service from Google Cloud Platform. (make sure the correct project is selected).

Step 8: Lastly, add firebase_core as a dependency in your pubspec.yaml file. and call Firebase.initializeApp() in the main method as shown:

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  runApp(_MainApp());
}

Usage

To use this plugin, add firebase_phone_auth_handler as a dependency in your pubspec.yaml file.

  dependencies:
    flutter:
      sdk: flutter
    firebase_phone_auth_handler:

First and foremost, import the widget.

import 'package:firebase_phone_auth_handler/firebase_phone_auth_handler.dart';

Wrap the MaterialApp with FirebasePhoneAuthProvider to enable your application to support phone authentication like shown.

class _MainApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return FirebasePhoneAuthProvider(
      child: MaterialApp(
        debugShowCheckedModeBanner: false,
        home: HomeScreen(),
      ),
    );
  }
}

You can now add a FirebasePhoneAuthHandler widget to your widget tree and pass all the required parameters to get started.

FirebasePhoneAuthHandler(
    phoneNumber: "+919876543210",
    builder: (context, controller) {
        return SizedBox.shrink();
    },
),

The phone number is the number to which the OTP will be sent which should be formatted in the following way:

+919876543210 - where +91 is the country code and 9876543210 is the phone number.

The widget returned from the builder is rendered on the screen. The builder exposes a controller which contains various variables and methods.

Callbacks such as onLoginSuccess or onLoginFailed can be passed to the widget.

onLoginSuccess is called whenever the otp was sent to the mobile successfully and was either auto verified or verified manually by calling verifyOTP function in the controller. The callback exposes UserCredential object which can be used to find user UID and other stuff. The boolean provided is whether the OTP was auto verified or verified manually be calling verifyOTP. True if auto verified and false is verified manually.

onLoginFailed is called if an error occurs while sending OTP or verifying the OTP or any internal error occurs, callback is triggered exposing FirebaseAuthException which can be used to handle the error.

onCodeSent is called when the OTP is successfully sent to the phone number.

FirebasePhoneAuthHandler(
    phoneNumber: "+919876543210",
    // If true, the user is signed out before the onLoginSuccess callback is fired when the OTP is verified successfully.
    signOutOnSuccessfulVerification: false,
    
    linkWithExistingUser: false,
    builder: (context, controller) {
      return SizedBox.shrink();
    },
    onLoginSuccess: (userCredential, autoVerified) {
      debugPrint("autoVerified: $autoVerified");
      debugPrint("Login success UID: ${userCredential.user?.uid}");
    },
    onLoginFailed: (authException, stackTrace) {
      debugPrint("An error occurred: ${authException.message}");
    },
    onError: (error, stackTrace) {},
),

To logout the current user(if any), simply call

await FirebasePhoneAuthHandler.signOut(context);

controller.signOut() can also be used to logout the current user if the functionality is needed in the same screen as the widget itself (where controller is the variable passed in the callback from the builder method in the widget).

Web (reCAPTCHA)

By default, the reCAPTCHA widget is a fully managed flow which provides security to your web application. The widget will render as an invisible widget when the sign-in flow is triggered. An "invisible" widget will appear as a full-page modal on-top of your application like demonstrated below.

reCAPTCHA1

Although, a RecaptchaVerifier instance can be passed which can be used to manage the widget.

Use the function recaptchaVerifierForWebProvider in FirebasePhoneAuthHandler which gives a boolean to check whether the current platform is Web or not.

NOTE: Do not pass a RecaptchaVerifier instance if the platform is not web, else an error occurs.

Example:

recaptchaVerifierForWebProvider: (isWeb) {
    if (isWeb) return RecaptchaVerifier();
},

It is however possible to display an inline widget which the user has to explicitly press to verify themselves.

reCAPTCHA2

To add an inline widget, specify a DOM element ID to the container argument of the RecaptchaVerifier instance. The element must exist and be empty otherwise an error will be thrown. If no container argument is provided, the widget will be rendered as "invisible".

RecaptchaVerifier(
  container: 'recaptcha',
  size: RecaptchaVerifierSize.compact,
  theme: RecaptchaVerifierTheme.dark,
  onSuccess: () => print('reCAPTCHA Completed!'),
  onError: (FirebaseAuthException error) => print(error),
  onExpired: () => print('reCAPTCHA Expired!'),
),

If the reCAPTCHA badge does not disappear automatically after authentication is done, try adding the following code in onLoginSuccess so that it disappears when the login process is done.

Firstly import querySelector from dart:html.

import 'dart:html' show querySelector;

Then add this in onLoginSuccess callback.

final captcha = querySelector('#__ff-recaptcha-container');
if (captcha != null) captcha.hidden = true;

If you want to completely disable the reCAPTCHA badge (typically appears on the bottom right), add this CSS style in the web/index.html outside any other tag.

<style>
    .grecaptcha-badge { visibility: hidden; }
</style>

How I prefer using it usually

I usually have a phone number input field, which handles phone number input. Then pass the phone number to the VerifyPhoneNumberScreen widget from the example app.

// probably some ui or dialog to get the phone number
final phoneNumber = _getPhoneNumber();

// then call
void _verifyPhoneNumber() async {
  Navigator.push(
    context,
    MaterialPageRoute(
      builder: (_) => VerifyPhoneNumberScreen(phoneNumber: phoneNumber),
    ),
  );
}

/// route to home screen or somewhere in the onLoginSuccess callback for [VerifyPhoneNumberScreen] 

Sample Usage

import 'package:firebase_phone_auth_handler/firebase_phone_auth_handler.dart';
import 'package:flutter/material.dart';
import 'package:phone_auth_handler_demo/screens/home_screen.dart';
import 'package:phone_auth_handler_demo/utils/helpers.dart';
import 'package:phone_auth_handler_demo/widgets/custom_loader.dart';
import 'package:phone_auth_handler_demo/widgets/pin_input_field.dart';

class VerifyPhoneNumberScreen extends StatefulWidget {
  static const id = 'VerifyPhoneNumberScreen';

  final String phoneNumber;

  const VerifyPhoneNumberScreen({
    Key? key,
    required this.phoneNumber,
  }) : super(key: key);

  @override
  State<VerifyPhoneNumberScreen> createState() =>
      _VerifyPhoneNumberScreenState();
}

class _VerifyPhoneNumberScreenState extends State<VerifyPhoneNumberScreen>
    with WidgetsBindingObserver {
  bool isKeyboardVisible = false;

  late final ScrollController scrollController;

  @override
  void initState() {
    scrollController = ScrollController();
    WidgetsBinding.instance.addObserver(this);
    super.initState();
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    scrollController.dispose();
    super.dispose();
  }

  @override
  void didChangeMetrics() {
    final bottomViewInsets = WidgetsBinding.instance.window.viewInsets.bottom;
    isKeyboardVisible = bottomViewInsets > 0;
  }

  // scroll to bottom of screen, when pin input field is in focus.
  Future<void> _scrollToBottomOnKeyboardOpen() async {
    while (!isKeyboardVisible) {
      await Future.delayed(const Duration(milliseconds: 50));
    }

    await Future.delayed(const Duration(milliseconds: 250));

    await scrollController.animateTo(
      scrollController.position.maxScrollExtent,
      duration: const Duration(milliseconds: 250),
      curve: Curves.easeIn,
    );
  }

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: FirebasePhoneAuthHandler(
        phoneNumber: widget.phoneNumber,
        signOutOnSuccessfulVerification: false,
        linkWithExistingUser: false,
        autoRetrievalTimeOutDuration: const Duration(seconds: 60),
        otpExpirationDuration: const Duration(seconds: 60),
        onCodeSent: () {
          log(VerifyPhoneNumberScreen.id, msg: 'OTP sent!');
        },
        onLoginSuccess: (userCredential, autoVerified) async {
          log(
            VerifyPhoneNumberScreen.id,
            msg: autoVerified
                ? 'OTP was fetched automatically!'
                : 'OTP was verified manually!',
          );

          showSnackBar('Phone number verified successfully!');

          log(
            VerifyPhoneNumberScreen.id,
            msg: 'Login Success UID: ${userCredential.user?.uid}',
          );

          Navigator.pushNamedAndRemoveUntil(
            context,
            HomeScreen.id,
            (route) => false,
          );
        },
        onLoginFailed: (authException, stackTrace) {
          log(
            VerifyPhoneNumberScreen.id,
            msg: authException.message,
            error: authException,
            stackTrace: stackTrace,
          );

          switch (authException.code) {
            case 'invalid-phone-number':
              // invalid phone number
              return showSnackBar('Invalid phone number!');
            case 'invalid-verification-code':
              // invalid otp entered
              return showSnackBar('The entered OTP is invalid!');
            // handle other error codes
            default:
              showSnackBar('Something went wrong!');
            // handle error further if needed
          }
        },
        onError: (error, stackTrace) {
          log(
            VerifyPhoneNumberScreen.id,
            error: error,
            stackTrace: stackTrace,
          );

          showSnackBar('An error occurred!');
        },
        builder: (context, controller) {
          return Scaffold(
            appBar: AppBar(
              leadingWidth: 0,
              leading: const SizedBox.shrink(),
              title: const Text('Verify Phone Number'),
              actions: [
                if (controller.codeSent)
                  TextButton(
                    onPressed: controller.isOtpExpired
                        ? () async {
                            log(VerifyPhoneNumberScreen.id, msg: 'Resend OTP');
                            await controller.sendOTP();
                          }
                        : null,
                    child: Text(
                      controller.isOtpExpired
                          ? 'Resend'
                          : '${controller.otpExpirationTimeLeft.inSeconds}s',
                      style: const TextStyle(color: Colors.blue, fontSize: 18),
                    ),
                  ),
                const SizedBox(width: 5),
              ],
            ),
            body: controller.isSendingCode
                ? Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    crossAxisAlignment: CrossAxisAlignment.center,
                    children: const [
                      CustomLoader(),
                      SizedBox(height: 50),
                      Center(
                        child: Text(
                          'Sending OTP',
                          style: TextStyle(fontSize: 25),
                        ),
                      ),
                    ],
                  )
                : ListView(
                    padding: const EdgeInsets.all(20),
                    controller: scrollController,
                    children: [
                      Text(
                        "We've sent an SMS with a verification code to ${widget.phoneNumber}",
                        style: const TextStyle(fontSize: 25),
                      ),
                      const SizedBox(height: 10),
                      const Divider(),
                      if (controller.isListeningForOtpAutoRetrieve)
                        Column(
                          children: const [
                            CustomLoader(),
                            SizedBox(height: 50),
                            Text(
                              'Listening for OTP',
                              textAlign: TextAlign.center,
                              style: TextStyle(
                                fontSize: 25,
                                fontWeight: FontWeight.w600,
                              ),
                            ),
                            SizedBox(height: 15),
                            Divider(),
                            Text('OR', textAlign: TextAlign.center),
                            Divider(),
                          ],
                        ),
                      const SizedBox(height: 15),
                      const Text(
                        'Enter OTP',
                        style: TextStyle(
                          fontSize: 20,
                          fontWeight: FontWeight.w600,
                        ),
                      ),
                      const SizedBox(height: 15),
                      PinInputField(
                        length: 6,
                        onFocusChange: (hasFocus) async {
                          if (hasFocus) await _scrollToBottomOnKeyboardOpen();
                        },
                        onSubmit: (enteredOtp) async {
                          final verified =
                              await controller.verifyOtp(enteredOtp);
                          if (verified) {
                            // number verify success
                            // will call onLoginSuccess handler
                          } else {
                            // phone verification failed
                            // will call onLoginFailed or onError callbacks with the error
                          }
                        },
                      ),
                    ],
                  ),
          );
        },
      ),
    );
  }
}

See the example directory for a complete sample app.

Created & Maintained By Rithik Bhandari

Use this package as a library

Depend on it

Run this command:

With Flutter:

 $ flutter pub add firebase_phone_auth_handler

This will add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get):

dependencies:
  firebase_phone_auth_handler: ^1.0.6

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

Import it

Now in your Dart code, you can use:

import 'package:firebase_phone_auth_handler/firebase_phone_auth_handler.dart'; 

example/lib/main.dart

import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_phone_auth_handler/firebase_phone_auth_handler.dart';
import 'package:flutter/material.dart';
import 'package:phone_auth_handler_demo/firebase_options.dart';
import 'package:phone_auth_handler_demo/screens/splash_screen.dart';
import 'package:phone_auth_handler_demo/utils/app_theme.dart';
import 'package:phone_auth_handler_demo/utils/globals.dart';
import 'package:phone_auth_handler_demo/utils/route_generator.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
  runApp(const _MainApp());
}

class _MainApp extends StatelessWidget {
  const _MainApp({
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return FirebasePhoneAuthProvider(
      child: MaterialApp(
        debugShowCheckedModeBanner: false,
        title: 'FirebasePhoneAuthHandler Demo',
        scaffoldMessengerKey: Globals.scaffoldMessengerKey,
        theme: AppTheme.lightTheme,
        darkTheme: AppTheme.darkTheme,
        onGenerateRoute: RouteGenerator.generateRoute,
        initialRoute: SplashScreen.id,
      ),
    );
  }
} 

Download Details:

Author: rithik-dev

Source Code: https://github.com/rithik-dev/firebase_phone_auth_handler

#flutter #auth #firebase 

Firebase Phone Auth Handler For Flutter
React Native

React Native

1660373535

A Simple ReactJS Package Ment to Make ReactJS/NextJS Firebase

ReactFBAuth

A simple react package ment to make React/NextJS firebase authentication easy.

Built with Firebase web V9

currently supports email/password and Google authentication, more will be added soon

Setup and Usage

Create a Firebase config file

This file will house your Firebase config as well as auth object and its providers. Config object comes from Firebase console.

import { initializeApp } from "firebase/app";
import { getAuth, GoogleAuthProvider } from "firebase/auth";

const firebaseConfig = {
  apiKey: "API-KEY",
  authDomain: "EXAMPLE-DOMAIN.firebaseapp.com",
  projectId: "PROJECT-ID",
  storageBucket: "DOMAIN.appspot.com",
  messagingSenderId: "ID",
  appId: "ID",
  measurementId: "ID",
};

// Initialize Firebase
const app = initializeApp(firebaseConfig);

// init auth
export const auth = getAuth(app);

// init google provider
export const gProvider = new GoogleAuthProvider();
gProvider.setCustomParameters({ prompt: "select_account" });

Add the provider to main

The provider takes in the auth object, the google provider, and the persistence type (https://firebase.google.com/docs/auth/web/auth-state-persistence).

import { auth, gProvider } from "./firebase_client";
import { browserLocalPersistence } from "firebase/auth";

ReactDOM.createRoot(document.getElementById("root")!).render(
  <FBAuthProvider
    fb_auth={auth}
    g_provider={gProvider}
    persistence_type={browserLocalPersistence}
  >
    <React.StrictMode>
      <App />
    </React.StrictMode>
  </FBAuthProvider>
);

Use anywhere

Basic App.tsx example:

import { useFBAuth } from "@matterhorn-studios/react-fb-auth";

function App() {
  const { user, loading, error, googleSignIn, logOut } = useFBAuth();

  if (loading) return <h1>LOADING</h1>;
  if (error) return <h1>Error: {error.message}</h1>;
  return (
    <div>
      {user ? (
        <>
          <div>{user.displayName}</div>
          <button onClick={logOut}>log out</button>
        </>
      ) : (
        <>
          <div>no user found</div>
          <button onClick={googleSignIn}>google sign in</button>
        </>
      )}
    </div>
  );
}

export default App;

Methods on useFBAuth()

  • googleSignIn Prompts the user to sign in with google, populates the user, loading, and error object
  • emailSignIn(email: string, password: string) Signs the user in with the provided email and password, populates the user, loading, and error object
  • emailSignUp(email: string, password: string) Signs the user up with the provided email and password, populates the user, loading, and error object
  • logOut Logs the user out
  • clearError Clears the error object (resets it to null)

Author: Matterhorn-Studios
Source code: https://github.com/Matterhorn-Studios/ReactFBAuth

#react #typescript #NextJS #firebase 

A Simple ReactJS Package Ment to Make ReactJS/NextJS Firebase
Marisol  Kuhic

Marisol Kuhic

1660295040

Discord Like Chatrooms with Firebase, NextJS, Chakra-UI

F.R.I.E.N.D.S chatrooms

made using next.js, firebase (firestore and auth), chakra-ui

Demo App

Contact Me at bijay.work37@gmail.com as many people have used the guest account now so easier to find me by email deployed on vercel

Built using

Front-end Tech

  • ReactJS - Frontend frameworkaction creators to return a function
  • Next.js - React production framework
  • Chakra-UI - Chakra-UI & Icons library
  • React Icons - React Icons library
  • date-fns - Library for manipulating/formatting of timestamps

Back-end Tech

  • Firebase - Platform for mobile and web development

Features

  • Authentication with firebase auth connected to google, or simple email and password
  • Guest Login and account
  • Chat with a single person added by email
  • Chatroom with multiple people
  • Sidebar chat navigation for all the chatrooms
  • Responsive UI for desktop and mobile
  • Loading spinners for backend processes

Usage

Client:

Change firebase config file to connect your own firebase app

Run client:

cd client
npm install
npm start

Author: Bijayb37
Source code: https://github.com/Bijayb37/Firebase-chatrooms
 

#react-native  #typescript #nextjs #firebase 

Discord Like Chatrooms with Firebase, NextJS, Chakra-UI

Laser Chat 2.0: Open Chat Web Created with React and Google Firebase

Laser Chat 2.0

preview1 preview2

This is an open chat web app that I made using Google Firebase.

Hosting a server

To host your own chat server, first open the terminal and type in git clone https://github.com/EesaZahed/laser-chat.git. Next, type cd laser-chat and npm i in the terminal.

Next, create a Google Firebase web project at https://console.firebase.google.com/, and enable a firestore database, and also add a method for user authentication with an the users Google Account. Once you create your project, go to your project settings, and copy the firebaseConfig object, and paste it in src/firebase/config.js.

To finally run it, type in the terminal of your project's directory npm start.

Your chat server is online and anyone with your API keys in the firebaseConfig object can edit your database. For more information about Firebase security, please read more about this at https://firebase.google.com/docs/projects/api-keys.

For your database rules, use the following:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /users/{id} {
      allow read, create, update: if isUserAuthenticated();
    }
    match /messages/{id} {
      allow read, create : if isUserAuthenticated();
    }
    function isUserAuthenticated() {
      return request.auth.uid != null;
    }
  }
}

Author: EesaZahed
Source code: https://github.com/EesaZahed/laser-chat-2.0
 

#javascript #react-native #firebase 

Laser Chat 2.0: Open Chat Web Created with React and Google Firebase
Ruthie  Blanda

Ruthie Blanda

1660189620

Todo List CRUD and OAuth with Firebase and React

Todo List CRUD and OAuth with Firebase

Esta es una app hecha con React y Firebase en la que puedas crear, leer, actualizar y borrar tareas dentro de una lista de tareas. El usuario solo pude crear, leer, actualizar y borrar las tareas que le pertenecen. Para eso el usuario debe estar logueado con Google, Facebook o Github.

Features

  • Login con Google
  • Login con Facebook
  • Login con GitHub
  • Crear tareas
  • Leer tareas
  • Actualizar tareas
  • Borrar tareas

Screenshots

image

image

Demo

Corre en local

Clona el repositorio

  git clone https://github.com/adonyssantos/cic-evaluation-app.git

Ve a la carpeta del proyecto

  cd cic-evaluation-app

Instala las dependencias

  npm install

Ejecuta la app

  npm run start

Funcionamiento

Autenticación

El usuario debe estar logueado para poder crear, leer, actualizar y borrar tareas. Para eso se utiliza una autenticación con Firebase. Se utilizan 3 providers para el login: Google, Facebook y Github.

Dentro de la carpeta src/utils/firebase-auth.js se encuentra toda la lógica de autenticación.

Funciones:

  • loginWithGoogle
  • loginWithFacebook
  • loginWithGithub
  • logout

Se almacenara en un state la información del usuario logueado. (email, photoUrl, uid...)

Base de datos

La base de datos utilizada es Firestore. Solo existe una colección llamada todos. Aquí se va a guardar toda la información.

  • id: Es el identificador de la tarea.
  • title: Es el título de la tarea.
  • completed: Es un booleano que indica si la tarea está completada o no.
  • userRef: Id del usuario que creó la tarea.

Dentro de la carpeta src/utils/firebase-db.js se encuentra toda la lógica de base de datos.

Funciones:

  • createTodo
  • readTodos
  • updateTodo
  • deleteTodo

Al momento de leer la colección, se va a filtrar utilizando el id del usuario que está logueado. Ejemplo: userRef == user.uid.

Hosting

La aplicación esta alojada en Firebase.

Comando para alojar la app:

  firebase deploy

Autores


Author: adonyssantos
Source code: https://github.com/adonyssantos/todo-list-react-firebase
 

#react-native #javascript #firebase 

Todo List CRUD and OAuth with Firebase and React
Chloe  Butler

Chloe Butler

1660166940

Netflix Clone: How to Build A Netflix Clone using ReactJS & Firebase

 Netflix Clone Built Using React.JS & Firebase

This is a clone of Netflix website built using React.JS as a Front-end. It's not a replica, and it doesn't have all the features of Netflix website. it's a similar version of Netflix with my own design touch, showing my abilities in React.JS to build something advancedlike Netflix. It contains the home page, sign-in page, sign-up page, browse page. 

Take a look at the live version here: https://netflix-clonee01.netlify.app/ :octocat: :heart_eyes:

Home Page netflix-getStarted

Sign-in Page netflix-Login

Browse Page netflix-main netflix-main2

Technologies

Basically, this project was developed based on the following technologies:

React JS    TMDB   

Getting Started with Create React App

This project was bootstrapped with Create React App.

Available Scripts

In the project directory, you can run:

npm start

Runs the app in the development mode.
Open http://localhost:3000 to view it in your browser.

The page will reload when you make changes.
You may also see any lint errors in the console.

npm test

Launches the test runner in the interactive watch mode.
See the section about running tests for more information.

npm run build

Builds the app for production to the build folder.
It correctly bundles React in production mode and optimizes the build for the best performance.

The build is minified and the filenames include the hashes.
Your app is ready to be deployed!

See the section about deployment for more information.

npm run eject

Note: this is a one-way operation. Once you eject, you can't go back!

If you aren't satisfied with the build tool and configuration choices, you can eject at any time. This command will remove the single build dependency from your project.

Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except eject will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own.

You don't have to ever use eject. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it.

Learn More

You can learn more in the Create React App documentation.

To learn React, check out the React documentation.

Code Splitting

This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting

Analyzing the Bundle Size

This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size

Making a Progressive Web App

This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app

Advanced Configuration

This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration

Deployment

This section has moved here: https://facebook.github.io/create-react-app/docs/deployment

npm run build fails to minify

This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify


Author: MuradRahmanzada
Source code: https://github.com/MuradRahmanzada/Netflix-clone
 

#react-native #javascript #firebase 

Netflix Clone: How to Build A Netflix Clone using ReactJS & Firebase
Marisol  Kuhic

Marisol Kuhic

1660138740

Fullstack Task App: A Task Application Made with Next and Firebase

SIMPLE TASK APP

A task application made with Next and Firebase

screenshot

Proposal

  • The application must have login and register functions.
  • Must be simple to use, and have a very friendly UI.
  • Achieve the task with the best logic possible.
  • Hooks (React Functional Components) instead of classes.
  • Must have a dashboard, when we will: create, edit and delete tasks and lists.
  • Can use any tools available.

Tools

This application uses the following open-source packages:

Core ones.

  • React (Main "framework")
  • Next (Server-side rendering)
  • Firebase (Back-end development platform)
  • TypeScript (Strongly typed programming language that builds on JavaScript)
  • Redux (Creates a global state to application)

Stylization.

Others.

Code formatter, and other environment development tools.

Author

made by niloodev | Ezequiel Nilo

ANY TIPS OR FEEDBACK IS HIGHLY APPRECIATED! 🐸


Author: niloodev
Source code: https://github.com/niloodev/fullstack-task-app
 

#react-native #typescript #firebase #next 

Fullstack Task App: A Task Application Made with Next and Firebase

Building Instagram 2.0 Clone With Nextjs, Tailwind CSS, Firebase

Instagram 2.0 Clone With ReactJs!

PREREQUISITES:

  • Sign up for a Firebase account HERE
  • Install Node JS in your computer HERE     
     

This project was bootstrapped with Create React App.

Available Scripts

npx create-next-app instagram_clone
cd instagram_clone

Install Tailwind CSS with Next.js

Install Tailwind CSS

Install tailwindcss and its peer dependencies via npm, and then run the init command to generate both tailwind.config.js and postcss.config.js.

npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p

Configure your template paths

Add the paths to all of your template files in your tailwind.config.js file. 
 

module.exports = {
  content: [
    "./pages/**/*.{js,ts,jsx,tsx}",
    "./components/**/*.{js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}

Add the Tailwind directives to your CSS

Add the @tailwind directives for each of Tailwind’s layers to your ./styles/globals.css file.

@tailwind base;
@tailwind components;
@tailwind utilities;

This is a Next.js project bootstrapped with create-next-app.

First, run the development server:

npm run dev
# or
yarn dev

Open http://localhost:3000 with your browser to see the result.

You can start editing the page by modifying pages/index.js. The page auto-updates as you edit the file.

API routes can be accessed on http://localhost:3000/api/hello. This endpoint can be edited in pages/api/hello.js.

The pages/api directory is mapped to /api/*. Files in this directory are treated as API routes instead of React pages.

Learn More

To learn more about Next.js, take a look at the following resources:

You can check out the Next.js GitHub repository - your feedback and contributions are welcome!

Deploy on Vercel

The easiest way to deploy your Next.js app is to use the Vercel Platform from the creators of Next.js.

Check out our Next.js deployment documentation for more details.

npm install @heroicons/react
npm install --save-dev @faker-js/faker
npm i tailwind-scrollbar
npm i tailwind-scrollbar-hide
npm i @tailwindcss/forms

npm install recoil

npm install @headlessui/react
npm i react-moment

image image

Download details:

Author: SashenJayathilaka
Source code: https://github.com/SashenJayathilaka/Instagram-Clone

#next #nextjs #react #javscript #nextauth #firebase #tailwindcss 

Building Instagram 2.0 Clone With Nextjs, Tailwind CSS, Firebase
Thierry  Perret

Thierry Perret

1659975660

Comment Créer Une App React Avec Les Fonctions Firebase Serverless

Firebase est une plate-forme cloud passionnante de Google disponible pour les entreprises aujourd'hui. Firebase connecte tout, des simples sites Web statiques aux appareils IoT en passant par l'IA et les plates-formes d'apprentissage automatique. La plateforme fournit divers services pour faciliter ces connexions, comme le stockage et l'authentification.

Dans ce didacticiel, vous découvrirez deux produits Firebase principaux : Cloud Functions for Firebase et Firebase Hosting. L'hébergement sert à déployer des applications Web statiques. Les fonctions sont la plate-forme sans serveur Firebase. Vous allez créer une application statique à l'aide de React qui authentifie les utilisateurs via la bibliothèque React d'Okta. Après avoir obtenu un jeton d'accès du serveur d'autorisation d'Okta, vous utiliserez une fonction Firebase pour échanger le jeton Okta contre un jeton Firebase et connecter l'utilisateur à l'aide de la structure d'authentification de Firebase. Vous obtiendrez des informations utilisateur supplémentaires en appelant le userInfopoint de terminaison sur votre serveur d'autorisation Okta et en incluant ces données dans votre jeton Firebase. Enfin, vous allez créer un autre point de terminaison de fonction pour gérer une requête HTTP simple nécessitant un utilisateur authentifié.

Une fois que vous avez créé votre application, vous la déploierez sur la plate-forme Firebase pour une utilisation publique à l'aide de la CLI Firebase.

Ajouter une authentification à l'aide d'OIDC

Avant de commencer, vous aurez besoin d'un compte développeur Okta gratuit. Installez l' interface de ligne de commande Okta et exécutez- okta registerla pour vous inscrire à un nouveau compte. Si vous avez déjà un compte, exécutez okta login. Ensuite, courez okta apps create. Sélectionnez le nom de l'application par défaut ou modifiez-le comme bon vous semble. Choisissez Application monopage et appuyez sur Entrée .

Utilisez http://localhost:3000/login/callbackpour l'URI de redirection et acceptez l'URI de redirection de déconnexion par défaut de http://localhost:3000/login.

Créez votre projet Firebase

Ensuite, ouvrez la console Firebase et cliquez sur Ajouter un projet . Donnez un nom à votre projet, de préférence okta-firebase. Pour l'instant, vous pouvez désactiver les analyses et créer le projet. Une fois cette opération terminée, vous pourrez accéder au tableau de bord du projet.

Tout d'abord, cliquez sur le bouton Mettre à niveau à côté de l' option Spark en bas de votre écran. Changez votre plan en Blaze et si vous le souhaitez, définissez un budget de facturation de 1 $ pour vous informer lorsque vous encourez des frais. Blaze est payant et les tarifs sont assez généreux pour les projets de loisirs. Vous ne devriez pas encourir de frais pour le moment, mais le budget vous indiquera si vous le faites.

Cliquez sur la roue des paramètres à côté de Vue d'ensemble du projet et cliquez sur Paramètres du projet . Au bas de la page de présentation, une invite vous invite à sélectionner une plate-forme pour commencer et à sélectionner une application Web (elle ressemblera à </> ). Donnez le surnom à votre application okta-firebase-demoet sélectionnez Configurer également l'hébergement Firebase pour cette application . Cliquez sur Enregistrer l'application et dans un instant, vous verrez du code JavaScript pour configurer votre application Firebase.

Configuration Firebase générée par la console Firebase à l'étape "Ajouter le SDK Firebase"

Conservez ces informations car vous en aurez besoin dans votre application. Cliquez sur le reste de l'assistant et revenez à votre console. Accédez à nouveau à la section Paramètres du projet et accédez à l' onglet Comptes de service . Cliquez sur Générer une nouvelle clé privée et laissez ce fichier se télécharger. Vous en aurez besoin dans votre fonction Firebase sous peu.

Capture d'écran de la vue "Firebase Admin SDK" avec un bouton pour "Générer une nouvelle clé privée"

Enfin, cliquez sur Build > Authentication dans la navigation latérale et cliquez sur Get Started . Le fait d' appuyer sur le bouton Démarrer génère une clé d'API Web requise pour utiliser les fonctions d'authentification.

Créez votre front-end dans React

Ensuite, créez votre application React.

npx create-react-app@5 okta-firebase
cd okta-firebase

La commande npx échafaudera une nouvelle application React version 18 pour vous.

Ensuite, vous devrez installer quelques dépendances pour vous aider.

npm i @okta/okta-react@6.4.3
npm i @okta/okta-auth-js@6.2.0
npm i react-router-dom@5.3.0
npm i bootstrap@5.1.3
npm i firebase@9.8.1

Tout d'abord, vous souhaitez installer le @okta/okta-reactpackage pour aider à authentifier les utilisateurs et à obtenir les jetons du serveur d'autorisation Okta. Ce package vous aidera à accéder à l'état d'authentification, à diriger les utilisateurs vers la page de connexion Okta et à gérer les rappels.

@okta/okta-reactrepose sur le @okta/okta-auth-jspackage, vous devez donc l'installer.

Ensuite, vous voulez installer react-router-dom. Ce package configurera la route de rappel pour vous et toute autre route dont vous pourriez avoir besoin.

Enfin, vous utiliserez le firebasepackage pour appeler les différentes fonctionnalités de la plate-forme dans Firebase, telles que les fonctions et l'authentification.

Ajoutez un fichier appelé .envdans votre répertoire racine et remplacez le code par ce qui suit.

REACT_APP_OKTA_ISSUER=https://{yourOktaDomain}/oauth2/default
REACT_APP_OKTA_CLIENTID={yourClientID}
REACT_APP_FIREBASE_APIKEY={yourFirebaseAPIKey}
REACT_APP_FIREBASE_AUTHDOMAIN={yourFirebaseAuthDomain}
REACT_APP_FIREBASE_PROJECTID={yourFirebaseProjectID}
REACT_APP_FIREBASE_STORAGEBUCKET={yourFirebaseStorageBucket}
REACT_APP_FIREBASE_MESSAGINGSENDERID={yourFirebaseMessagingSenderID}
REACT_APP_FIREBASE_APPID={yourFirebaseAppID}
REACT_APP_ENV=production

Vous avez obtenu les valeurs Okta lors de la création de votre application à l'aide de l'interface de ligne de commande Okta. Votre domaine Okta fait partie de l'émetteur. Les valeurs Firebase proviennent de la configuration que vous avez copiée lors de la première création de votre application.

Il existe actuellement une erreur connue dans React 18 avec la bibliothèque Okta React où plusieurs rendus peuvent entraîner un message d'erreur dans l' oktaAuthobjet. Le travail sur la correction de cette erreur est en cours. En attendant, vous pouvez contourner ce problème en désactivant React strict. Remplacez le code de votre index.jsfichier par le code suivant.

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
    <App />
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

Ensuite, ouvrez votre App.jsfichier et remplacez le code par ce qui suit.

import "./App.css";

import { BrowserRouter as Router } from "react-router-dom";
import AppWithRouterAccess from "./AppWithRouterAccess";
import 'bootstrap/dist/css/bootstrap.min.css';


function App() {

  return (
    <Router>      
      <AppWithRouterAccess />
    </Router>
  );
}

export default App;

Vous remplacez le code par défaut par un Routeret un AppWithRouterAccessque vous écrirez ensuite. Ouvrez un nouveau fichier appelé AppWithRouterAccess.jsxet ajoutez le code suivant.

import "./App.css";

import { Route, useHistory } from "react-router-dom";
import { OktaAuth, toRelativeUrl } from "@okta/okta-auth-js";
import { Security, LoginCallback } from "@okta/okta-react";

import Home from "./Home";

const {
  REACT_APP_OKTA_ISSUER,
  REACT_APP_OKTA_CLIENTID
} = process.env;

const oktaAuth = new OktaAuth({
  issuer: REACT_APP_OKTA_ISSUER,
  clientId: REACT_APP_OKTA_CLIENTID,
  redirectUri: window.location.origin + "/login/callback",
  scopes: ['openid', 'profile', 'email']
});

function AppWithRouterAccess() {
  const history = useHistory();

  const restoreOriginalUri = async (_oktaAuth, originalUri) => {
    history.replace(toRelativeUrl(originalUri || "/", window.location.origin));
  };

  return (
    <Security oktaAuth={oktaAuth} restoreOriginalUri={restoreOriginalUri}>
      <Route path="/" component={Home} />
      <Route path="/login/callback" component={LoginCallback} />
    </Security>
  );
}

export default AppWithRouterAccess;

Ce fichier définira vos itinéraires et établira l' /login/callbackitinéraire permettant à Okta de gérer la signature de vos utilisateurs.

Enfin, ajoutez le Home.jsxfichier à votre application avec le code suivant.

import React, { useState } from "react";

import { useOktaAuth } from "@okta/okta-react";

import { initializeApp } from "firebase/app";
import { getAuth, signInWithCustomToken, signOut } from "firebase/auth";
import {
  getFunctions,
  httpsCallable,
  connectFunctionsEmulator,
} from "firebase/functions";

function Home() {
  const [reportCardData, setReportCardData] = useState();
  
  const [selectedSemester, setSelectedSemester] = useState("Spring 2022");
  const { oktaAuth, authState } = useOktaAuth();

  const login = async () => oktaAuth.signInWithRedirect();
  const logout = async () => {
    signOut(auth);
    oktaAuth.signOut("/");
  };

  const {
    REACT_APP_FIREBASE_APIKEY,
    REACT_APP_FIREBASE_AUTHDOMAIN,
    REACT_APP_FIREBASE_PROJECTID,
    REACT_APP_FIREBASE_STORAGEBUCKET,
    REACT_APP_FIREBASE_MESSAGINGSENDERID,
    REACT_APP_FIREBASE_APPID,
    REACT_APP_ENV,
  } = process.env;

  const firebaseConfig = {
    apiKey: REACT_APP_FIREBASE_APIKEY,
    authDomain: REACT_APP_FIREBASE_AUTHDOMAIN,
    projectId: REACT_APP_FIREBASE_PROJECTID,
    storageBucket: REACT_APP_FIREBASE_STORAGEBUCKET,
    messagingSenderId: REACT_APP_FIREBASE_MESSAGINGSENDERID,
    appId: REACT_APP_FIREBASE_APPID,
  };

  const app = initializeApp(firebaseConfig);
  const functions = getFunctions(app);

  const auth = getAuth();

  if (REACT_APP_ENV === "development") {
    connectFunctionsEmulator(functions, "localhost", 5001);
  }

  const getGrades = async () => {
    const getGradesCall = httpsCallable(functions, "getGrades");
    const resp = await getGradesCall({
      name: selectedSemester.split(" ")[0],
      year: selectedSemester.split(" ")[1],
    });

    setReportCardData(resp.data);
  };

  const exchangeOktaTokenForFirebaseToken = async () => {
    const exchangeToken = httpsCallable(
      functions,
      "exchangeOktaTokenForFirebaseToken"
    );

    const resp = await exchangeToken({
      accessToken: authState.accessToken.accessToken
    });

    await signInWithCustomToken(auth, resp.data.firebaseToken);
  };

  if (authState?.isAuthenticated) {
    exchangeOktaTokenForFirebaseToken();
  }

  return (
    <div className="App">
      <main role="main" className="inner cover container">
        <nav className="navbar navbar-expand-lg navbar-light bg-light ">
          <ul className="nav navbar-nav ml-auto navbar-right ms-auto">
            <li>
              {auth?.currentUser && (
                <button
                  className="btn btn-outline-secondary my-2 my-sm-0"
                  onClick={logout}
                >
                  Logout
                </button>
              )}

              {!auth?.currentUser && (
                <button className="btn btn-outline-secondary" onClick={login}>
                  Login
                </button>
              )}
            </li>
          </ul>
        </nav>

        {!auth?.currentUser && (
          <div>
            <p className="lead">
              In order to use this application you must be logged into your Okta
              account
            </p>
            <p className="lead">
              <button className="btn btn-primary" onClick={login}>
                Login
              </button>
            </p>
          </div>
        )}
        {auth?.currentUser && (
          <div>
            <h1 className="cover-heading">
              Please select a semester to get your report card
            </h1>

            <div className="row">
              <div className="col-2">
                <select
                  className="form-select"
                  value={selectedSemester}
                  onChange={(e) => {
                    setSelectedSemester(e.target.value);
                  }}
                >
                  <option value="Fall 2021">Fall 2021</option>
                  <option value="Spring 2021">Spring 2021</option>
                  <option value="Fall 2022">Fall 2022</option>
                  <option value="Spring 2022">Spring 2022</option>
                </select>
              </div>
              <div className="col-2">
                <button className="btn btn-primary" onClick={getGrades}>
                  Get Grades
                </button>
              </div>
            </div>

            {reportCardData && (
              <>
                <p>
                  <b>Name: </b> {reportCardData.name}
                </p>
                <p>
                  <b>School: </b> {reportCardData.school}
                </p>
                <p>
                  <b>Semester: </b> {reportCardData.semester} -{" "}
                  {reportCardData.year}
                </p>

                <table className="table table-striped">
                  <thead>
                    <tr>
                      <th className="text-start"> Course </th>
                      <th> Score </th>
                      <th> Letter Grade </th>
                    </tr>
                  </thead>
                  <tbody>
                    {reportCardData.grades.map((grade, i) => {
                      return (
                        <tr key={i}>
                          <td className="text-start">{grade.course}</td>
                          <td>{grade.score}</td>
                          <td>{grade.letterGrade}</td>
                        </tr>
                      );
                    })}
                  </tbody>
                </table>
              </>
            )}
          </div>
        )}

        <footer
          className="bg-light text-center fixed-bottom"
          style={{
            width: "100%",
            padding: "0 15px",
          }}
        >
          <p>
            A Small demo using <a href="https://developer.okta.com/">Okta</a> to
            Secure an{" "}
            <a href="https://firebase.google.com/">
              Firebase hosted application{" "}
            </a>{" "}
            with a serverless{" "}
            <a href="https://firebase.google.com/docs/functions">function</a>
          </p>
          <p>
            By <a href="https://github.com/nickolasfisher">Nik Fisher</a>
          </p>
        </footer>
      </main>
    </div>
  );
}

export default Home;

 

Cette page gérera à la fois les états authentifiés et non authentifiés. Si l'utilisateur n'est pas authentifié, un écran lui demandant de le faire lui est présenté. Si l'utilisateur est authentifié, il peut le faire getGradesen sélectionnant un semestre dans la liste déroulante et en appelant le serveur.

La getGradesfonction garantit que l'utilisateur est authentifié à l'aide de la fonction d'authentification intégrée dans Firebase. Firebase s'intègre également aux jetons d'authentification de fournisseurs tels qu'Okta. Pour utiliser cette fonctionnalité, vous créerez un jeton d'authentification Firebase à l'aide d'un jeton d'authentification Okta. Okta renvoie un accessTokenau client lorsque l'utilisateur se connecte. Le client transmet ensuite le accessTokenà une fonction Firebase appelée exchangeOktaTokenForFirebaseToken. Dans cette fonction Cloud pour Firebase, vous vérifiez le jeton et renvoyez un jeton Firebase pour connecter l'utilisateur. Ensuite, les appels ultérieurs aux fonctions traiteront cet utilisateur comme étant connecté à Firebase.

À ce stade, vous pouvez utiliser la npm run startcommande pour exécuter votre application localement. Vous verrez quelques erreurs de console de Firebase et vous verrez des boutons de connexion . Notez que vous pouvez maintenant vous authentifier auprès d'Okta, mais que le processus de connexion n'appelle pas encore Firebase, votre connexion est donc toujours incomplète.

Préparez votre application React pour Firebase

Vous êtes maintenant prêt à préparer votre application pour Firebase. Si vous ne l'avez pas encore fait, veuillez installer la CLI Firebase.

npm install -g firebase-tools@11.1.0

Pour vous connecter avec votre compte Google, vous devrez peut-être exécuter firebase login.

Ensuite, exécutez la commande firebase initpour lancer l'initialisation de votre projet.

Sélectionnez les deux fonctionnalités suivantes :

  • Functions : configurer un répertoire Cloud Functions et ses fichiers
  • Hébergement : configurez les fichiers pour l'hébergement Firebase et (éventuellement) configurez les déploiements d'action GitHub

Sélectionnez Utiliser un projet existant et sélectionnez votre okta-firebase-{ID}projet

Après un moment, vous verrez des invites pour configurer les fonctions Firebase. Sélectionnez les options suivantes :

  1. Langage - Javascript
  2. EsLint - Non (vous devez l'utiliser dans les applications prêtes pour la production.)
  3. Lorsque vous êtes invité à installer les dépendances, dites Oui

Ensuite, sélectionnez les options suivantes pour configurer l'hébergement.

  1. Que voulez-vous utiliser comme répertoire public ? - construire
  2. Configurer en tant qu'application d'une seule page ? oui
  3. Configurer les compilations automatiques ? non

Avant de déployer votre application, vous devez exécuter la buildcommande sur votre application React pour la préparer correctement. En configurant votre application en tant que SPA, vous indiquez à la CLI Firebase de modifier la configuration pour rediriger vers /index.html.

Ajouter des fonctions Firebase authentifiées

Vous devriez remarquer qu'un nouveau dossier appelé functionsa été ajouté à votre répertoire. Là, vous verrez des éléments de configuration Firebase et un fichier appelé index.js. Vous allez ajouter le code pour deux fonctions.

Tout d'abord, vous en aurez besoin d'un qui accepte un jeton Okta et renvoie un jeton Firebase à utiliser par le client. Pour vérifier le jeton, vous utiliserez le @okta/jwt-verifierpackage d'Okta.

La deuxième fonction acceptera les arguments du client, à savoir le semestre, et les utilisera avec certaines informations du jeton pour créer des données de bulletin que le client utilisera pour créer le bulletin.

Commencez par accéder à votre répertoire de fonctions et installez vos dépendances.

cd functions
npm i @okta/jwt-verifier@2.3.0

Le @okta/jwt-verifiervérifiera votre JWT d'Okta lors de l'appel de la exchangeOktaTokenForFirebaseTokenfonction.

Ensuite, copiez le fichier de clés que vous avez téléchargé précédemment à partir de la console Firebase et ajoutez-le à votre functionsdossier. Notez le nom, car vous en aurez besoin sous peu.

Ajoutez un fichier à votre functionsdossier appelé grades.jset ajoutez le code suivant.

const getGrades = (user, semester) => {
  return {
    name: user.name,
    school: getFakeUniversityName(user.email),
    semester: semester.name,
    year: semester.year,
    grades: grades
      .filter((r) => r.year == semester.year)
      .filter((r) => r.semester == semester.name),
  };
};

const getFakeUniversityName = (email) => {
  const number = Math.floor(Math.random() * 2);
  const domain = parseDomain(email);

  switch (number) {
    case 0:
      return "University of " + domain;
    case 1:
      return domain + " State University";
    default:
      return "University of " + domain;
  }
};

const parseDomain = (email) => {
  const emailParts = email.split("@");
  const domainParts = emailParts[1].split(".");

  let name = "";

  domainParts.forEach((part, i) => {
    if (i > 0) {
      name += " ";
    }
    if (i + 1 < domainParts.length) {
      name += part.charAt(0).toUpperCase() + part.slice(1);
    }
  });

  return name;
};

const grades = [
  {
    course: "Calculus 1",
    score: 72,
    letterGrade: "C",
    year: 2021,
    semester: "Fall",
  },
  {
    course: "Intro to Ballroom Dance",
    score: 94,
    letterGrade: "A",
    year: 2021,
    semester: "Fall",
  },
  {
    course: "Computer Science 101",
    score: 65,
    letterGrade: "F",
    year: 2021,
    semester: "Fall",
  },
  {
    course: "Intro to Modern Physics",
    score: 88,
    letterGrade: "B",
    year: 2021,
    semester: "Fall",
  },

  {
    course: "Calculus 2",
    score: 84,
    letterGrade: "C",
    year: 2021,
    semester: "Spring",
  },
  {
    course: "Geometry",
    score: 97,
    letterGrade: "A",
    year: 2021,
    semester: "Spring",
  },
  {
    course: "Computer Science 101",
    score: 76,
    letterGrade: "C",
    year: 2021,
    semester: "Spring",
  },
  {
    course: "Physics II",
    score: 88,
    letterGrade: "B",
    year: 2021,
    semester: "Spring",
  },

  {
    course: "Calculus 3",
    score: 84,
    letterGrade: "C",
    year: 2022,
    semester: "Fall",
  },
  {
    course: "Abstract Algebra",
    score: 97,
    letterGrade: "A",
    year: 2022,
    semester: "Fall",
  },
  {
    course: "Computer Science 102",
    score: 76,
    letterGrade: "C",
    year: 2022,
    semester: "Fall",
  },
  {
    course: "Public Speaking",
    score: 88,
    letterGrade: "B",
    year: 2022,
    semester: "Fall",
  },

  {
    course: "Adv Calculus",
    score: 84,
    letterGrade: "C",
    year: 2022,
    semester: "Spring",
  },
  {
    course: "Geometry",
    score: 97,
    letterGrade: "A",
    year: 2022,
    semester: "Spring",
  },
  {
    course: "Javascript in the Modern Web",
    score: 76,
    letterGrade: "C",
    year: 2022,
    semester: "Spring",
  },
  {
    course: "Cryptography",
    score: 88,
    letterGrade: "B",
    year: 2022,
    semester: "Spring",
  },
];

module.exports = { getGrades };

Tout d'abord, exchangeOktaTokenForFirebaseTokenfournira un jeton personnalisé de Firebase à utiliser dans votre application. Firebase vous permet de contrôler totalement votre authentification via la signInWithCustomTokenméthode que vous avez utilisée sur le client. Vous devez créer un jeton personnalisé à l'aide de votre compte de service. Vous avez précédemment téléchargé la définition de votre compte de service en tant que fichier JSON. Vous pouvez maintenant appeler createCustomTokendepuis votre authobjet contre votre compte de service. Cette fonction nécessite un uidet accepte éventuellement d'autres revendications que vous pourriez souhaiter ajouter. N'oubliez pas que Firebase réserve les noms de jeton .

Vous pouvez ensuite obtenir un jeton auprès du serveur d'autorisation Okta et le présenter à la fonction Firebase pour qu'il soit vérifié à l'aide du fichier OktaJwtVerifier. Si le jeton Okta est valide, vous appellerez le point de userInfoterminaison de votre serveur d'autorisation Okta pour obtenir des informations supplémentaires sur votre utilisateur. Vous pouvez inclure ces informations dans votre jeton Firebase en tant que revendications personnalisées. Ensuite, vous pouvez utiliser l' firebaseAppobjet pour créer votre jeton avec ces revendications. Vous renverrez ce jeton au client et vous vous connecterez avec.

Ensuite, vous avez la getGradesfonction. Vous vérifiez context.authsi l'utilisateur est connecté. Si ce n'est pas le cas, vous générez une erreur. Si c'est le cas, autorisez l'utilisateur à accéder aux données de notes dans ce fichier.

Il existe deux manières différentes de configurer des fonctions dans Firebase onCallet onRequest. onRequestvous donne une forme plus brute de gestion de l'appel entrant. Vous devez configurer votre code CORS, votre authentification et toutes les bonnes choses dont le onCallwrapper s'occupe pour vous. Par exemple, context.authest fourni parce que vous avez utilisé le onCall, alors que onRequestvous auriez besoin d'obtenir ces informations manuellement.

Testez votre application localement à l'aide d'émulateurs Firebase

Vous êtes maintenant prêt à tester votre application localement via l'émulateur Firebase. L'émulateur fera en sorte que vos services puissent communiquer comme s'ils étaient déployés sur Firebase.

Tout d'abord, éditez votre .envfichier pour le remplacer REACT_APP_ENV=productionpar REACT_APP_ENV=development. Cette modification indique à l'application de se connecter à l'émulateur. Ensuite, exécutez les commandes suivantes dans le répertoire racine de votre projet.

npm run build
firebase emulators:start

Tout d'abord, vous devez créer votre application car Firebase s'attend à ce que votre application se trouve dans le buildrépertoire que vous avez configuré précédemment. Ensuite, il créera un émulateur et déploiera votre fonction et votre application Web sur cet émulateur. Par défaut, l'application Web se déploie localhost:5000plutôt que l'habituel localhost:3000.

Si vous constatez que vous avez des conflits avec les ports par défaut utilisés par Firebase, vous pouvez mettre à jour les firebase.jsonentrées de fichier pour emulators.functions.portet emulators.hosting.portvers les ports dont vous disposez. Voir ci-dessous pour un exemple firebase.jsonde fichier qui utilise le port 5002 pour l'hébergement et 5001 pour les fonctions.

{
  "hosting": {
    "public": "build",
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ],
    "rewrites": [
      {
        "source": "**",
        "destination": "/index.html"
      }
    ]
  },
  "functions": {
    "source": "functions"
  },
  "emulators": {
    "functions": {
      "port": 5001
    },
    "hosting": {
      "port": 5002
    },
    "ui": {
      "enabled": false
    }
  }
}

Vous devrez ouvrir la console d'administration Okta et accéder à votre application Okta pour ajouter ces ports à la liste d'autorisation.

Ouvrez le portail des développeurs Okta et connectez-vous à Okta . Appuyez ensuite sur Admin pour lancer la console d'administration.

Accédez à Applications > Applications et recherchez votre application Okta pour ce projet. Sélectionnez-le pour le modifier. Dans l' onglet Général , modifiez les paramètres généraux avec les nouveaux ports. Par exemple, si votre port d'hébergement Firebase est 5000, ajoutez http://localhost:5000/login/callbackaux URI de redirection de connexion et http://localhost:5000à vos URI de redirection de déconnexion . Mettez à jour le numéro de port en fonction des paramètres de votre émulateur Firebase et enregistrez.

Il y a un autre endroit pour ajouter le nouveau port dans la console d'administration Okta. Vous ajouterez le port en tant qu'origine approuvée afin que votre application puisse terminer le processus de déconnexion. Accédez à Sécurité > API et ouvrez l' onglet Origines approuvées. Appuyez sur le bouton + Ajouter une origine et ajoutez la nouvelle URL avec le port, par exemple http://localhost:5000/. Cochez les cases Rediriger et CORS et enregistrez, puis revenez à votre application.

À ce stade, vous devriez pouvoir vous connecter à Okta, échanger votre jeton contre un jeton Firebase, sélectionner un semestre et cliquer sur Obtenir des notes pour voir votre bulletin généré.

Capture d'écran du projet final montrant un bulletin pour l'utilisateur authentifié

Déployez votre application sur Firebase

Une fois que cela fonctionne, vous êtes prêt à déployer votre application sur Firebase. Tout d'abord, définissez votre .enventrée pour REACT_APP_ENVrevenir à productionsi vous l'aviez définie sur development. Vous devrez peut-être exécuter la npm run buildcommande une fois de plus au cas où vous auriez apporté des modifications ou des modifications. Une fois terminé, exécutez la commande firebase deployà partir de votre répertoire racine.

Après avoir terminé cette étape, votre CLI fournira une URL à votre application. Vous devriez voir votre application s'exécuter sur Firebase si vous cliquez dessus. Mais, Okta ne fonctionnera pas à ce stade. Vous devez revenir à votre portail d'administration Okta, sous votre application, et ajouter {yourFirebaseDomain}/login/callbackà vos URI de redirection de connexion , {yourFirebaseDomain}à vos URI de redirection de déconnexion dans l' onglet Paramètres généraux de votre application Okta, et ajouter {yourFirebaseDomain} en tant que Trusted Origine

Revenez maintenant à votre application dans Firebase et cliquez sur Se connecter pour vous assurer qu'Okta est correctement connecté. Une fois connecté, vous devriez pouvoir sélectionner un semestre et cliquer sur Obtenir les notes pour voir votre bulletin généré.


Lien : https://developer.okta.com/blog/2022/06/27/react-firebase-serverless

#firebase #react #serverless

Comment Créer Une App React Avec Les Fonctions Firebase Serverless
Hans  Marvin

Hans Marvin

1659968400

How to Build a React App with Firebase Serverless Functions

Firebase is an exciting cloud platform from Google available to businesses today. Firebase connects everything from simple static websites to IoT devices to AI and machine learning platforms. The platform provides various services to facilitate these connections, like storage and authentication.

In this tutorial, you will learn about two core Firebase products: Cloud Functions for Firebase and Firebase Hosting. Hosting is for deploying static web applications. Functions are the Firebase serverless platform. You will create a static application using React that authenticates users via Okta’s React library. After obtaining an access token from Okta’s authorization server, you will use a Firebase function to exchange the Okta token for a Firebase token and sign in the user using Firebase’s authentication structure. You will obtain additional user information by calling the userInfo endpoint on your Okta authorization server and including that data in your Firebase token. Finally, you will create another function endpoint to handle a simple HTTP request that requires an authenticated user.

See more at: https://developer.okta.com/blog/2022/06/27/react-firebase-serverless

#firebase #react #serverless

How to Build a React App with Firebase Serverless Functions
加藤  七夏

加藤 七夏

1659961140

如何使用 Firebase 無服務器函數構建 React 應用程序

Firebase 是 Google 提供的令人興奮的雲平台,如今可供企業使用。Firebase 連接了從簡單的靜態網站到物聯網設備再到人工智能和機器學習平台的一切。該平台提供各種服務來促進這些連接,例如存儲和身份驗證。

在本教程中,您將了解兩個核心 Firebase 產品:Firebase 的 Cloud Functions 和 Firebase 託管。託管用於部署靜態 Web 應用程序。函數是 Firebase 無服務器平台。您將使用 React 創建一個靜態應用程序,該應用程序通過 Okta 的 React 庫對用戶進行身份驗證。從 Okta 的授權服務器獲取訪問令牌後,您將使用 Firebase 函數將 Okta 令牌交換為 Firebase 令牌,並使用 Firebase 的身份驗證結構登錄用戶。您將通過調用userInfoOkta 授權服務器上的端點並將該數據包含在您的 Firebase 令牌中來獲取其他用戶信息。最後,您將創建另一個函數端點來處理需要經過身份驗證的用戶的簡單 HTTP 請求。

構建應用程序後,您將使用 Firebase CLI 將其部署到 Firebase 平台以供公眾使用。

使用 OIDC 添加身份驗證

在開始之前,您需要一個免費的 Okta 開發者帳戶。安裝Okta CLI並運行okta register以註冊一個新帳戶。如果您已經有一個帳戶,請運行okta login. 然後,運行okta apps create。選擇默認應用名稱,或根據需要進行更改。選擇Single-Page App並按Enter

用於http://localhost:3000/login/callback重定向 URI 並接受默認的註銷重定向 URI http://localhost:3000/login

創建您的 Firebase 項目

接下來,打開Firebase 控制台並單擊Add Project。給你的項目起個名字,最好是okta-firebase. 現在,您可以關閉分析並創建項目。完成後,您將能夠訪問項目儀表板。

首先,單擊屏幕底部Spark選項旁邊的升級按鈕。將您的計劃更改為 Blaze,如果您願意,可以設置 1 美元的計費預算,以便在您產生費用時通知您。Blaze 是現收現付的,而且對於業餘愛好項目的費率相當慷慨。您目前不應該產生任何費用,但如果您這樣做,預算會通知您。

單擊項目概覽旁邊的設置輪,然後單擊項目設置。在概述頁面的底部,有一個提示選擇一個平台以開始並選擇網絡應用程序(它看起來像</>)。為您的應用指定暱稱okta-firebase-demo,然後選擇Also setup Firebase Hosting for this app。單擊註冊應用程序,稍後您將看到一些用於設置 Firebase 應用程序的 JavaScript 代碼。

Firebase 控制台在“添加 Firebase SDK”步驟下生成的 Firebase 配置

保留此信息,因為您將在您的應用程序中需要它。單擊嚮導的其餘部分並返回到您的控制台。再次轉到項目設置部分並導航到服務帳戶選項卡。單擊生成新私鑰並下載該文件。您很快就會在 Firebase 函數中用到它。

帶有“生成新私鑰”按鈕的“Firebase Admin SDK”視圖的屏幕截圖

最後,單擊側導航中的Build > Authentication並單擊Get Started。按下Get Started按鈕會生成使用身份驗證功能所需的Web API 密鑰。

在 React 中創建你的前端

接下來,創建您的 React 應用程序。

npx create-react-app@5 okta-firebase
cd okta-firebase

npx 命令將為您搭建一個新的 React 版本 18 應用程序。

接下來,您將需要安裝一些依賴項來幫助您。

npm i @okta/okta-react@6.4.3
npm i @okta/okta-auth-js@6.2.0
npm i react-router-dom@5.3.0
npm i bootstrap@5.1.3
npm i firebase@9.8.1

首先,您要安裝該@okta/okta-react軟件包以幫助驗證用戶並從 Okta 授權服務器獲取令牌。這個包將幫助您訪問身份驗證狀態,將用戶引導到 Okta 登錄頁面,並處理任何回調。

@okta/okta-react依賴@okta/okta-auth-js包,所以你需要安裝它。

接下來,您要安裝react-router-dom. 該軟件包將為您設置回調路由以及您可能需要的任何其他路由。

最後,您將使用該firebase包調用 Firebase 中的各種平台功能,例如函數和身份驗證。

在根目錄中添加一個名為的文件.env,並將代碼替換為以下內容。

REACT_APP_OKTA_ISSUER=https://{yourOktaDomain}/oauth2/default
REACT_APP_OKTA_CLIENTID={yourClientID}
REACT_APP_FIREBASE_APIKEY={yourFirebaseAPIKey}
REACT_APP_FIREBASE_AUTHDOMAIN={yourFirebaseAuthDomain}
REACT_APP_FIREBASE_PROJECTID={yourFirebaseProjectID}
REACT_APP_FIREBASE_STORAGEBUCKET={yourFirebaseStorageBucket}
REACT_APP_FIREBASE_MESSAGINGSENDERID={yourFirebaseMessagingSenderID}
REACT_APP_FIREBASE_APPID={yourFirebaseAppID}
REACT_APP_ENV=production

您在之前使用 Okta CLI 創建應用程序時獲得了 Okta 值。您的 Okta 域是頒發者的一部分。Firebase 值來自您在首次創建應用程序時復制的配置。

目前在使用 Okta React 庫的 React 18 中存在一個已知錯誤,其中多次重新渲染可能導致對oktaAuth像中出現錯誤消息。修復此錯誤的工作正在進行中。strict同時,您可以通過將 React 退出模式來解決它。將index.js文件中的代碼替換為以下代碼。

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
    <App />
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

接下來,打開您的App.js文件並將代碼替換為以下內容。

import "./App.css";

import { BrowserRouter as Router } from "react-router-dom";
import AppWithRouterAccess from "./AppWithRouterAccess";
import 'bootstrap/dist/css/bootstrap.min.css';


function App() {

  return (
    <Router>      
      <AppWithRouterAccess />
    </Router>
  );
}

export default App;

您將用接下來編寫的 aRouter和 an替換默認代碼。AppWithRouterAccess打開一個名為的新文件AppWithRouterAccess.jsx並添加以下代碼。

import "./App.css";

import { Route, useHistory } from "react-router-dom";
import { OktaAuth, toRelativeUrl } from "@okta/okta-auth-js";
import { Security, LoginCallback } from "@okta/okta-react";

import Home from "./Home";

const {
  REACT_APP_OKTA_ISSUER,
  REACT_APP_OKTA_CLIENTID
} = process.env;

const oktaAuth = new OktaAuth({
  issuer: REACT_APP_OKTA_ISSUER,
  clientId: REACT_APP_OKTA_CLIENTID,
  redirectUri: window.location.origin + "/login/callback",
  scopes: ['openid', 'profile', 'email']
});

function AppWithRouterAccess() {
  const history = useHistory();

  const restoreOriginalUri = async (_oktaAuth, originalUri) => {
    history.replace(toRelativeUrl(originalUri || "/", window.location.origin));
  };

  return (
    <Security oktaAuth={oktaAuth} restoreOriginalUri={restoreOriginalUri}>
      <Route path="/" component={Home} />
      <Route path="/login/callback" component={LoginCallback} />
    </Security>
  );
}

export default AppWithRouterAccess;

該文件將定義您的路由並/login/callback為 Okta 建立路由以處理您的用戶登錄。

最後,Home.jsx使用以下代碼將該文件添加到您的應用程序中。

import React, { useState } from "react";

import { useOktaAuth } from "@okta/okta-react";

import { initializeApp } from "firebase/app";
import { getAuth, signInWithCustomToken, signOut } from "firebase/auth";
import {
  getFunctions,
  httpsCallable,
  connectFunctionsEmulator,
} from "firebase/functions";

function Home() {
  const [reportCardData, setReportCardData] = useState();
  
  const [selectedSemester, setSelectedSemester] = useState("Spring 2022");
  const { oktaAuth, authState } = useOktaAuth();

  const login = async () => oktaAuth.signInWithRedirect();
  const logout = async () => {
    signOut(auth);
    oktaAuth.signOut("/");
  };

  const {
    REACT_APP_FIREBASE_APIKEY,
    REACT_APP_FIREBASE_AUTHDOMAIN,
    REACT_APP_FIREBASE_PROJECTID,
    REACT_APP_FIREBASE_STORAGEBUCKET,
    REACT_APP_FIREBASE_MESSAGINGSENDERID,
    REACT_APP_FIREBASE_APPID,
    REACT_APP_ENV,
  } = process.env;

  const firebaseConfig = {
    apiKey: REACT_APP_FIREBASE_APIKEY,
    authDomain: REACT_APP_FIREBASE_AUTHDOMAIN,
    projectId: REACT_APP_FIREBASE_PROJECTID,
    storageBucket: REACT_APP_FIREBASE_STORAGEBUCKET,
    messagingSenderId: REACT_APP_FIREBASE_MESSAGINGSENDERID,
    appId: REACT_APP_FIREBASE_APPID,
  };

  const app = initializeApp(firebaseConfig);
  const functions = getFunctions(app);

  const auth = getAuth();

  if (REACT_APP_ENV === "development") {
    connectFunctionsEmulator(functions, "localhost", 5001);
  }

  const getGrades = async () => {
    const getGradesCall = httpsCallable(functions, "getGrades");
    const resp = await getGradesCall({
      name: selectedSemester.split(" ")[0],
      year: selectedSemester.split(" ")[1],
    });

    setReportCardData(resp.data);
  };

  const exchangeOktaTokenForFirebaseToken = async () => {
    const exchangeToken = httpsCallable(
      functions,
      "exchangeOktaTokenForFirebaseToken"
    );

    const resp = await exchangeToken({
      accessToken: authState.accessToken.accessToken
    });

    await signInWithCustomToken(auth, resp.data.firebaseToken);
  };

  if (authState?.isAuthenticated) {
    exchangeOktaTokenForFirebaseToken();
  }

  return (
    <div className="App">
      <main role="main" className="inner cover container">
        <nav className="navbar navbar-expand-lg navbar-light bg-light ">
          <ul className="nav navbar-nav ml-auto navbar-right ms-auto">
            <li>
              {auth?.currentUser && (
                <button
                  className="btn btn-outline-secondary my-2 my-sm-0"
                  onClick={logout}
                >
                  Logout
                </button>
              )}

              {!auth?.currentUser && (
                <button className="btn btn-outline-secondary" onClick={login}>
                  Login
                </button>
              )}
            </li>
          </ul>
        </nav>

        {!auth?.currentUser && (
          <div>
            <p className="lead">
              In order to use this application you must be logged into your Okta
              account
            </p>
            <p className="lead">
              <button className="btn btn-primary" onClick={login}>
                Login
              </button>
            </p>
          </div>
        )}
        {auth?.currentUser && (
          <div>
            <h1 className="cover-heading">
              Please select a semester to get your report card
            </h1>

            <div className="row">
              <div className="col-2">
                <select
                  className="form-select"
                  value={selectedSemester}
                  onChange={(e) => {
                    setSelectedSemester(e.target.value);
                  }}
                >
                  <option value="Fall 2021">Fall 2021</option>
                  <option value="Spring 2021">Spring 2021</option>
                  <option value="Fall 2022">Fall 2022</option>
                  <option value="Spring 2022">Spring 2022</option>
                </select>
              </div>
              <div className="col-2">
                <button className="btn btn-primary" onClick={getGrades}>
                  Get Grades
                </button>
              </div>
            </div>

            {reportCardData && (
              <>
                <p>
                  <b>Name: </b> {reportCardData.name}
                </p>
                <p>
                  <b>School: </b> {reportCardData.school}
                </p>
                <p>
                  <b>Semester: </b> {reportCardData.semester} -{" "}
                  {reportCardData.year}
                </p>

                <table className="table table-striped">
                  <thead>
                    <tr>
                      <th className="text-start"> Course </th>
                      <th> Score </th>
                      <th> Letter Grade </th>
                    </tr>
                  </thead>
                  <tbody>
                    {reportCardData.grades.map((grade, i) => {
                      return (
                        <tr key={i}>
                          <td className="text-start">{grade.course}</td>
                          <td>{grade.score}</td>
                          <td>{grade.letterGrade}</td>
                        </tr>
                      );
                    })}
                  </tbody>
                </table>
              </>
            )}
          </div>
        )}

        <footer
          className="bg-light text-center fixed-bottom"
          style={{
            width: "100%",
            padding: "0 15px",
          }}
        >
          <p>
            A Small demo using <a href="https://developer.okta.com/">Okta</a> to
            Secure an{" "}
            <a href="https://firebase.google.com/">
              Firebase hosted application{" "}
            </a>{" "}
            with a serverless{" "}
            <a href="https://firebase.google.com/docs/functions">function</a>
          </p>
          <p>
            By <a href="https://github.com/nickolasfisher">Nik Fisher</a>
          </p>
        </footer>
      </main>
    </div>
  );
}

export default Home;

 

此頁面將處理經過身份驗證和未經身份驗證的狀態。如果用戶未通過身份驗證,則會向他們顯示一個屏幕,要求他們這樣做。如果用戶通過身份驗證,他們可以getGrades通過從下拉列表中選擇學期並調用服務器。

getGrades函數可確保使用 Firebase 中的內置身份驗證功能對用戶進行身份驗證。Firebase 還與來自 Okta 等提供商的身份驗證令牌集成。要利用此功能,您將使用 Okta 身份驗證令牌生成 Firebase 身份驗證令牌。當用戶登錄時, OktaaccessToken向客戶端返回一個。然後客戶端將 傳遞accessToken給一個名為 的 Firebase 函數exchangeOktaTokenForFirebaseToken。在此 Cloud Function for Firebase 中,您驗證令牌並返回 Firebase 令牌以使用戶登錄。隨後對函數的調用將把該用戶視為已登錄到 Firebase。

此時,您可以使用npm run start命令在本地運行您的應用程序。您會看到一些來自 Firebase 的控制台錯誤,並且您會看到登錄按鈕。請注意,您現在可以使用 Okta 進行身份驗證,但登錄過程尚未調用 Firebase,因此您的登錄仍然不完整。

讓您的 React 應用程序為 Firebase 做好準備

現在您已準備好為 Firebase 準備應用程序。如果您還沒有這樣做,請安裝 Firebase CLI。

npm install -g firebase-tools@11.1.0

要使用您的 Google 帳戶登錄,您可能需要運行firebase login.

接下來,運行命令firebase init來開始你的項目的初始化。

選擇以下兩個功能:

  • Functions:配置 Cloud Functions 目錄及其文件
  • 託管:為 Firebase 託管配置文件並(可選)設置 GitHub 操作部署

選擇使用現有項目並選擇您的okta-firebase-{ID}項目

片刻之後,您會看到設置 Firebase 功能的提示。選擇以下選項:

  1. 語言 - Javascript
  2. EsLint -(您應該在生產就緒的應用程序中使用它。)
  3. 當提示安裝依賴項時說

接下來,選擇以下選項來設置託管。

  1. 你想用什麼作為你的公共目錄?-建立
  2. 配置為單頁應用?是的
  3. 設置自動構建?

在部署您的應用程序之前,您必須build在您的 React 應用程序上運行命令以正確準備它。通過將您的應用配置為 SPA,您可以告訴 Firebase CLI 編輯配置以重定向到/index.html.

添加經過身份驗證的 Firebase 函數

您應該注意到一個名為的新文件夾functions已添加到您的目錄中。在那裡,您將看到一些 Firebase 配置內容和一個名為index.js. 您將為兩個函數添加代碼。

首先,您需要一個接受 Okta 令牌並返回 Firebase 令牌供客戶端使用的令牌。要驗證令牌,您將使用@okta/jwt-verifier來自 Okta 的包。

第二個函數將接受來自客戶端的參數,即學期,並將其與令牌中的一些信息一起使用來構建報告卡數據,供客戶用於創建報告卡。

首先導航到您的函數目錄並安裝您的依賴項。

cd functions
npm i @okta/jwt-verifier@2.3.0

調用該函數@okta/jwt-verifier時,它將從 Okta 驗證您的 JWT 。exchangeOktaTokenForFirebaseToken

接下來,複製您之前從 Firebase 控制台下載的密鑰文件並將其添加到您的functions文件夾中。記下名稱,因為您很快就會需要它。

將文件添加到functions名為的文件夾grades.js並添加以下代碼。

const getGrades = (user, semester) => {
  return {
    name: user.name,
    school: getFakeUniversityName(user.email),
    semester: semester.name,
    year: semester.year,
    grades: grades
      .filter((r) => r.year == semester.year)
      .filter((r) => r.semester == semester.name),
  };
};

const getFakeUniversityName = (email) => {
  const number = Math.floor(Math.random() * 2);
  const domain = parseDomain(email);

  switch (number) {
    case 0:
      return "University of " + domain;
    case 1:
      return domain + " State University";
    default:
      return "University of " + domain;
  }
};

const parseDomain = (email) => {
  const emailParts = email.split("@");
  const domainParts = emailParts[1].split(".");

  let name = "";

  domainParts.forEach((part, i) => {
    if (i > 0) {
      name += " ";
    }
    if (i + 1 < domainParts.length) {
      name += part.charAt(0).toUpperCase() + part.slice(1);
    }
  });

  return name;
};

const grades = [
  {
    course: "Calculus 1",
    score: 72,
    letterGrade: "C",
    year: 2021,
    semester: "Fall",
  },
  {
    course: "Intro to Ballroom Dance",
    score: 94,
    letterGrade: "A",
    year: 2021,
    semester: "Fall",
  },
  {
    course: "Computer Science 101",
    score: 65,
    letterGrade: "F",
    year: 2021,
    semester: "Fall",
  },
  {
    course: "Intro to Modern Physics",
    score: 88,
    letterGrade: "B",
    year: 2021,
    semester: "Fall",
  },

  {
    course: "Calculus 2",
    score: 84,
    letterGrade: "C",
    year: 2021,
    semester: "Spring",
  },
  {
    course: "Geometry",
    score: 97,
    letterGrade: "A",
    year: 2021,
    semester: "Spring",
  },
  {
    course: "Computer Science 101",
    score: 76,
    letterGrade: "C",
    year: 2021,
    semester: "Spring",
  },
  {
    course: "Physics II",
    score: 88,
    letterGrade: "B",
    year: 2021,
    semester: "Spring",
  },

  {
    course: "Calculus 3",
    score: 84,
    letterGrade: "C",
    year: 2022,
    semester: "Fall",
  },
  {
    course: "Abstract Algebra",
    score: 97,
    letterGrade: "A",
    year: 2022,
    semester: "Fall",
  },
  {
    course: "Computer Science 102",
    score: 76,
    letterGrade: "C",
    year: 2022,
    semester: "Fall",
  },
  {
    course: "Public Speaking",
    score: 88,
    letterGrade: "B",
    year: 2022,
    semester: "Fall",
  },

  {
    course: "Adv Calculus",
    score: 84,
    letterGrade: "C",
    year: 2022,
    semester: "Spring",
  },
  {
    course: "Geometry",
    score: 97,
    letterGrade: "A",
    year: 2022,
    semester: "Spring",
  },
  {
    course: "Javascript in the Modern Web",
    score: 76,
    letterGrade: "C",
    year: 2022,
    semester: "Spring",
  },
  {
    course: "Cryptography",
    score: 88,
    letterGrade: "B",
    year: 2022,
    semester: "Spring",
  },
];

module.exports = { getGrades };

首先,exchangeOktaTokenForFirebaseToken將提供來自 Firebase 的自定義令牌以在您的應用程序中使用。signInWithCustomTokenFirebase 允許您通過在客戶端上使用的方法完全控制您的身份驗證。您需要使用您的服務帳戶創建自定義令牌。您之前已將服務帳戶定義下載為 JSON 文件。現在您可以createCustomToken從您的auth對象調用您的服務帳戶。此功能需要 auid並且可以選擇接受您可能希望添加的其他聲明。請注意Firebase 會保留令牌名稱

然後,您可以從 Okta 授權服務器獲取令牌並將其提供給 Firebase 函數以使用OktaJwtVerifier. 如果 Okta 令牌有效,您將調用 Okta 授權服務器的userInfo端點以獲取有關您的用戶的其他信息。您可以將此信息作為其自定義聲明包含在您的 Firebase 令牌中。然後,您可以使用該firebaseApp對象通過這些聲明創建您的令牌。您將將此令牌返回給客戶端並使用它登錄。

接下來,您擁有該getGrades功能。您檢查context.auth用戶是否已登錄。如果沒有,您將引發錯誤。如果是,則允許用戶訪問該文件中的成績數據。

在 Firebase 中設置函數有兩種不同的方法onCallonRequest. onRequest為您提供處理來電的更原始形式。您需要設置 CORS 代碼、身份驗證以及onCall包裝器為您處理的所有好東西。例如,context.auth提供是因為您使用了onCall,而通過onRequest您需要手動獲取此信息。

使用 Firebase 模擬器在本地測試您的應用

現在您已準備好通過 Firebase 模擬器在本地測試您的應用程序。模擬器將使您的服務可以像部署到 Firebase 一樣進行通信。

首先,編輯您的.env文件以替換REACT_APP_ENV=productionREACT_APP_ENV=development. 此更改告訴應用程序連接到模擬器。接下來,在項目的根目錄中運行以下命令。

npm run build
firebase emulators:start

首先,您需要構建您的應用程序,因為 Firebase 期望您的應用程序位於build您之前配置的目錄中。接下來,它將創建一個模擬器並將您的函數和 Web 應用程序部署到該模擬器。默認情況下,Web 應用程序部署到localhost:5000而不是通常的localhost:3000.

如果您發現您與 Firebase 使用的默認端口有衝突,您可以更新您可用的端口的文件firebase.json條目。請參閱下面的示例文件,該文件使用端口 5002 進行託管,使用端口 5001 進行功能。emulators.functions.portemulators.hosting.portfirebase.json

{
  "hosting": {
    "public": "build",
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ],
    "rewrites": [
      {
        "source": "**",
        "destination": "/index.html"
      }
    ]
  },
  "functions": {
    "source": "functions"
  },
  "emulators": {
    "functions": {
      "port": 5001
    },
    "hosting": {
      "port": 5002
    },
    "ui": {
      "enabled": false
    }
  }
}

您需要打開 Okta 管理控制台並導航到您的 Okta 應用程序以將這些端口添加到允許列表中。

打開Okta 開發人員門戶登錄到 Okta。然後按Admin啟動管理控制台。

導航到應用程序>應用程序並找到此項目的 Okta 應用程序。選擇它進行編輯。在常規選項卡上,使用新端口編輯常規設置。例如,如果您的 Firebase 託管端口是 5000,請添加到http://localhost:5000/login/callbackSign -in redirect URIsSign-out redirect URIs。根據您的 Firebase 模擬器設置更新端口號並保存。http://localhost:5000

在 Okta 管理控制台中還有一個地方可以添加新端口。您將端口添加為受信任的來源,以便您的應用程序可以完成註銷過程。導航到Security > API並打開Trusted Origins選項卡。按+ Add Origin按鈕並添加帶有端口的新 URL,例如http://localhost:5000/. 選擇RedirectCORS複選框並保存,然後返回到您的應用程序。

此時,您應該能夠登錄 Okta,將您的令牌換成 Firebase 令牌,選擇一個學期,然後單擊Get Grades以查看您的報告卡生成。

最終項目的屏幕截圖,顯示經過身份驗證的用戶的報告卡

將您的應用部署到 Firebase

一旦成功,您就可以將應用程序部署到 Firebase。首先,如果您已將.env條目設置為REACT_APP_ENV,請將其設置為。如果您進行了任何編輯或更改,您可能需要再次運行該命令。完成後,從根目錄運行命令。productiondevelopmentnpm run buildfirebase deploy

完成此步驟後,您的 CLI 將為您的應用程序提供一個 URL。如果您單擊它,您應該會看到您的應用程序在 Firebase 上運行。但是,此時 Okta 將無法工作。您需要返回您的 Okta 管理門戶,在您的應用程序下,添加{yourFirebaseDomain}/login/callback到您的Sign-in redirect URIs{yourFirebaseDomain}到您的Sign-out redirect URIs到您的 Okta 應用程序的General Settings選項卡,並將 {yourFirebaseDomain} 添加為Trusted起源

現在返回到您在 Firebase 中的應用程序並單擊登錄以確保 Okta 已正確連接。登錄後,您應該能夠選擇一個學期並單擊“獲取成績”以查看生成的成績單。


鏈接:https ://developer.okta.com/blog/2022/06/27/react-firebase-serverless

#firebase #react #serverless

如何使用 Firebase 無服務器函數構建 React 應用程序