OAuth 2.0 & Social Login - ParkEunwoo/seoul-smart-app GitHub Wiki
React native expoλ‘ κ°λ°μ μ§ννλ©΄μ, μμ λ‘κ·ΈμΈμ ꡬννλλ° μμμΌ ν κ°λ λ€μ λ¨Όμ μμ.
- API(or μλΉμ€) μ΄μ©μ μΈμ¦, 리μμ€(μ 곡 μλΉμ€)μ λν κΆνλΆμ¬λ°©λ² μ€ νλ.
- λΉμ νμλ©΄, λ‘κ·ΈμΈκ³Ό OAuthλ λΆλ¦¬ν΄μ μ΄ν΄ν΄μΌνλ€! λ°©λ¬Έμ¦κ³Ό μ¬μμ¦μ μ°¨μ΄..
- μλ²-ν΄λΌμ΄μΈνΈ μ¬μ΄μ μΈμ¦ μλ£ μ κΆνλΆμ¬μ κ²°κ³Όλ‘ μ‘μΈμ€ ν ν°(Access Token)μ λ°κΈ -> ν΄λΌμ΄μΈνΈ: μ‘μΈμ€ ν ν°μ μ΄μ©ν΄ API(or μλΉμ€)μ μ κ·Ό/μλΉμ€ μμ² -> μλ²: μ‘μΈμ€ ν ν° κΈ°λ°μΌλ‘ μλΉμ€/μ κ·Όμ¬λΆλ₯Ό νλ¨ν΄ λ°μ΄ν° μ 곡
- ꡬκΈ, μΉ΄μΉ΄μ€, νμ΄μ€λΆμμ OAuth 2.0μ ν΅ν΄ μλΉμ€μλν μΈμ¦/κΆνλΆμ¬λ₯Ό μ¬μ©νκ³ μμ
[ν΄λΌμ΄μΈνΈ-μλ² μ΅μ΄ μΈμ¦μ]
- ν΄λΌμ΄μΈνΈ: μ΄λ©μΌ, λΉλ°λ²νΈλ₯Ό 리νμ€νΈ λ°λμ λ΄μ μλ²λ‘ μΈμ¦ μμ²
- μλ²: νμΈνμ¬ μΈμ¦λ ν΄λΌμ΄μΈνΈ μ 보λ₯Ό μΈμ μ μ μ₯β ν¨μ€ν¬νΈκ° νλ μΌ [ν΄λΌμ΄μΈνΈ-μλ² nλ²μ§Έ(>1) μΈμ¦ μ]
- ν΄λΌμ΄μΈνΈ(μ΅μ΄ μΈμ¦ λ΄λ ₯o): μλ²μμ λ°μ μΈμ μμ΄λλ₯Ό μΏ ν€μ μ μ₯ ν μΈμ¦μ΄ νμν API νΈμΆ μ μΈμ μμ΄λ μ 보λ₯Ό ν¨κ» λ΄μ μμ²
- μλ² : μΏ ν€μ μΈμ¦νλ λ΄λ ₯μ λ³΄κ³ api μλ΅μ 보λ΄μ€
- μΈμ
μ μΈμ¦ μ 보λ₯Ό μ μ₯ ν νμκ° μμ
- μ‘μΈμ€ ν ν°μ μΈμ¦μ λ³΄κ° μκΈ° λλ¬Έ!
- μλ²μμλ λμ½λ©νμ¬ νμΈ κ°λ₯
- μΈμ¦ ν νλν μ‘μΈμ€ ν ν°μ ν€λμ λ£μ΄ νΈμΆ
- κ°λ° μ€ μλ²κ° μ¬κ΅¬λ λ λ λ‘κ·ΈμΈ νλ‘ν μ½μ νΈμΆνμ§ μμλ λλ€.
OAuth 2.0 κΈ°λ°μ μ¬μ©μ μΈμ¦ κΈ°λ₯μ ν΅νμ¬ λ€μ΄λ²/μΉ΄μΉ΄μ€κ° μλ λ€λ₯ΈμλΉμ€μμ λ€μ΄λ²μ μ¬μ©μ μΈμ¦ κΈ°λ₯μ μ΄μ©ν μ μκ² νλ μλΉμ€
- μ ν리μΌμ΄μ λ±λ‘ λ€μ΄λ²μμ΄λλ‘ λ‘κ·ΈμΈ μ μ©μ μν΄ μ ν리μΌμ΄μ λ±λ‘, ν΄λΌμ΄μΈνΈ μμ΄λ/μν¬λ¦Ώ ν€ λ°κΈ
- μ ν리μΌμ΄μ κ°λ°
- μλΉμ€μ OAuth μ μ©
OAuthμ κ°λ μ μ΄λ κ³ , react native - expo νλ‘μ νΈμ μ€μ§μ μΌλ‘ μ¬μ©νκΈ° μν λ΄μ©/μ½λλ€μ μ§κ³ λμ΄κ°λ€. κ·Έλ¬λ©΄μ AuthSession, Axios μ κ΄ν λ΄μ©μ κ°μ΄ μ΄ν΄νλλ‘ νμ.
- HTTP ν΄λΌμ΄μΈνΈ λΌμ΄λΈλ¬λ¦¬
- λΉλκΈ° λ°©μμΌλ‘ HTTP λ°μ΄ν° μμ²μ μ€ν
- λ΄λΆμ μΌλ‘ axiosλ μ§μ μ μΌλ‘ XMLHttpRequestλ₯Ό λ€λ£¨μ§ μκ³ βAJAX νΈμΆ(μλ°μ€ν¬λ¦½νΈλ‘ μλ²μ λ°μ΄ν°λ₯Ό μμ²)βμ΄ κ°λ₯νλ€. => μ½κ²λ§ν΄, axiosλ₯Ό ν΅ν΄ HTTPμμ²(μΉμλ²-ν΄λΌμ΄μΈνΈ κ° ν΅μ κ·μ½μΌλ‘, λ¬Έμ κ΅νμ μν΄ μμ²/μλ΅νλ κ²)μΌλ‘ λ°μ΄ν°λ₯Ό κ°μ Έμ¬ μ μλ€!
npm install axios --save;
$ yarn add axios
- import
import axuis from "axios";
- .GET
- λ€μ΄λ² oauth2.0μ ν΅ν΄ access_tokenμ getν κ°μ μ μ₯νλ€.
const {
data: { access_token }
} = await axios.get(`https://nid.naver.com/oauth2.0/token?grant_type=authorization_code&client_id=${NV_APP_ID}
&client_secret=${NV_APP_SECRET}
&code=${code}&state=${STATE_STRING}`);
setToken(data.access_token); //setState
- bearer ν ν°μ μ£Όκ³ λ κ²μ μΈμ¦μΌλ‘ νλ€. : Bearer ν ν°μ΄λ, μνΈννμ§ μμ κ·Έλ₯! ν ν°μΌλ‘ κΈ°λ³Έμ μΌλ‘ HTTPS λ₯Ό μ¬μ©νκΈ° λλ¬Έμ ν ν°μ μμ νκ² μ£Όκ³ λ°λ κ²μ HTTPSμ μνΈνμ μμ‘΄νλ€. APIλ₯Ό νΈμΆ ν λ κ°λ¨νκ² Header μ λ£μ΄ 보λ΄λ κ²μΌλ‘, api ν μ€νΈλ κ°λ₯ν΄λ€!
const config = {
headers: {
Authorization: `Bearer ${access_token}`
}
};
λ°μμ¨ λ€μ΄λ² apiμ headerλ₯Ό λ΄μ λ³΄λ΄ λ€μνλ² getνμ¬ dataλ₯Ό μ»λλ€.
const { data } = await axios.get(
"https://openapi.naver.com/v1/nid/me",
config
);
console.log(data);
setUser(data);
}
: webbrowser μμ ꡬμΆλ μΉ λΈλΌμ°μ κΈ°λ° OAuth flow(μΉλΈλΌμ°μ κΈ°λ° μΈμ¦) μ μ±μ μΆκ°νλ λ°©λ²!
- μ±μ΄ λ€μν URLμ μμ μ μκΈ°λλ¬Έμ, expoμμ νΉνλ μ μ©νλ€!
- μμ : user κ° login λ²νΌ λλ¦
- μΉλΈλΌμ°μ μ΄κΈ°: μ±μ΄ μΉλΈλΌμ°μ λ₯Ό μ΄κ³ , λ‘κ·ΈμΈ νμ΄μ§λ₯Ό μν openURLμλ μΌλ°μ μΌλ‘ μ±μ μλ³νκΈ°μν μ 보 + μ±κ³΅μ redirection(리λλ μ ) ν URLμ΄ ν¬ν¨λ¨. cf. μΉλΈλΌμ°μ λ μΏ ν€μ κΈ°μΈμ¦μ¬λΆ λ±μ λ΄κ³ μμ§μ΄κΈ°λλ¬Έμ μ΅μ΄ λ‘κ·ΈμΈ νμλ, λ€μ λ‘κ·ΈμΈ ν νμκ° μλ€!
- μΈμ¦ 곡κΈμκ° λ¦¬λλ μ : μ±κ³΅μ μΈ μΈμ¦ μ μΈμ¦ 곡κΈμ νμ΄μ§μ URLμ΄ μ 곡 νμ©λ 리λλ μ URLμ νμ© λͺ©λ‘μ μ‘΄μ¬νκ³ , 리λλ μ μλ μμΉ ν΄μ, 쿼리 맀κ°λ³μ λλ λλ€μ URL(ex. UserID, token) λ±μ λ°μ΄ν°κ° ν¬ν¨λμ΄μμ
- μ±μ΄ 리λλ μ μ²λ¦¬, 리λλμ URLμμ λ°μ΄ν°λ₯Ό ꡬ문λΆμ
- μ¬μ©μ μΈμ¦
https://auth.expo.io/@your-user-name/your-app-slug
- μΈμ¦ λ°©μ
- μΈμ¦ μ±κ³΅ μ μΈμ¦ 곡κΈμκ° ν΄λΉ Expo Auth URLλ‘ λ¦¬λλ μ
- Expo Auth μλΉμ€κ° λ€μ μμ©νλ‘κ·Έλ¨μΌλ‘ 리λλ μ
- μ΄λ, μΈμ¦μλΉμ€κ° λ€μ 리λλ μ νλ URLμ΄ μ±/λ 립μ€νν μ± μ²΄κ³μΈ
https://auth.expo.io/@your-user-name/your-app-slug
λλ
yourscheme:// ~~~~
μ κ²μλ URLκ³Ό μΌμΉνμ§ μμΌλ©΄ ->κ²½κ³ νμ΄μ§!
- expoμμ μλΉμ€ url
https://auth.expo.io/@your-username/your-app-slug/start
μ μλΉμ€ urlλ‘ νμ¬, authsesi 5. API
-
AuthSession.startAsync(options)
: AuthSession
*let*result=awaitAuthSession.startAsync({
authUrl:`https://nid.naver.com/oauth2.0/authorize?response_type=code&client_id=${NV_APP_ID}&redirect_uri=${encodeURIComponent(redirectUrl)}&state=${STATE_STRING}`,
});
-
AuthSession.getRedirecrUrl()
: μΈμ¦ 곡κΈμκ° λ¦¬λλ μ ν΄μΌνλ urlμ κ°μ Έμ΄encodeURIComponent()
: gettRedirectUrl ν΄ κ°μ Έμ¨ urlμ μΈμ½λ©
*let*redirectUrl=AuthSession.getRedirectUrl();
-
AuthSession.startAsync()
: μΈμ¦ μΈμ μ μμνλ λ©μλ. : μΈμ¦ 곡κΈμλ‘λΆν° μ λ¬λ μ 보 (ex. userID)λ₯Ό μ¬μ©νμ¬ κ°μ²΄λ‘ νμΈλλ κ²μ λ°ν!
const result = await AuthSession.startAsync({ authUrl: `https://nid.naver.com/oauth2.0/authorize?response_type=code&client_id=${NV_APP_ID}&redirect_uri=${encodeURIComponent( redirectUrl )}&state=${STATE_STRING}` });
this.setState({ result });
μ μ½λμμμ url/νμμ μΈμ¦ μλΉμ€μ λ°λΌ λ€λ₯΄λ€. μλ λ€μλ‘μ μΈμ¦ μλΉμ€ μμλ€.
리λλ μ μ νμλ‘ νλ AuthSessionμ μ¬μ©νλ €λ©΄ expo loginμ κΌ! ν΄μ£Όμ΄μΌ νλ€.
bangsoyun-ui-MacBook-Pro:login-example pongso$ expo login
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β
β There is a new version of expo-cli available (3.0.10). β
β You are currently using expo-cli 2.18.0 β
β Run `npm install -g expo-cli` to get the latest version β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
? Username/Email Address: pongsoyun
? Password: [hidden]
Success. You are now logged in as pongsoyun.
bangsoyun-ui-MacBook-Pro:login-example pongso$ exp start
We've built a brand new CLI for Expo!
Expo CLI is a drop in replacement for exp.
Install: npm install -g expo-cli
Use: expo --help
Read more: https://blog.expo.io/expo-cli-2-0-released-a7a9c250e99c
[02:09:26] Using project at /Users/pongso/loginExample/login-example
[02:09:30] Starting Metro Bundler on port 19001.
[02:09:32] Tunnel ready.
[02:09:32] Expo is ready.
- λ€μ΄λ² κ°λ°μ νμκ°μ -> λ‘κ·ΈμΈ -> λ€μλ‘(λ€μ΄λ² μμ΄λλ‘ λ‘κ·ΈμΈ)/μ ν리μΌμ΄μ λ±λ‘
- api μ€μ > λ‘κ·ΈμΈ μ€ν API μλΉμ€ νκ²½ > νκ²½ μΆκ°(Mobile web)
- 리μ‘νΈ λ€μ΄ν°λΈ μ± κ°λ° μ, and/ios λ‘ λΉλνκ³ μ± κ°λ° μμλ android/ios ν΄λ λ΄λΆλ‘ λ€μ΄κ° λ°λ‘ μμ μ ν΄μ£Όμ΄λ λμ§λ§, νμ¬ μ°λ¦¬λ expoλ₯Ό μ΄μ©νμ¬ κ°λ°μ μ§ννκ³ μκΈ° λλ¬Έμ μ±λ΄μμ μΈλΆ μΉμ μ€νν κ²μ΄λ€.
[μλΉμ€ URL]
https://auth.expo.io/@pongsoyun/login-example/start
[CallBack URL]
https://auth.expo.io/@pongsoyun/login-example
μ κΈ°μ¬ν΄ μ€λ€.
3. yarn start
λ₯Ό ν΅ν΄ expo μ€ν
4. μ½λ
λ€μλ‘ κ΅¬νμ μν΄, μμ κΈ°μ¬ν κ°λ
λ€μ μ μ©ν μ½λμ΄λ€.
import React, { useState } from "react";
import { StyleSheet, Text, View, TouchableOpacity } from "react-native";
import { AuthSession } from "expo";
import axios from "axios";
//
const NV_APP_ID = "YOUR_CLIENT_ID";
const NV_APP_SECRET = "YOUR_CLIENT_PW";
const STATE_STRING = "YOUR_SECRET_STRING";
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
alignItems: "center",
justifyContent: "center"
}
});
export default function App() {
//useState(), token/code/user
const [token, setToken] = useState();
const [code, setCode] = useState();
const [user, setUser] = useState();
//authsession
async function handlePressAsync() {
let redirectUrl = AuthSession.getRedirectUrl();
console.log(redirectUrl);
console.log(encodeURIComponent(redirectUrl));
const result = await AuthSession.startAsync({
authUrl: `https://nid.naver.com/oauth2.0/authorize?response_type=code&client_id=${NV_APP_ID}&redirect_uri=${encodeURIComponent(
redirectUrl
)}&state=${STATE_STRING}`
});
console.log("result", result);
setCode(result.code);
handleGetAccess();
}
//http data request
async function handleGetAccess() {
const {
data: { access_token }
} = await axios.get(`https://nid.naver.com/oauth2.0/token?grant_type=authorization_code&client_id=${NV_APP_ID}
&client_secret=${NV_APP_SECRET}
&code=${code}&state=${STATE_STRING}`);
const config = {
headers: {
Authorization: `Bearer ${access_token}`
}
};
setToken(data.access_token);
const { data } = await axios.get(
"https://openapi.naver.com/v1/nid/me",
config
);
console.log(data);
setUser(data);
}
//TEXT press -> handlePressAsync()
return (
<View style={styles.container}>
<TouchableOpacity onPress={handlePressAsync}>
<Text>λ€μ΄λ² μμ΄λλ‘ μμνκΈ°</Text>
</TouchableOpacity>
</View>
);
}
- CALL BACK μλ΅μ 보
API μμ² μ±κ³΅μ : http://μ½λ°±URL/redirect?code={codeκ°}&state={stateκ°}
API μμ² μ€ν¨μ : http://μ½λ°±URL/redirect?state={stateκ°}&error={μλ¬μ½λκ°}&error_description={μλ¬λ©μμ§}