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

[expo-local-authentication] Add support for promptMessage, cancelLabel and disableDeviceFallback on Android #8219

Merged
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -33,8 +33,9 @@ export default class LocalAuthenticationScreen extends React.Component<{}, State
this.setState({ waiting: true });
try {
const result = await LocalAuthentication.authenticateAsync({
promptMessage: 'This message only shows up on iOS',
fallbackLabel: '',
promptMessage: 'Authenticate',
cancelLabel: 'Cancel label',
disableDeviceFallback: true,
});
if (result.success) {
alert('Authenticated!');
Expand Down
Expand Up @@ -15,6 +15,7 @@

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

Expand All @@ -32,7 +33,6 @@ public class LocalAuthenticationModule extends ExportedModule {
private boolean mIsAuthenticating = false;
private ModuleRegistry mModuleRegistry;
private UIManager mUIManager;

diegolmello marked this conversation as resolved.
Show resolved Hide resolved
private static final int AUTHENTICATION_TYPE_FINGERPRINT = 1;

private final BiometricPrompt.AuthenticationCallback mAuthenticationCallback =
Expand Down Expand Up @@ -96,7 +96,7 @@ public void isEnrolledAsync(final Promise promise) {
}

@ExpoMethod
public void authenticateAsync(final Promise promise) {
public void authenticateAsync(final Map<String, Object> options, final Promise promise) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
promise.reject("E_NOT_SUPPORTED", "Cannot display biometric prompt on android versions below 6.0");
return;
Expand Down Expand Up @@ -130,6 +130,22 @@ public void run() {
return;
}

String promptMessage = "";
String cancelLabel = "";
boolean disableDeviceFallback = false;

if (options.containsKey("promptMessage")) {
promptMessage = (String) options.get("promptMessage");
}

if (options.containsKey("cancelLabel")) {
cancelLabel = (String) options.get("cancelLabel");
}

if (options.containsKey("disableDeviceFallback")) {
disableDeviceFallback = (Boolean) options.get("disableDeviceFallback");
}

mIsAuthenticating = true;
mPromise = promise;
mCancellationSignal = new CancellationSignal();
Expand All @@ -138,10 +154,13 @@ public void run() {
Executor executor = Executors.newSingleThreadExecutor();
BiometricPrompt biometricPrompt = new BiometricPrompt(fragmentActivity, executor, mAuthenticationCallback);

BiometricPrompt.PromptInfo promptInfo = new BiometricPrompt.PromptInfo.Builder()
.setDeviceCredentialAllowed(true)
.setTitle("Authenticate")
.build();
BiometricPrompt.PromptInfo.Builder promptInfoBuilder = new BiometricPrompt.PromptInfo.Builder()
.setDeviceCredentialAllowed(!disableDeviceFallback)
.setTitle(promptMessage);
if (cancelLabel != null && disableDeviceFallback) {
promptInfoBuilder.setNegativeButtonText(cancelLabel);
}
BiometricPrompt.PromptInfo promptInfo = promptInfoBuilder.build();
biometricPrompt.authenticate(promptInfo);
}
});
Expand Down
20 changes: 7 additions & 13 deletions packages/expo-local-authentication/build/LocalAuthentication.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

27 changes: 11 additions & 16 deletions packages/expo-local-authentication/src/LocalAuthentication.ts
@@ -1,6 +1,5 @@
import { UnavailabilityError } from '@unimodules/core';
import invariant from 'invariant';
import { Platform } from 'react-native';

import ExpoLocalAuthentication from './ExpoLocalAuthentication';
import {
Expand Down Expand Up @@ -47,24 +46,20 @@ export async function authenticateAsync(
options = { promptMessage: options };
}

if (Platform.OS === 'ios') {
if (options.hasOwnProperty('promptMessage')) {
invariant(
typeof options.promptMessage === 'string' && options.promptMessage.length,
'LocalAuthentication.authenticateAsync : `options.promptMessage` must be a non-empty string.'
);
}
if (options.hasOwnProperty('promptMessage')) {
invariant(
typeof options.promptMessage === 'string' && options.promptMessage.length,
'LocalAuthentication.authenticateAsync : `options.promptMessage` must be a non-empty string.'
);
}

diegolmello marked this conversation as resolved.
Show resolved Hide resolved
const promptMessage = options.promptMessage || 'Authenticate';
const result = await ExpoLocalAuthentication.authenticateAsync({ ...options, promptMessage });
const promptMessage = options.promptMessage || 'Authenticate';
const result = await ExpoLocalAuthentication.authenticateAsync({ ...options, promptMessage });

if (result.warning) {
console.warn(result.warning);
}
return result;
} else {
return await ExpoLocalAuthentication.authenticateAsync();
if (result.warning) {
console.warn(result.warning);
}
return result;
}

export async function cancelAuthenticate(): Promise<void> {
Expand Down
Expand Up @@ -6,9 +6,9 @@ export enum AuthenticationType {
}

export type LocalAuthenticationOptions = {
// iOS only
promptMessage?: string;
cancelLabel?: string;
fallbackLabel?: string;
disableDeviceFallback?: boolean;
// iOS only
fallbackLabel?: string;
};

This file was deleted.

Expand Up @@ -6,7 +6,7 @@ beforeEach(() => {
ExpoLocalAuthentication.authenticateAsync.mockImplementation(async () => ({ success: true }));
});

it(`uses options on iOS`, async () => {
it(`uses options`, async () => {
const options = {
promptMessage: 'Authentication is required',
cancelLabel: 'Abort',
Expand All @@ -18,7 +18,7 @@ it(`uses options on iOS`, async () => {
expect(ExpoLocalAuthentication.authenticateAsync).toHaveBeenLastCalledWith(options);
});

it(`throws when an invalid message is used on iOS`, async () => {
it(`throws when an invalid message is used`, async () => {
expect(
LocalAuthentication.authenticateAsync({ promptMessage: undefined as any })
).rejects.toThrow();
Expand Down