1. Expo 개발환경 준비하기
Expo 공식 문서에 따라 React Native(Expo) 개발 환경설정을 진행해주세요.
이미 React Native(Expo) 개발환경이 설정되신 분은 다음 단계로 넘어가셔도 좋습니다.
Git 저장소 다운로드
Sample 프로젝트를 다운받아 사용하고 싶으신 분은 다음 명령어를 통해 git 저장소를 다운로드 받으세요.
Furo Expo Sample 프로젝트를 다운로드 받습니다.
git clone https://github.com/furo-official/furo-sample-expo
2. 라이브러리 설치 및 사용법
다음 명령어를 입력해 필요한 라이브러리를 설치합니다.
이번 가이드에서는 다음의 라이브러리를 추가적으로 사용합니다.
-
@react-navigation/native, @react-navigation/native-stack
로그인 이후 deeplink를 처리하기 위해 사용됩니다.
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
const Stack = createNativeStackNavigator();
<NavigationContainer linking={linking}>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
</Stack.Navigator>
</NavigationContainer>;
-
react-native-inappbrowser-reborn
InApp 브라우저를 통해 Furo 로그인 페이지를 열기 위해 사용됩니다.
import { InAppBrowser } from 'react-native-inappbrowser-reborn';
const response = await InAppBrowser.openAuth(url, 'exp://', options);
-
axios
Furo의 인증 API를 호출하기 위해 사용됩니다.
import axios from 'axios';
const { data } = await axios.post(
'https://api.furo.one/sessions/code/authenticate',
{ code },
);
3. Furo 로그인 연동하기
- Furo 콘솔에서 복사한 Client ID 값을 .env 파일에 추가합니다. 만약 .env 파일이 없다면 생성한 후 아래와 같이 환경변수를 설정합니다.
EXPO_FURO_CLIENT_ID="{{YOUR_CLIENT_ID}}"
- Furo 콘솔 입력창의 Default Callback URI에
exp://
을 입력합니다.
-
로그인 후 사용자를 애플리케이션 내의 특정 페이지로 이동시킬 때 사용하는 속성입니다.
-
redirectUri는 애플리케이션 내에서 직접, Default Callback URI는 Furo 콘솔에서 각각 지정할 수 있습니다.
-
앱 내에서 지정한 redirectUri는 통신 중에 악의적으로 조작될 위험이 있기 때문에, Furo 콘솔에서 Default Callback URI에 등록된 URI에 한해서만 redirectUri가 정상 동작합니다.
4. 전체 Sample code
import React, { useCallback, useEffect, useState } from 'react';
import { StatusBar } from 'expo-status-bar';
import { StyleSheet, Text, View, Button, Linking } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { InAppBrowser } from 'react-native-inappbrowser-reborn';
import axios from 'axios';
const CLIENT_ID = process.env.EXPO_FURO_CLIENT_ID;
const LOGIN_URL = `https://auth.furo.one/login/${CLIENT_ID}`;
const Stack = createNativeStackNavigator();
export default function App() {
const linking = {
prefixes: ['exp://'],
config: {
screens: {
Home: '*',
},
},
};
return (
<NavigationContainer linking={linking}>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
const HomeScreen = () => {
const [accessToken, setAccessToken] = useState(null);
const openURLFromInAppBrowserOrLinking = useCallback(async (url) => {
try {
if (await InAppBrowser.isAvailable()) {
const response = await InAppBrowser.openAuth(url, 'exp://', {
// iOS Properties
ephemeralWebSession: false,
// Android Properties
showTitle: false,
enableUrlBarHiding: true,
enableDefaultShare: false,
showInRecents: true,
forceCloseOnRedirection: true,
});
if (response.type === 'success' && response.url) {
console.log(response);
Linking.openURL(response.url);
}
} else {
Linking.openURL(url);
}
} catch (error) {
console.error(error);
}
}, []);
useEffect(() => {
const handleDeepLink = ({ url }) => {
const code = url.split('?')[1]?.match(/code=([^&]+)/)?.[1];
if (code) {
authenticateWithCode(code)
.then((response) => {
const { access_token } = response;
setAccessToken(access_token);
})
.catch((e) => {
console.log('error: ', e.response.data);
});
}
};
Linking.addEventListener('url', handleDeepLink);
return () => {
Linking.removeAllListeners('url');
};
}, []);
const authenticateWithCode = async (code) => {
const { data } = await axios.post(
'https://api.furo.one/sessions/code/authenticate',
{ code },
);
return data;
};
return (
<View style={styles.container}>
<Text style={styles.title}>[Furo] React Native(Expo) Example</Text>
<View style={styles.content}>
{accessToken ? (
<>
<Text>{accessToken}</Text>
<Button title="Clear" onPress={() => setAccessToken(null)} />
</>
) : (
<Button
title="Login"
onPress={() => openURLFromInAppBrowserOrLinking(LOGIN_URL)}
/>
)}
</View>
<StatusBar style="auto" />
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
title: {
fontSize: 16,
fontWeight: '600',
},
content: {
marginTop: 10,
width: '80%',
alignItems: 'center',
},
});
이제 앱에서 Furo 로그인 연동이 완료되었습니다.
앱을 실행하고 Login
버튼을 클릭하면 Furo 로그인 페이지가 InApp 브라우저로 열리고, 로그인이 완료되면 accessToken이 화면에 표시됩니다.