import AsyncStorage from '@react-native-async-storage/async-storage'; import Slider from '@react-native-community/slider'; import { Subscription } from '@unimodules/core'; import * as AppleAuthentication from 'expo-apple-authentication'; import React from 'react'; import { Alert, Button, ScrollView, StyleSheet, Text, View } from 'react-native'; import MonoText from '../components/MonoText'; const { AppleAuthenticationButtonStyle, AppleAuthenticationButtonType, AppleAuthenticationCredentialState, AppleAuthenticationScope, } = AppleAuthentication; type State = { isAvailable: boolean; buttonStyle: AppleAuthentication.AppleAuthenticationButtonStyle; buttonType: AppleAuthentication.AppleAuthenticationButtonType; cornerRadius: number; credentials?: AppleAuthentication.AppleAuthenticationCredential | null; credentialState: AppleAuthentication.AppleAuthenticationCredentialState | null; }; const USER_CREDENTIAL_KEY = 'ExpoNativeComponentList/AppleAuthentication'; const CREDENTIAL_MESSAGES = { [AppleAuthenticationCredentialState.REVOKED]: 'Your authorization has been revoked.', [AppleAuthenticationCredentialState.AUTHORIZED]: "You're authorized.", [AppleAuthenticationCredentialState.NOT_FOUND]: "You're not registered yet.", [AppleAuthenticationCredentialState.TRANSFERRED]: 'Credentials transferred.', // Whatever that means... }; // See: https://github.com/expo/expo/pull/10229#discussion_r490961694 // eslint-disable-next-line @typescript-eslint/ban-types export default class AppleAuthenticationScreen extends React.Component<{}, State> { static navigationOptions = { title: 'Apple Authentication', }; readonly state: State = { isAvailable: false, buttonStyle: AppleAuthenticationButtonStyle.WHITE, buttonType: AppleAuthenticationButtonType.SIGN_IN, cornerRadius: 5, credentials: null, credentialState: null, }; _subscription?: Subscription; componentDidMount() { this.checkAvailability(); this.checkCredentials(); this._subscription = AppleAuthentication.addRevokeListener(this.revokeListener); } componentWillUnmount() { if (this._subscription) { this._subscription.remove(); } } revokeListener = () => { this.setState({ credentials: null }); Alert.alert('Credentials revoked!'); }; checkAvailability = async () => { const isAvailable = await AppleAuthentication.isAvailableAsync(); this.setState({ isAvailable }); }; signIn = async () => { try { const credentials = await AppleAuthentication.signInAsync({ requestedScopes: [AppleAuthenticationScope.FULL_NAME, AppleAuthenticationScope.EMAIL], state: 'this-is-a-test', }); this.setState({ credentials }); if (credentials.user) { await AsyncStorage.setItem(USER_CREDENTIAL_KEY, credentials.user); } await this.checkCredentials(); } catch (error) { alert(error); } }; refresh = async () => { try { const credentials = await AppleAuthentication.refreshAsync({ requestedScopes: [AppleAuthenticationScope.FULL_NAME, AppleAuthenticationScope.EMAIL], user: (await this.getUserIdentifier())!, state: 'this-is-a-test', }); this.setState({ credentials }); await this.checkCredentials(); } catch (error) { alert(error); } }; signOut = async () => { try { await AppleAuthentication.signOutAsync({ user: (await this.getUserIdentifier())!, state: 'this-is-a-test', }); this.setState({ credentials: null, credentialState: null }); } catch (error) { alert(error); } }; async checkCredentials() { try { const user = (await this.getUserIdentifier())!; const credentialState = await AppleAuthentication.getCredentialStateAsync(user); this.setState({ credentialState }); } catch (e) { // Obtaining a user or the credentials failed - fallback to not found. this.setState({ credentialState: AppleAuthenticationCredentialState.NOT_FOUND }); } } async getUserIdentifier(): Promise { return ( this.state.credentials?.user ?? (await AsyncStorage.getItem(USER_CREDENTIAL_KEY)) ?? null ); } isAuthorized(): boolean { return this.state.credentialState === AppleAuthenticationCredentialState.AUTHORIZED; } render() { if (this.state.isAvailable === undefined) { return ( Checking availability ... ); } if (!this.state.isAvailable) { return ( SignIn with Apple is not available ); } return ( {this.state.credentialState && ( {CREDENTIAL_MESSAGES[this.state.credentialState]} )} Button Style: