Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Amplify Liveness intermitently does not ask for Camera Permission on FireFox mobile (iOS & Android) #5847

Open
4 tasks done
paternina opened this issue Sep 27, 2024 · 24 comments
Labels
Liveness not-reproducible Not able to reproduce the issue question General question

Comments

@paternina
Copy link

Before creating a new issue, please confirm:

On which framework/platform are you having an issue?

React

Which UI component?

Liveness

How is your app built?

Vite

What browsers are you seeing the problem on?

Firefox

Which region are you seeing the problem in?

us-east-1

Please describe your bug.

Sometimes the package does not ask for camera permission on Firefox mobile, and keep on an infinite loop displaying the waitingCameraPermissionText.

Please note that it is intermittent, sometimes works, sometimes does not but it fails in most cases. It works fine on Firefox for desktop and other browsers, the issue is only on Firefox mobile.

What's the expected behaviour?

We should see a pop up requesting the camera access permission or display the component on the screen.

Help us reproduce the bug!

  • Create a vite react app
  • Install amplify liveness as described in the documentation
  • Use the example provided in the documentation to create a sessions ID

Code Snippet

// Put your code below this line.
import React, { useEffect, useState } from "react";
import { FaceLivenessDetector } from "@aws-amplify/ui-react-liveness";
import { Loader, ThemeProvider, Button, Flex } from "@aws-amplify/ui-react";
import CancelIcon from "../components/icons/CancelIcon";
import CheckIcon from "../components/icons/CheckIcon";
import { API_URL } from "../data/constants";
import { FaceLivenessDictionary } from "../data/translations";
import { useTranslation } from "react-i18next";
import { extractLanguageCode } from "../utils/common";

const FaceLiveness = ({ handleNextStep, dispatch, acessToken }) => {
  const { t, i18n } = useTranslation();
  const [language, setLanguage] = useState("");
  const [loading, setLoading] = useState(true);
  const [createLivenessApiData, setCreateLivenessApiData] = useState(null);
  const [result, setResult] = useState({
    completed: false,
    ok: false,
  });

  const handleError = (error) => {
    dispatch({
      type: "awsLivenessError",
      value: error,
    });
  };

  const fetchCreateLiveness = async () => {
    try {
      const response = await fetch(`${API_URL}/create_face_liveness_response`, {
        headers: new Headers({
          Authorization: `Bearer ${acessToken}`,
        }),
      });
      const json = await response.json();
      setCreateLivenessApiData(json);
      setLoading(false);
    } catch (error) {
      handleError({
        state: "SERVER_ERROR",
        message: "Failed to fetch when createliveness",
      });
    }
  };

  useEffect(() => {
    const language = i18n.language;
    setLanguage(extractLanguageCode(language));
    fetchCreateLiveness();
  }, []);

  const handleSuccess = (data) => {
    setResult({
      completed: true,
      ok: true,
    });

    dispatch({ type: "awsLiveness", value: data });

    setTimeout(() => {
      handleNextStep();
    }, 2000);
  };

  const handleFailure = () => {
    setResult({
      completed: true,
      ok: false,
    });

    fetchCreateLiveness();

    setTimeout(() => {
      setResult({
        completed: false,
        ok: false,
      });
    }, 2000);
  };

  const fetchApi = async () => {
    try {
      const response = await fetch(
        `${API_URL}/get_face_liveness_response/${createLivenessApiData.SessionId}`,
        {
          headers: new Headers({
            Authorization: `Bearer ${acessToken}`,
          }),
        }
      );

      return response;
    } catch (error) {
      handleError({
        state: "SERVER_ERROR",
        message: "Failed to fetch when getting liveness",
      });
      return false;
    }
  };

  const handleResponse = async (response) => {
    const data = await response.json();

    return data;
  };

  const handleAnalysisComplete = async () => {
    const response = await fetchApi();

    if (!response) {
      handleFailure();
      return;
    }

    const data = await handleResponse(response);

    if (data.Confidence < 75) {
      handleFailure();
    } else {
      handleSuccess(data);
    }
  };

  const handleRetry = () => {
    fetchCreateLiveness();
    setLoading(true);
    setTimeout(() => {
      setLoading(false);
    }, 1000);
  };

  return (
    <div className="w-full flex flex-col align-center justify-between flex-grow text-center lg:px-20 z-10">
      {result.completed ? (
        <>
          <p className="text-lg md:text-xl">{t("result")}</p>
          {result.ok ? (
            <>
              <CheckIcon />
              <p className="text-lg md:text-xl">{t("redirectMessage")}...</p>
            </>
          ) : (
            <>
              <CancelIcon />
              <p className="text-lg md:text-xl">{t("retryMessage")}...</p>
            </>
          )}
        </>
      ) : loading ? (
        <Loader className="self-center" />
      ) : (
        <ThemeProvider>
          <FaceLivenessDetector
            sessionId={createLivenessApiData.SessionId}
            region="us-east-1"
            onAnalysisComplete={handleAnalysisComplete}
            displayText={FaceLivenessDictionary[language]}
            onError={handleError}
            components={{
              ErrorView: ({ children }) => {
                return (
                  <Flex
                    justifyContent="center"
                    alignItems="center"
                    width="100%"
                    height="100%"
                  >
                    <Flex
                      backgroundColor="white"
                      direction="column"
                      justifyContent="center"
                      padding="32px"
                    >
                      {children}
                      <Button
                        maxWidth="120px"
                        alignSelf="center"
                        onClick={handleRetry}
                      >
                        {t("retry")}?
                      </Button>
                    </Flex>
                  </Flex>
                );
              },
            }}
          />
        </ThemeProvider>
      )}
    </div>
  );
};

export default FaceLiveness;

Console log output

No console error, the request to create the session ID is OK.

Additional information and screenshots

LivenessError
@github-actions github-actions bot added pending-triage Issue is pending triage pending-maintainer-response Issue is pending response from an Amplify UI maintainer labels Sep 27, 2024
@reesscot
Copy link
Contributor

@paternina What version of Firefox mobile are you using and what's your OS version?

@Tyg0th
Copy link

Tyg0th commented Sep 27, 2024

@paternina What version of Firefox mobile are you using and what's your OS version?

Is Firefox Mobile 130.0.1 and we used iOS 18 but we have the same behavior in Android 13

@thaddmt thaddmt added question General question Liveness pending-triage Issue is pending triage and removed pending-triage Issue is pending triage question General question labels Sep 27, 2024
@reesscot
Copy link
Contributor

When you say FireFox Mobile, you mean Firefox for iOS and Firefox for Android, right?

@Tyg0th
Copy link

Tyg0th commented Sep 27, 2024

That's right

@reesscot
Copy link
Contributor

What version of the ui-react-liveness package are you using?

@reesscot reesscot added pending-community-response Issue is pending response from the issue requestor or other community members and removed pending-triage Issue is pending triage pending-maintainer-response Issue is pending response from an Amplify UI maintainer labels Sep 27, 2024
@Tyg0th
Copy link

Tyg0th commented Sep 28, 2024

We have the following:

"@aws-amplify/ui-react": "^6.5.1",
"@aws-amplify/ui-react-liveness": "^3.1.11",
"aws-amplify": "^6.6.2"

@github-actions github-actions bot added pending-maintainer-response Issue is pending response from an Amplify UI maintainer and removed pending-community-response Issue is pending response from the issue requestor or other community members labels Sep 28, 2024
@reesscot
Copy link
Contributor

reesscot commented Sep 30, 2024

Hi @Tyg0th,
I'm unable to reproduce this issue using iOS 18 and Firefox 130.1. The browser is correctly prompting me for permissions on every load of the component.

Any other patterns of when this happens you can think of that might help us narrow this down?

@reesscot reesscot added not-reproducible Not able to reproduce the issue question General question and removed pending-maintainer-response Issue is pending response from an Amplify UI maintainer labels Sep 30, 2024
@esauerbo
Copy link
Contributor

@Tyg0th are you using http by any chance? Some browsers will block camera and microphone access on insecure origins. You could try printing navigator.mediaDevices to the console; if it's undefined it likely means camera access is blocked.

@github-actions github-actions bot added the pending-maintainer-response Issue is pending response from an Amplify UI maintainer label Sep 30, 2024
@esauerbo esauerbo removed the pending-maintainer-response Issue is pending response from an Amplify UI maintainer label Sep 30, 2024
@paternina
Copy link
Author

Hi @reesscot it fails intermittently sometimes ask for permissions and sometimes it does not, the only difference from our code and the one shared in the documentation is that we are using a check camera utility function before showing the component

 useEffect(() => {
      const checkCamera = async () => {
        const cameraExist = await detectWebcam();
        setDeviceHasCamera(cameraExist); // if false show an error message and does not load the component
      };
      checkCamera();
  }, []);

This is just a JavaScript function that check if customer have a camera.

export const detectWebcam = async () => {
    let md = navigator.mediaDevices;

    if (!md || !md.enumerateDevices){
        return false;
    }

    try {
        const devices = await md.enumerateDevices();
        return devices.some(device => device.kind === "videoinput"); 
    } catch (error) {
        console.error('Error enumerating devices: ', error);
        return false;
    }
};

It may responde your question @esauerbo , we are using https and we check the cameras using the above function.

As you may noticed, the FaceLivenessDetector component is loaded and is always showing the waitingCameraPermissionText script.

@github-actions github-actions bot added the pending-maintainer-response Issue is pending response from an Amplify UI maintainer label Sep 30, 2024
@thaddmt
Copy link
Contributor

thaddmt commented Oct 1, 2024

@paternina just for reference, we perform a similar check in the component to make sure that a customer has a valid video devices - https://github.com/aws-amplify/amplify-ui/blob/main/packages/react-liveness/src/components/FaceLivenessDetector/service/machine/machine.ts#L961

However we also have a simple check for virtual cameras as the Rekognition services want to avoid the usage of virtual cameras when possible to help avoid fraud.

@paternina
Copy link
Author

paternina commented Oct 1, 2024

Hi @thaddmt

Thank you for the reference!

I’d like to clarify my understanding: it seems that this implementation only checks if the user has a virtual camera device and then throws an error. The error returned should be handled by the onError?: (livenessError: LivenessError) => void; method from the FaceLivenessDetectorCoreProps interface. If that's the case, we should be able to catch it in the dispatch, set it in the state, and the component should display a different error message on the screen rather than the one requesting permission, correct?

Additionally, please note that the FaceLivenessDetector component is being displayed but is always showing the "waiting for camera permission" text, so I am not sure if the issue is related to the detectCamera function.

To provide more context, the main issue we're encountering is that when customers visit our site for the first time using Firefox for Android and Firefox for iOS, it correctly requests camera access, and everything works as expected. However, on subsequent visits (the second or third time), the component fails to load even though permissions are granted. Currently, the only workaround is to clear all site permissions and start fresh.

@esauerbo
Copy link
Contributor

esauerbo commented Oct 7, 2024

@paternina the component throws an error if there are only non virtual cameras available, but it also checks for a few other constraints like frame rate and video width/height. Otherwise that's correct.

Are you still able to reproduce this behavior if you remove the detectWebcam logic? If so would you be able to share a reproduction repository with us?

@paternina
Copy link
Author

paternina commented Oct 15, 2024

Hi @esauerbo

Unfortunately I cannot share a public repository, it is a private work and company does not allow us to share it, but, the component is exactly like shared above, and I also shared the detectWebcam logic. I removed the detectWebcam logic and the issue persist, it request access to camera once, but subsequent visits do not request it.

@github-actions github-actions bot added the pending-maintainer-response Issue is pending response from an Amplify UI maintainer label Oct 15, 2024
@reesscot
Copy link
Contributor

@paternina Can you share what you are using in the screenshot to debug?

Are you using the camera anywhere else in your app which might be preventing the Liveness component from using it? In the screenshot I see the log message "Stopping active stream on Back Triple Camera". The Liveness component doesn't support using the back cameras, on the forward/user facing cameras.

CleanShot 2024-10-15 at 12 06 12@2x

@github-actions github-actions bot removed the pending-maintainer-response Issue is pending response from an Amplify UI maintainer label Oct 15, 2024
@cwomack cwomack added the pending-community-response Issue is pending response from the issue requestor or other community members label Oct 15, 2024
@paternina
Copy link
Author

Hi @reesscot , yes we use a component to capture the user document (KYC flow) before the liveness process, it uses another package and a custom fallback option when customer device does not meet the minimum requirements for the first one. However, this step is completed successfully and then pass to LivenessDetector.

@github-actions github-actions bot added pending-maintainer-response Issue is pending response from an Amplify UI maintainer and removed pending-community-response Issue is pending response from the issue requestor or other community members labels Oct 17, 2024
@reesscot
Copy link
Contributor

reesscot commented Oct 18, 2024

@paternina That's good information, thanks for sharing. My suspicion is that your other component is not releasing the video stream, which is causing the Liveness component to be unable to access the camera. Can you try replacing the Liveness component with a getUserMedia call using the Liveness default constraints?

@github-actions github-actions bot removed the pending-maintainer-response Issue is pending response from an Amplify UI maintainer label Oct 18, 2024
@cwomack cwomack added the pending-community-response Issue is pending response from the issue requestor or other community members label Oct 18, 2024
@cwomack
Copy link
Member

cwomack commented Nov 25, 2024

@paternina, wanted to circle back and see if you tried the suggestion above. Let us know if you've had a chance to replace the Liveness component with a getUserMedia call using the Liveness default constraints?

@gary-hodgson-infotrack
Copy link

gary-hodgson-infotrack commented Dec 11, 2024

Hi @cwomack & @reesscot

We have had a similar issue reported by a client. They are using Firefox version 133.0.2, Android version 14, Samsung Galaxy A53. When they switch over to Chrome it works as expected.

When the user loads the liveness page there is no prompt asking to use the camera. We get the below error

 error: {
  "state": "CAMERA_ACCESS_ERROR",
  "error": {}
}

And the UI defaults to:
image

Manually changing the camera permissions (and all other media permissions) for our website to allowed, we see the same issue.

Using BrowserStack to test on different devices, we can replicate the issue on additional devices, a Samsung A51, A52 using Firefox (stopped testing on other Samsung devices at this point).

Looking at your above mentioned recommendations of using getUserMedia call using the Liveness default constraints in place of the liveness component, we can see that there is no prompt asking for camera permissions when using Firefox on a Samsung A52 device. Changing the Liveness default constraints to something like { audio: false, video: true } the prompt to request camera permissions works as expected for Firefox on the A52 device.

Testing liveness on Firefox on my device, Android 14, Samsung Galaxy S23 Ultra it works as expected (additionally the getUserMedia query with the Liveness default constraints works) and I get the prompt to enable camera permissions, the issue seems to be related to the model of the mobile.

Using:

 "@aws-amplify/ui-react-liveness": "^3.1.18",
"aws-amplify": "^6.10.2"

Let me know if you require any more details to look into this issue further for us, thanks

@github-actions github-actions bot added pending-maintainer-response Issue is pending response from an Amplify UI maintainer and removed pending-community-response Issue is pending response from the issue requestor or other community members labels Dec 11, 2024
@cwomack
Copy link
Member

cwomack commented Dec 13, 2024

@gary-hodgson-infotrack, thanks for the testing and response here. It may be that this is a Firefox specific issue, but we're going to try and reproduce this internally and follow up.

@github-actions github-actions bot removed the pending-maintainer-response Issue is pending response from an Amplify UI maintainer label Dec 13, 2024
@swathimaddali-infotrack

@gary-hodgson-infotrack, thanks for the testing and response here. It may be that this is a Firefox specific issue, but we're going to try and reproduce this internally and follow up.

@cwomack do you have any updates on this issue raised by Gary from our team. The client has reached out that this is still an issue with the specific firefox version on the device Android version 14, Samsung Galaxy A53.

@github-actions github-actions bot added the pending-maintainer-response Issue is pending response from an Amplify UI maintainer label Jan 31, 2025
@cwomack
Copy link
Member

cwomack commented Jan 31, 2025

@swathimaddali-infotrack, thanks for the additional info from the client. Is there a way to confirm if this is experienced on later version of Firefox as well? Or has this been pinned down to only occur with this specific version (or specifically the combination of the Firefox version and the Android device)? Trying to see if this is isolated to that combo or if it's impacting other devices outside the Samsung A52/53's. Basically, want to ask either you or the client to use the latest version of Firefox and confirm if the behavior continues.

@github-actions github-actions bot removed the pending-maintainer-response Issue is pending response from an Amplify UI maintainer label Jan 31, 2025
@cwomack cwomack added the pending-community-response Issue is pending response from the issue requestor or other community members label Jan 31, 2025
@IvanWijayaInfoTrack
Copy link

IvanWijayaInfoTrack commented Feb 10, 2025

@cwomack Just to add some more information. We tried using android studio - virtual device with same exact firefox version and Android 14, and it works great. But unfortunately we cannot get the Samsung A series to test directly. I wonder whether that's device specific issue or not.

@github-actions github-actions bot added pending-maintainer-response Issue is pending response from an Amplify UI maintainer and removed pending-community-response Issue is pending response from the issue requestor or other community members labels Feb 10, 2025
@tiffanynwyeung
Copy link
Member

@IvanWijayaInfoTrack and anyone else following this thread, thank you for the additional information. We're investigating this issue and looking for ways to be able to reproduce this on our end. We'll be sure to follow up with another comment if we can identify the root cause.

@github-actions github-actions bot removed the pending-maintainer-response Issue is pending response from an Amplify UI maintainer label Feb 12, 2025
@IvanWijayaInfoTrack
Copy link

Thank you @tiffanynwyeung .

@github-actions github-actions bot added the pending-maintainer-response Issue is pending response from an Amplify UI maintainer label Feb 12, 2025
@jordanvn jordanvn removed the pending-maintainer-response Issue is pending response from an Amplify UI maintainer label Feb 13, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Liveness not-reproducible Not able to reproduce the issue question General question
Projects
None yet
Development

No branches or pull requests

12 participants