Skip to content

Commit

Permalink
feat(liveness): Liveness cred provider (#4260)
Browse files Browse the repository at this point in the history
* feat(liveness): add credentialProvider prop (#3790)

* feat(liveness): add credentialProvier prop

* chore: update sdk types

* chore: remove extra api definition

* update yarn.lock

* chore: update credential provider API based on internal review

* chore: add unit tests to fix coverage

* chore: address feedback

* chore: add comment on why credentials are copied, fix unit tests

* chore: add size limit check for react-liveness

* fix: reusable unit test size check

* chore: update types to better match AWS SDK types, export them as well

* Update packages/react-liveness/src/components/FaceLivenessDetector/service/types/liveness.ts

Co-authored-by: Caleb Pollman <cpollman@amazon.com>

* Update packages/react-liveness/src/components/FaceLivenessDetector/service/types/liveness.ts

Co-authored-by: Caleb Pollman <cpollman@amazon.com>

* chore: switch to using credential-provider@3.310.0

* address feedback

* Update packages/e2e/features/ui/components/liveness/with-credential-provider.feature

* update to use more recent aws-sdk deps

* try fixing deps

* update aws-sdk dep to 3.350.0

* maybe this fixes it

* another try

* fix yarn.lock

* fix e2e test

---------

Co-authored-by: Caleb Pollman <cpollman@amazon.com>
Co-authored-by: William Lee <43682783+wlee221@users.noreply.github.com>

* update yarn.lock

* Create pretty-laws-obey.md

* update ignored links

* fix yarn.lock

---------

Co-authored-by: Caleb Pollman <cpollman@amazon.com>
Co-authored-by: William Lee <43682783+wlee221@users.noreply.github.com>
  • Loading branch information
3 people authored Jul 19, 2023
1 parent d165ddf commit e4bf312
Show file tree
Hide file tree
Showing 23 changed files with 1,155 additions and 177 deletions.
5 changes: 5 additions & 0 deletions .changeset/pretty-laws-obey.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@aws-amplify/ui-react-liveness": minor
---

feat(liveness): Liveness cred provider
4 changes: 4 additions & 0 deletions .github/workflows/reusable-unit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@ jobs:
if: ${{ matrix.package == 'react' }}
run: yarn react size

- name: Check ${{ matrix.package }} bundle size
if: ${{ matrix.package == 'react-liveness' }}
run: yarn react-liveness size

- name: Cache ${{ matrix.package }}/dist
uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1 https://github.com/actions/cache/commit/88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8
with:
Expand Down
1 change: 1 addition & 0 deletions docs/src/data/ignoredLinks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export const IGNORED_LINKS = [
'https://github.com/aws-amplify/amplify-ui-swift/issues/new/choose', // 302 amplify-ui-wift redirects to amplify-ui-swift-liveness
'https://github.com/aws-amplify/amplify-ui-swift-liveness/issues/new/choose', // 302 amplify-ui-swift-liveness does not have issue templates yet
'https://developer.apple.com/design/human-interface-guidelines/patterns/accessing-private-data/', // 401 Apple doesn't like bots maybe
'https://developer.apple.com/documentation/xcode/localization', // 403
'https://twitter.com/AWSAmplify',
'https://tfhub.dev/tensorflow/tfjs-model/blazeface/1/default/1/model.json?tfjs-format=file', // 302 to a google cdn
'https://cdn.liveness.rekognition.amazonaws.com/face-detection/tensorflow/tfjs-backend-wasm/3.11.0/', // 404 this is the intentional path as the tfjs library will append the correct file name
Expand Down
1 change: 1 addition & 0 deletions examples/next/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"@aws-amplify/ui-react-storage": "^2.0.4",
"@aws-amplify/ui-react-notifications": "^1.0.5",
"@aws-amplify/ui-react-geo": "^1.0.1",
"@aws-sdk/credential-providers": "^3.370.0",
"aws-amplify": "latest",
"next": "^11.1.3",
"next-global-css": "^1.1.1",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import { View, Flex, Loader, Text } from '@aws-amplify/ui-react';
import { FaceLivenessDetector } from '@aws-amplify/ui-react-liveness';
import {
FaceLivenessDetector,
FaceLivenessDetectorCore,
} from '@aws-amplify/ui-react-liveness';
import { useLiveness } from './useLiveness';
import { SessionIdAlert } from './SessionIdAlert';
import LivenessInlineResults from './LivenessInlineResults';

export default function LivenessDefault({
disableInstructionScreen = false,
components = undefined,
credentialProvider = undefined,
}) {
const {
getLivenessResponse,
Expand Down Expand Up @@ -49,21 +53,40 @@ export default function LivenessDefault({

<Flex gap="0" direction="column" position="relative">
{!getLivenessResponse ? (
<FaceLivenessDetector
sessionId={createLivenessSessionApiData.sessionId}
region={'us-east-1'}
onUserCancel={onUserCancel}
onAnalysisComplete={async () => {
await handleGetLivenessDetection(
createLivenessSessionApiData.sessionId
);
}}
onError={(error) => {
console.error(error);
}}
disableInstructionScreen={disableInstructionScreen}
components={components}
/>
credentialProvider ? (
<FaceLivenessDetectorCore
sessionId={createLivenessSessionApiData.sessionId}
region={'us-east-1'}
onUserCancel={onUserCancel}
onAnalysisComplete={async () => {
await handleGetLivenessDetection(
createLivenessSessionApiData.sessionId
);
}}
onError={(error) => {
console.error(error);
}}
disableInstructionScreen={disableInstructionScreen}
components={components}
config={{ credentialProvider }}
/>
) : (
<FaceLivenessDetector
sessionId={createLivenessSessionApiData.sessionId}
region={'us-east-1'}
onUserCancel={onUserCancel}
onAnalysisComplete={async () => {
await handleGetLivenessDetection(
createLivenessSessionApiData.sessionId
);
}}
onError={(error) => {
console.error(error);
}}
disableInstructionScreen={disableInstructionScreen}
components={components}
/>
)
) : null}
</Flex>
</Flex>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ export default function NavBar({
<Link href="/ui/components/liveness/with-custom-components">
With Custom Components
</Link>

<Link href="/ui/components/liveness/with-credential-provider">
With Credential Provider
</Link>
</Flex>
</Card>
</View>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import awsExports from '@environments/liveness/liveness-environment/src/aws-exports';
export default awsExports;
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import dynamic from 'next/dynamic';
import React from 'react';
import { Amplify } from 'aws-amplify';
import { fromCognitoIdentityPool } from '@aws-sdk/credential-providers';
import { AwsCredentialProvider } from '@aws-amplify/ui-react-liveness';

import LivenessDefault from '../components/LivenessDefault';
import Layout from '../components/Layout';
import awsExports from './aws-exports';

Amplify.configure({
...awsExports,
Analytics: {
autoSessionRecord: false,
},
});

const App = () => {
const credentialProvider: AwsCredentialProvider = fromCognitoIdentityPool({
clientConfig: { region: 'us-east-2' },
identityPoolId: 'us-east-2:89d27d83-5313-46b2-a7cb-328604399400',
});

return (
<Layout>
<LivenessDefault credentialProvider={credentialProvider} />
</Layout>
);
};

export default dynamic(() => Promise.resolve(App), {
ssr: false,
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
Feature: Liveness with Custom Components

Liveness component supports using a custom credential provider.

Background:
Given I'm running the example "ui/components/liveness/with-credential-provider/"

@react
Scenario: See camera module and close with the close icon
Then I click the "Begin check" button
And I click the "close-icon"
Then I see the "Begin check" button

@react
Scenario: See camera module and instructions
Then I click the "Begin check" button
And I see "liveness-detector" element
And I see "connecting"
And I see "Move closer"
And I see "Face didn't fill oval within time limit."
And I click the "Try again" button
Then I see the "Begin check" button
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
exports[`exports should match the expected snapshot 1`] = `
Array [
"FaceLivenessDetector",
"FaceLivenessDetectorCore",
]
`;
Original file line number Diff line number Diff line change
@@ -1,98 +1,34 @@
import * as React from 'react';
import { useActor, useInterpret } from '@xstate/react';
import { Credentials as AmplifyCredentials } from '@aws-amplify/core';
import {
livenessMachine,
AwsTemporaryCredentials,
FaceLivenessDetectorProps as FaceLivenessDetectorPropsFromUi,
} from './service';
import { View, Flex } from '@aws-amplify/ui-react';

import { FaceLivenessDetectorProvider } from './providers';
import { StartLiveness } from './StartLiveness';
import { LivenessCheck } from './LivenessCheck';
import { getVideoConstraints } from './StartLiveness/helpers';
import { StartScreenComponents } from './shared/DefaultStartScreenComponents';
import FaceLivenessDetectorCore, {
FaceLivenessDetectorComponents,
} from './FaceLivenessDetectorCore';
import { LivenessDisplayText } from './displayText';
import { getDisplayText } from './utils/getDisplayText';
import { CheckScreenComponents } from './shared/FaceLivenessErrorModal';

const DETECTOR_CLASS_NAME = 'liveness-detector';

type FaceLivenessDetectorComponents = StartScreenComponents &
CheckScreenComponents;

export interface FaceLivenessDetectorProps
extends FaceLivenessDetectorPropsFromUi {
components?: FaceLivenessDetectorComponents;
displayText?: LivenessDisplayText;
}

const credentialProvider = async () => {
const credentials =
(await AmplifyCredentials.get()) as AwsTemporaryCredentials;
return credentials;
};

export default function FaceLivenessDetector(
props: FaceLivenessDetectorProps
): JSX.Element {
const {
disableInstructionScreen = false,
components,
config,
displayText,
} = props;
const currElementRef = React.useRef<HTMLDivElement>(null);
const {
hintDisplayText,
cameraDisplayText,
instructionDisplayText,
streamDisplayText,
errorDisplayText,
} = getDisplayText(displayText);

const service = useInterpret(livenessMachine, {
devTools: process.env.NODE_ENV === 'development',
context: {
componentProps: {
...props,
config: config ?? {},
},
},
});

const [state, send] = useActor(service);
const isStartView = state.matches('start') || state.matches('userCancel');

const beginLivenessCheck = React.useCallback(() => {
const videoConstraints = getVideoConstraints();

send({
type: 'BEGIN',
data: { videoConstraints },
});
}, [send]);

React.useLayoutEffect(() => {
if (disableInstructionScreen && isStartView) {
beginLivenessCheck();
}
}, [beginLivenessCheck, disableInstructionScreen, isStartView]);

const { config, ...rest } = props;
return (
<View className={DETECTOR_CLASS_NAME} testId={DETECTOR_CLASS_NAME}>
<FaceLivenessDetectorProvider componentProps={props} service={service}>
<Flex direction="column" ref={currElementRef}>
{isStartView ? (
<StartLiveness
beginLivenessCheck={beginLivenessCheck}
components={components}
instructionDisplayText={instructionDisplayText}
/>
) : (
<LivenessCheck
hintDisplayText={hintDisplayText}
cameraDisplayText={cameraDisplayText}
streamDisplayText={streamDisplayText}
errorDisplayText={errorDisplayText}
components={components}
/>
)}
</Flex>
</FaceLivenessDetectorProvider>
</View>
<FaceLivenessDetectorCore
{...rest}
config={{ credentialProvider, ...config }}
/>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import * as React from 'react';
import { useActor, useInterpret } from '@xstate/react';
import {
livenessMachine,
FaceLivenessDetectorCoreProps as FaceLivenessDetectorPropsFromUi,
} from './service';
import { View, Flex } from '@aws-amplify/ui-react';

import { FaceLivenessDetectorProvider } from './providers';
import { StartLiveness } from './StartLiveness';
import { LivenessCheck } from './LivenessCheck';
import { getVideoConstraints } from './StartLiveness/helpers';
import { StartScreenComponents } from './shared/DefaultStartScreenComponents';
import { LivenessDisplayText } from './displayText';
import { getDisplayText } from './utils/getDisplayText';
import { CheckScreenComponents } from './shared/FaceLivenessErrorModal';

const DETECTOR_CLASS_NAME = 'liveness-detector';

export type FaceLivenessDetectorComponents = StartScreenComponents &
CheckScreenComponents;

export interface FaceLivenessDetectorCoreProps
extends FaceLivenessDetectorPropsFromUi {
components?: FaceLivenessDetectorComponents;
displayText?: LivenessDisplayText;
}

export default function FaceLivenessDetectorCore(
props: FaceLivenessDetectorCoreProps
): JSX.Element {
const {
disableInstructionScreen = false,
components,
config,
displayText,
} = props;
const currElementRef = React.useRef<HTMLDivElement>(null);
const {
hintDisplayText,
cameraDisplayText,
instructionDisplayText,
streamDisplayText,
errorDisplayText,
} = getDisplayText(displayText);

const service = useInterpret(livenessMachine, {
devTools: process.env.NODE_ENV === 'development',
context: {
componentProps: {
...props,
config: config ?? {},
},
},
});

const [state, send] = useActor(service);
const isStartView = state.matches('start') || state.matches('userCancel');

const beginLivenessCheck = React.useCallback(() => {
const videoConstraints = getVideoConstraints();

send({
type: 'BEGIN',
data: { videoConstraints },
});
}, [send]);

React.useLayoutEffect(() => {
if (disableInstructionScreen && isStartView) {
beginLivenessCheck();
}
}, [beginLivenessCheck, disableInstructionScreen, isStartView]);

return (
<View className={DETECTOR_CLASS_NAME} testId={DETECTOR_CLASS_NAME}>
<FaceLivenessDetectorProvider componentProps={props} service={service}>
<Flex direction="column" ref={currElementRef}>
{isStartView ? (
<StartLiveness
beginLivenessCheck={beginLivenessCheck}
components={components}
instructionDisplayText={instructionDisplayText}
/>
) : (
<LivenessCheck
hintDisplayText={hintDisplayText}
cameraDisplayText={cameraDisplayText}
streamDisplayText={streamDisplayText}
errorDisplayText={errorDisplayText}
components={components}
/>
)}
</Flex>
</FaceLivenessDetectorProvider>
</View>
);
}
Loading

0 comments on commit e4bf312

Please sign in to comment.