ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Laravel + nuxt 소셜로그인 #sanctum #social #로그인 #소셜 #vue #nuxt #api
    Laravel 2023. 3. 23. 22:50
    반응형
    SMALL

     # What?

    - API 방식으로 소셜로그인 구현하는 방법

     

     # How?

    - 기본원리

    -> 클라이언트 서버쪽에서 API 서버쪽으로 redirect

    -> API 서버에서 소셜로그인 후 callback 받기

    -> callback 받은 후 로그인 or 가입처리 후 토큰생성 후 클라이언트쪽에 토큰값 넘겨주기

    -> 클라이언트쪽에서 socialLogin페이지 따로 만들어놓고 token params받아 로그인 시도하기

    -> API쪽에서 login 메소드에 token으로 로그인 요청 있는지를 확인하여 유저정보 및 token값 리턴하기

    -> 클라이언트쪽에서 해당 정보로 로그인처리 완료하기

     

    1. 백엔드 세팅

    - 백엔드 sanctum 세팅 (sanctum글 참고)

    - .env 세팅 -> 프론트 서버 url 및 소셜키값 세팅

    @ .env

    APP_CLIENT_URL=http://localhost:3000

    @ app.php

    'client_url' => env('APP_CLIENT_URL', 'https://whatpick.com'),

     

    - 컨트롤러 세팅

    @ Api/UserController - login과, socialLogin 메소드가 핵심

        public function login(Request $request)
        {
            // 소셜로그인 시도 시
            if($request->token && auth()->user()){
                return $this->respondSuccessfully([
                    "token" => $request->token,
                    "user" => UserResource::make(auth()->user())
                ]);
            }
    
            $data = $request->validate([
                "ids" => "required|string|max:500",
                "password" => "required|string|max:500",
            ]);
    
            if(auth()->attempt($request->only("ids", "password"))) {
                session()->regenerate();
    
                $token = auth()->user()->createToken("auth");
    
                return $this->respondSuccessfully([
                    "token" => $token->plainTextToken,
                    "user" => UserResource::make(auth()->user())
                ]);
            }
    
            return throw ValidationException::withMessages([
                "ids" => [
                    __("socialLogin.invalid")
                ]
            ]);
        }
    
        public function openSocialLoginPop($social)
        {
            return Socialite::driver($social)->stateless()->redirect();
        }
    
        public function socialLogin(Request $request, $social)
        {
            $socialUser = Socialite::driver($social)->stateless()->user();
    
            // 일단 네이버
            $user = User::where("social_id", $socialUser->id)->where("social_platform", $social)->first();
    
            if(!$user) {
                $user = User::create([
                    "name" => $social,
                    "social_id" => $socialUser->id,
                    "social_platform" => $social
                ]);
            }
    
            $token = $user->createToken("auth")->plainTextToken;
    
            Auth::login($user);
    
            return redirect(config("app.client_url")."/socialLogin?token=".$token);
        }

     

    - 프론트 세팅

    @ socialLogin.vue

    <template>
    
    </template>
    <script>
    export default {
        data(){
            return {
                token: this.$route.query.token ?  this.$route.query.token : null,
            }
        },
    
        mounted() {
             this.$auth.loginWith('laravelSanctum', {
                    data: {
                    	token:this.token
                    }
                }).then(response => {
                this.$router.back();
            }).catch((e) => {
                alert("소셜로그인에 실패하였습니다.");
    
                return this.$router.push("/");
            })
        }
    }
    </script>

    @ login.vue

    <template>
        <main class="login">
            <div class="container col-group">
                <div class="left-wrap row-group">
                    <div class="login-wrap">
                        <form @submit.prevent="login" @keydown="() => {form.errors.clear()}">
                            <h2 class="login-title scd6">
                                <span class="scd6">왓픽</span> 회원 로그인
                            </h2>
                            <div class="login-form row-group">
                                <input type="text" placeholder="아이디를 입력해 주세요." v-model="form.ids">
                                <p class="m-input-error" v-if="form.errors.has('ids')">{{form.errors.get("ids")}}</p>
    
                                <input type="password" placeholder="비밀번호를 입력해 주세요." v-model="form.password">
                                <p class="m-input-error" v-if="form.errors.has('password')">{{form.errors.get("password")}}</p>
    
                                <button type="submit" class="login-submit scd5">로그인</button>
                                <div class="col-group">
                                    <label for="auto_login">
                                        <input type="checkbox" id="auto_login">
                                        <span class="check-icon"></span>
                                        자동로그인
                                    </label>
                                    <div class="find-account col-group">
                                        <a href="find_id1.html">아이디 찾기</a>
                                        <a href="find_pw1.html">비밀번호 찾기</a>
                                    </div>
                                </div>
                            </div>
                            <div class="sns-login-wrap col-group">
                                <p class="title">
                                    <strong>SNS</strong> 계정 로그인
                                </p>
                                <ul class="sns-login-list col-group">
                                    <li>
                                        <a :href="`${$store.state.domain}/openLoginPop/naver`">
                                            <img src="/images/sns_login_naver.png" alt="">
                                        </a>
                                    </li>
                                    <li>
                                        <a :href="`${$store.state.domain}/openLoginPop/kakao`">
                                            <img src="/images/sns_login_kakao.png" alt="">
                                        </a>
                                    </li>
                                    <li>
                                        <a :href="`${$store.state.domain}/openLoginPop/google`">
                                            <img src="/images/sns_login_facebook.png" alt="">
                                        </a>
                                    </li>
                                    <li>
                                        <a :href="`${$store.state.domain}/openLoginPop/facebook`">
                                            <img src="/images/sns_login_google.png" alt="">
                                        </a>
                                    </li>
                                </ul>
                            </div>
                        </form>
                    </div>
                    <a href="join.html" class="join-wrap col-group">
                        <div class="txt-box">
                            <h3 class="join-title scd5">아직 회원이 아니신가요?</h3>
                            <p class="title">
                                <span class="red scd5">회원가입</span> 후 다양한 서비스를 만나보세요
                            </p>
                        </div>
                        <i class="xi-arrow-right"></i>
                    </a>
                </div>
                <div class="right-wrap">
                    <div class="txt-box">
                            <span class="notice">
                                <i class="xi-info"></i>
                            </span>
                        <p class="title">
                            왓픽은 중고차 <span class="scd5">구매 도우미</span> 입니다.
                        </p>
                        <p class="txt">
                            왓픽은 중고차를 판매하지 않습니다. 왓픽은 중고차 매물의 진짜 정보를 제공합니다.
                        </p>
                    </div>
                    <img src="/images/login_bg.png" alt="">
                </div>
            </div>
    
        </main>
    </template>
    
    <script>
    import Form from "../utils/Form";
    export default {
        name: 'Login',
        data(){
            return {
                form : new Form(this.$axios, {
                    ids:"",
                    password:""
                })
            }
        },
        methods: {
            login(){
                this.$auth.loginWith('laravelSanctum', {
                    data: this.form.data()
                });
    
                /*this.$axios.get('/sanctum/csrf-cookie').then(response => {
                    this.form.post("/api/login")
                        .then(response => {
                            this.$axios.defaults.headers.common["Authorization"] = `Bearer ${response.data.token}`;
    
                            this.$store.commit("setToken", response.data.token);
                            this.$store.commit("setUser", response.data.user);
    
                            console.log(this.$store.state.user);
                        })
                        .catch(error => {
    
                        })
                });*/
            }
        }
    }
    </script>

    * 실 배포 후 필수세팅

    SESSION_DOMAIN=.whatpick.com // 본 도메인
    SANCTUM_STATEFUL_DOMAINS=whatpick.com // 클라이언트 도메인
    LIST

    댓글

Designed by Tistory.