clerk / javascript

Official Javascript repository for Clerk authentication
https://clerk.com
MIT License
981 stars 221 forks source link

[DO NOT MERGE] feat(clerk-expo): Introduce support for LocalAuth with LocalCredentials #3663

Open panteliselef opened 3 weeks ago

panteliselef commented 3 weeks ago

Description

This PR introduces a new experimental hook API

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

const {
  hasCredentials, // boolean
  userOwnsCredentials, // boolean | null
  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_useLocalCredentials as useLocalCredentials,
} 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()} />
      {
        hasCredentials &&
        <Button title="Remove biometric credentials" onPress={() => clearCredentials()} />
      }
    </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

Type of change

changeset-bot[bot] commented 3 weeks ago

🦋 Changeset detected

Latest commit: 388877aad1e3d15121874ea8ba39db7a4cef1180

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package | Name | Type | | ----------------- | ----- | | @clerk/clerk-expo | Minor |

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

panteliselef commented 3 weeks ago

!snapshot

clerk-cookie commented 3 weeks ago
Hey @panteliselef - the snapshot version command generated the following package versions: Package Version
@clerk/clerk-expo 1.3.0-snapshot.ve946474
gatsby-plugin-clerk 5.0.0-beta.45

Tip: Use the snippet copy button below to quickly install the required packages. @clerk/clerk-expo

npm i @clerk/clerk-expo@1.3.0-snapshot.ve946474 --save-exact

gatsby-plugin-clerk

npm i gatsby-plugin-clerk@5.0.0-beta.45 --save-exact
panteliselef commented 2 weeks ago

!snapshot

clerk-cookie commented 2 weeks ago
Hey @panteliselef - the snapshot version command generated the following package versions: Package Version
@clerk/clerk-expo 1.3.0-snapshot.v388877a
gatsby-plugin-clerk 5.0.0-beta.45
@clerk/testing 1.1.10-snapshot.v388877a

Tip: Use the snippet copy button below to quickly install the required packages. @clerk/clerk-expo

npm i @clerk/clerk-expo@1.3.0-snapshot.v388877a --save-exact

gatsby-plugin-clerk

npm i gatsby-plugin-clerk@5.0.0-beta.45 --save-exact

@clerk/testing

npm i @clerk/testing@1.1.10-snapshot.v388877a --save-exact