-
Rust: 카카오 소셜 로그인 하기 (JWT, actix-rs, react.js)컴퓨터/Rust 2023. 7. 10. 18:55728x90반응형
소개: 이 글에서는 React.js 프트엔드에서 간단하게 카카오 로그인을 구현한 예제이다.
프론트만으로 해결하려 했으나 redirect 등의 문제로 백엔드를 Rust로 구현하게 되었다. (React.js + Actix-rs)
왜냐하면 vercel로 배포했기 때문에 프론트만 업로드 되어있다...프론트+백엔드 같이 배포하면 더 쉬워진다.
백엔드는 fly.io와 같은 사이트를 이용해서 퍼블릭 주소로 서비스 해준다.
일단 https://developers.kakao.com/console/app 에서 앱을 만들고 카카오 로그인 기능을 켜준다.
그다음 아래에 REDIRECT URI에 개발 홈페이지 주소를 적어준다.
https://developers.kakao.com/docs/latest/ko/kakaologin/rest-api
React.js
프론트엔드에서는 일단 카카오 REST 키를 이용해서 할 것이다. 다른 방법들도 있으니 그건 인터넷 참고
아바타 버튼을 누르면 handleLoginOpen 함수를 불러서 rest api 키와 리다이렉트 경로를 지정해서 넘어가준다.
일단 넘어가줘야 한다. 그냥 fetch로 하면 OAuth2 방식이므로 잘 안된다.
const handleLoginOpen = () => { const rest_api_key = process.env.REACT_APP_KAKAO_REST_KEY; //REST API KEY const redirect_uri = "http://나의 리다이렉트 경로/oauth"; //Redirect URI // oauth 요청 URL const kakaoURL = `https://kauth.kakao.com/oauth/authorize?client_id=${rest_api_key}&redirect_uri=${redirect_uri}&response_type=code`; window.location.href = kakaoURL; // redirect user to Kakao login page }; ... <Link to="#" onClick={handleLoginOpen}> <Avatar /> </Link>
리다이렉트 경로는 백엔드를 연결했다. (로그인 -> 백엔드에서 -> access_token 얻기 -> 유저 정보 얻기 -> mongodb에 유저 정보 저장하기 -> JWT 토큰 만들어서 프론트 주소로 재경로 -> 프론트에서 oauth?token=... 데이터를 로컬에 저장)
Rust
몽고DB에 연결하고 kauth.kakao.com에 맞는 파라미터를 만들고 요청하고 JWT 토큰을 만드는 그러한 코드이다.
#[get("/oauth")] async fn handle_auth(conn: web::Data<DbPool>, data: web::Query<AuthRequest>) -> impl Responder { let code = &data.code; println!("{}", code); let users = conn .lock() .unwrap() .database("libra") .collection::<User>("users"); // Here, use the code to request an access token from Kakao API let client = reqwest::Client::new(); let params = [ ("grant_type", "authorization_code"), ("client_id", KAKAO_REST_KEY), ("redirect_uri", REDIRECT_URI), ("code", &code), ]; let res = client .post("https://kauth.kakao.com/oauth/token") .header( "Content-Type", "application/x-www-form-urlencoded; charset=UTF-8", ) .form(¶ms) .send() .await .unwrap(); // let text = res.text().await.unwrap(); // println!("Response: {}", text); let kakao_response: KakaoResponse = res.json().await.unwrap(); let access_token = kakao_response.access_token; println!("Access token: {}", access_token); // Here, use the access token to request user information from Kakao API let res = client .get("https://kapi.kakao.com/v2/user/me") .bearer_auth(&access_token) .send() .await .unwrap(); // let text = res.text().await.unwrap(); // println!("Response2: {}", text); let user_info: User = res.json().await.unwrap(); // create a new user document let user_doc = doc! { "$set": { "access_token": &access_token, // add other fields as needed } }; // update the user document in the collection, or insert if it does not exist users .update_one( doc! { "id": &user_info.id, "connected_at": &user_info.connected_at }, user_doc, UpdateOptions::builder().upsert(true).build(), ) .await .unwrap(); // Here, generate a JWT and send it to the front end let my_claims = Claims { sub: user_info.id, // user identifier exp: (SystemTime::now() + Duration::from_secs(kakao_response.refresh_token_expires_in as u64)) // expires .duration_since(SystemTime::UNIX_EPOCH) .unwrap() .as_secs() as usize, }; let key = EncodingKey::from_secret(SECRET_KEY.as_ref()); let token: String = encode(&Header::default(), &my_claims, &key).unwrap(); // HttpResponse::Ok().json(json!({ "token": token })) let redirect_url = format!("http://내 프론트 경로/oauth?token={}", token); // redirect the client to the URL HttpResponse::Found() .append_header(("Location", redirect_url)) .finish() }
React.js
react-router-dom v6+ 기준으로 oauth 링크를 만들어 주었다. (백엔드에서 프론트주소/oauth?token=... 으로 이동하기에)
oauth로 하면 catch all이다, regex 표현으로 저기에 대충 맞으면 다 저기로 넘어간다.
App.js
import { BrowserRouter as Router, Routes, Route } from "react-router-dom"; import Home from "./pages/Home/Home"; import HandleAuth from "./pages/HandleAuth/HandleAuth"; import SearchResult from "./pages/SearchResult/SearchResult"; import "./App.css"; function App() { return ( <div className="app"> <Router> <Routes> {/* Home (the one with the search on) */} <Route path="/" element={<Home />} /> {/* SearchPage (The results page) */} <Route path="search" element={<SearchResult />} /> <Route path="oauth" element={<HandleAuth />} /> </Routes> </Router> </div> ); } export default App;
HandleAuth.js
oauth에서 로컬에 저장하고 홈 (/)으로 이동한다.
import { useEffect } from "react"; import { useNavigate, useLocation } from "react-router-dom"; const HandleAuth = () => { const location = useLocation(); const navigate = useNavigate(); useEffect(() => { // extract the token from the URL const queryParams = new URLSearchParams(location.search); const token = queryParams.get("token"); if (token) { // save the token in local storage localStorage.setItem("jwt", token); console.log(token); // redirect the user to the Home page navigate("/"); } else { console.error("No token in URL"); } }, [location, navigate]); return null; // or you can return a loading spinner or a message while the token is being fetched }; export default HandleAuth;
끝이다.
일단 kauth 페이지들은 CORS가 열려있어서 모든 호출이 가능하고 (credentials이 필요 없다는 이야기)
인가요청은 카카오 측 페이지로 이동하고 리다이렉트 해서 AJAX 방식 (fetch()...)로 하면 안 된다.
(로그인 이후 -> 리다이렉트 URI에서 token 조회는 fetch로 가능)
JS SDK의 Kakao.Auth.login 방식은 팝업을 띄우고 팝업 내 통신으로 별도 토큰 요청 없이, 토큰까지 리턴 받는 방식이 있는데 그건 SDK를 참고한다.
728x90'컴퓨터 > Rust' 카테고리의 다른 글
Rust: OAuth2 구글, Github, 카카오, 네이버 로그인 (1) 2023.08.06 Rust: WASM async fn + 카카오맵 API 사용하기 (0) 2023.06.26 Rust: actix-rs + React.js (0) 2023.06.02