javascript icon indicating copy to clipboard operation
javascript copied to clipboard

feat(clerk-expo): Introduce support for LocalAuth with LocalCredentials

Open panteliselef opened this issue 1 year ago • 3 comments

Description

This PR introduces a new experimental hook API

import {
  __experimental_useLocalCredentials as useLocalCredentials,
} from "@clerk/clerk-expo";

const {
  hasCredentials, // boolean
  setCredentials, // ({ identifier: string, password: string }) => void
  authenticate, // () => Promise<SignInResource>
  clearCredentials, // () => Promise<void>
} = useLocalCredentials();

Video of the new UX flow

https://github.com/clerk/javascript/assets/19269911/7b8fa397-f3e1-440c-bf9f-1fc8a3602ef1

1. LocalCrendetialsProvider

import {
  ClerkProvider,
  __experimental_LocalCredentialsProvider as LocalCredentialsProvider,
} from "@clerk/clerk-expo";


<ClerkProvider publishableKey={publishableKey}>
	<LocalCredentialsProvider>
		{...}
	</LocalCredentialsProvider>
</ClerkProvider>

2.Example usage in a profile page

import { 
  useUser, 
  useClerk, 
  __experimental_useLocalAuthCredentials as useLocalAuthCredentials
} from "@clerk/clerk-expo";

export default function Page() {
  const { user } = useUser();
  const { signOut } = useClerk();

  const {
    hasCredentials,
    clearCredentials,
  } = useLocalCredentials();

  return (
    <View>
      <Text>Settings, {user?.emailAddresses[0].emailAddress}</Text>
      <Button title="Sign out" onPress={() => signOut()} />
      {
        hasLocalAuthCredentials &&
        <Button title="Remove biometric credentials" onPress={() => clearLocalAuthAccount()} />
      }
    </View>
  );
}

3.Example usage in a custom sign in flow


import {
  useSignIn,
  __experimental_useLocalCredentials as useLocalCredentials,
} from "@clerk/clerk-expo";
import { Link, Stack, useRouter } from "expo-router";
import {
  Text,
  TextInput,
  Button,
  View,
  TouchableOpacity,
  StyleSheet,
} from "react-native";
import React from "react";
import { SymbolView } from "expo-symbols";

export default function Page() {
  const { signIn, setActive, isLoaded } = useSignIn();
  const router = useRouter();

  const [emailAddress, setEmailAddress] = React.useState("");
  const [password, setPassword] = React.useState("");
  const { hasCredentials, setCredentials, authenticate, biometryType } =
    useLocalCredentials();

  const onSignInPress = React.useCallback(
    async (useLocal = false) => {
      if (!isLoaded) {
        return;
      }

      try {
        const signInAttempt =
          hasCredentials && useLocal
            ? await authenticate()
            : await signIn.create({
                identifier: emailAddress,
                password,
              });

        if (signInAttempt.status === "complete") {
          await setActive({ session: signInAttempt.createdSessionId });
          if (!(hasCredentials && useLocal)) {
            await setCredentials({
              identifier: emailAddress,
              password,
            })
          }

          // navigate away
        } else {
          // handle other statuses of sign in 
        }
      } catch (err: any) {
         // handle any other error
      }
    },
    [isLoaded, emailAddress, password]
  );

  return (
    <View>
      <TextInput
        value={emailAddress}
        onChangeText={(emailAddress) => setEmailAddress(emailAddress)}
      />
      <TextInput
        value={password}
        onChangeText={(password) => setPassword(password)}
      />
      <Button title="Sign In" onPress={() => onSignInPress()} />
      {hasCredentials && biometryType && (
        <TouchableOpacity
          onPress={() => onSignInPress(true)}
        >
          <SymbolView
            name={
              biometryType === "face-recognition" ? "faceid" : "touchid"
            }
            type="monochrome"
          />
        </TouchableOpacity>
      )}
    </View>
  );
}

Checklist

  • [ ] npm test runs as expected.
  • [ ] npm run build runs as expected.
  • [ ] (If applicable) JSDoc comments have been added or updated for any package exports
  • [ ] (If applicable) Documentation has been updated

Type of change

  • [ ] 🐛 Bug fix
  • [ ] 🌟 New feature
  • [ ] 🔨 Breaking change
  • [ ] 📖 Refactoring / dependency upgrade / documentation
  • [ ] other:

panteliselef avatar Jul 03 '24 21:07 panteliselef