Laravel

Laravel + nuxt 소셜로그인 #sanctum #social #로그인 #소셜 #vue #nuxt #api

짱구를왜말려? 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