programing

vue.js 및 Auth0을 사용하여 사용자가 이미 인증되어 있는 경우 로그인 페이지를 건너뛰려면 어떻게 해야 합니까?

copyandpastes 2022. 7. 8. 00:02
반응형

vue.js 및 Auth0을 사용하여 사용자가 이미 인증되어 있는 경우 로그인 페이지를 건너뛰려면 어떻게 해야 합니까?

묘사

나는 꽤 좋은 기준을 가지고 있다.SPA로 건설된.vue.js사용하고 있는 장소Auth0공식 예에 따라 인증 부분을 처리합니다.앱 흐름은 다음과 같습니다.

Register in the Initial.vue via Auth0 lock->Callback is called->User's redirected to /home

위의 흐름은 모두 정상적으로 동작하고 있습니다만, 여기에 문제가 있습니다.

문제

사용자가 등록되면/home다른 모든 경로(예: 다른 경로)에 액세스할 수 있도록 합니다./doctors인증을 받은 경우, 인증을 받지 않은 경우 다시 로그인하도록 프롬프트가 표시됩니다.위의 링크에 따라 이것은router.beforeEach기능.

액세스 하면, 문제가 표시됩니다./로그인 페이지(Initialvue사용자가 이미 등록되어 있고 해당 루트에 접속하려고 할 때 사용자가 해당 루트로 리다이렉트되도록 합니다./home를 생략합니다.loginpage. 이 기능을 구현하려고 합니다.beforeEnter하지만auth.isAuthenticated에 의해 실패하다tokenExpirynull입니다(사용자가 인증되어 있는 경우에도 마찬가지입니다.

코드

나의AuthService.js:

import auth0 from 'auth0-js';
import EventEmitter from 'events';
import authConfig from '../config/auth_config.json';

const webAuth = new auth0.WebAuth({
  domain: authConfig.domain,
  redirectUri: `${window.location.origin}/callback`,
  clientID: authConfig.clientId,
  responseType: 'id_token',
  scope: 'openid profile email'
});

const localStorageKey = 'loggedIn';
const loginEvent = 'loginEvent';

class AuthService extends EventEmitter {
  idToken = null;
  profile = null;
  tokenExpiry = null;

  // Starts the user login flow
  login(customState) {
    webAuth.authorize({
      appState: customState
    });
  }

  // Handles the callback request from Auth0
  handleAuthentication() {
    return new Promise((resolve, reject) => {
      webAuth.parseHash((err, authResult) => {
        if (err) {
          reject(err);
        } else {
          this.localLogin(authResult);
          resolve(authResult.idToken);
        }
      });
    });
  }

  localLogin(authResult) {
    // console.log(authResult); TODO-me: Handle this
    this.idToken = authResult.idToken;
    this.profile = authResult.idTokenPayload;

    // Convert the JWT expiry time from seconds to milliseconds
    this.tokenExpiry = new Date(this.profile.exp * 1000);

    localStorage.setItem(localStorageKey, 'true');

    this.emit(loginEvent, {
      loggedIn: true,
      profile: authResult.idTokenPayload,
      state: authResult.appState || {}
    });
  }

  renewTokens() {
    return new Promise((resolve, reject) => {
      if (localStorage.getItem(localStorageKey) !== "true") {
        return reject("Not logged in");
      }``;

      webAuth.checkSession({}, (err, authResult) => {
        if (err) {
          reject(err);
        } else {
          this.localLogin(authResult);
          resolve(authResult);
        }
      });
    });
  }

  logOut() {
    localStorage.removeItem(localStorageKey);

    this.idToken = null;
    this.tokenExpiry = null;
    this.profile = null;

    webAuth.logout({
      returnTo: window.location.origin
    });

    this.emit(loginEvent, { loggedIn: false });
  }

  isAuthenticated() {
     console.log('In tokenExp is:');
     console.log(this.tokenExpiry); //THIS returns null when /home -> /
    return (
      Date.now() < this.tokenExpiry &&
      localStorage.getItem(localStorageKey) === 'true'
    );
  }
}

export default new AuthService();

나의Initial.vue:

<template>
    <v-container
        app
        fluid
    >
        <v-parallax
            src="https://cdn.vuetifyjs.com/images/backgrounds/vbanner.jpg"
            height="1000"
        >
            <v-layout
                row
                wrap
            >
                <!-- LOGIN-->

                    <v-toolbar
                        flat
                        light
                        dense
                        color="transparent"
                    >
                        <v-spacer></v-spacer>
                        <v-toolbar-items>
                            <v-btn
                                medium
                                color="lime lighten-2"
                                @click="login"
                                class="font-weight-bold title text-uppercase"
                            >
                                Login
                            </v-btn>
                        </v-toolbar-items>
                    </v-toolbar>
            <v-layout
                align-center
                column
            >
                <h1 class="display-2 font-weight-thin mb-3 text-uppercase lime--text lighten-2" >Pulse</h1>
                <h4 class="subheading">A digital intelligent insurance built for you!</h4>
            </v-layout>

            </v-layout>
        </v-parallax>
    </v-container>
</template>

<script>
    import VContainer from "vuetify/lib/components/VGrid/VContainer";
    import VFlex from "vuetify/lib/components/VGrid/VFlex";
    import VLayout from "vuetify/lib/components/VGrid/VLayout";
    import VBtn from "vuetify/lib/components/VBtn/VBtn";
    import VToolbar from "vuetify/lib/components/VToolbar/VToolbar";
    import VParallax from "vuetify/lib/components/VParallax/VParallax";

    export default {
        name: "Initial",
        components: {
            VContainer,
            VLayout,
            VFlex,
            VBtn,
            VToolbar,
            VParallax
        },
        data() {
            return {
            isAuthenticated: false
            };
        },
        async created() {
            try {
                await this.$auth.renewTokens();
            } catch (e) {
            // console.log(e);
            }
        },
        methods: {
            login() {
                this.$auth.login();
        },
            // logout() {
            //     this.$auth.logOut();
            // },
            handleLoginEvent(data) {
                this.isAuthenticated = data.loggedIn;
                this.profile = data.profile;
            }
        }


    }
</script>

<style scoped>

</style>

나의Callback.vue:

<template>
  <div>
    <p>Loading...</p>
  </div>
</template>

<script>
    export default {
      methods: {
        handleLoginEvent(data) {
            console.log('State.target is:');
            console.log(data.state.target);
          //If user has just signed up redirect to complete-signup form
          if ((data.profile['<AUTH_DOMAIN>'].justSignedUp) && (data.state.target===undefined)){
              // this.$router.push(data.state.target || "/complete-signup");
              this.$router.push('/complete-signup');
              }else {
                  // this.$router.push('/home');
                  this.$router.push(data.state.target);
              }
        }
      },
      created() {
        this.$auth.handleAuthentication();
      }
    }

</script>

<style scoped>

</style>

나의router.js:

import Vue from 'vue';
import Router from 'vue-router';
import auth from '../auth/AuthService';

import Callback from '../components/Callback';

Vue.use(Router)

// export default new Router({
const router = new Router({
    mode: 'history',
    base: process.env.BASE_URL,
    routes: [
        // {
        //     path: '/',
        //     name: 'login',
        //     component: () => import('@/views/Login')
        // },
        {
            path: '/',
            name: 'initial',
            component: () => import('@/views/Initial'),
            // meta: {isAuth: true},
            beforeEnter: ((to, from, next) => {
                // if (to.matched.some(record => record.meta.isAuth)) {
                     console.log(auth.isAuthenticated()); //THIS is false for the above scenario
                    if (auth.isAuthenticated()) {
                        next({
                            path: '/home',
                            query: {redirect: to.fullPath}
                        })
                    } else {
                        next()
                    }
                // }
            })

        },
        {
            path: '/callback',
            name: 'callback',
            component: Callback
        },
        {
            path: '/home',
            name: 'home',
            component: () => import('@/views/Home')
        },
        {
            path: '/doctors',
            name: 'doctors',
            component: () => import('@/views/Doctors')
        },
        {
            path: '/complete-signup',
            name: 'complete-signup',
            component: () => import('@/views/CompleteSignup')
        },

    ]
});

// Add a `beforeEach` handler to each route
router.beforeEach((to, from, next) => {
  if (to.path === "/" || to.path === "/callback" || auth.isAuthenticated()) {
    return next();
  }

  // Specify the current path as the customState parameter, meaning it
  // will be returned to the application after auth
    console.log('OUT beforeach if');
  auth.login({ target: to.path });
});

'Complete Signup(등록 완료)'is my signup form after registering where the user's is filling out a form and then posting it via악세서and then redirected to/home':

//Form data before
methods: {
this.$store.dispatch(REGISTER,registerFormData)
          .then(() => this.$router.push('/home'));
}

저도 쓰고 있어요.vuetify그리고 나의 메인App.vue컴포넌트는 다음과 같습니다.

<template>

    <v-app
        style= "background: #E0EAFC;  /* fallback for old browsers */
        background: -webkit-linear-gradient(to left, #CFDEF3, #E0EAFC);  /* Chrome 10-25, Safari 5.1-6 */
        background: linear-gradient(to left, #CFDEF3, #E0EAFC); /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */
        "
    >  
      <v-content>
        <router-view></router-view>
      </v-content>
    </v-app>
</template> 


<script>   
  export default {
      name: 'App',
      components: {

      }

  };


</script>

<style>
</style>

디폴트 동작을 사용자가 로그인한 동작으로 하고 루트 가드를 사용하여 각 루트를 보호함으로써 문제를 단순화할 수 있습니다.

1) 포인트:/로./home
2) 로그인/"인셜"을 위한 개별 경로 생성
3) 사용방법beforeEach훅을 사용하여 사용자가 인증되었는지 확인하고 사용자가 인증되지 않은 경우 사용자로 리디렉션합니다.Initial.vue(또는 트리거)auth.login()직접)

...
  {
    path: '/',
    redirect: 'home'
  },
  {
    path: '/initial',
    ...
  ...
}
...
router.beforeEach((to, from, next) => {
  if(to.name == 'callback' || auth.isAuthenticated()) { 
    next()
  } else { // trigger auth0 login or redirect to your Initial.vue
    auth.login()
    // next({ path: '/initial' })
  }
})

난 이해했다.문제는 이 시스템이this.tokenExpiry,this.idToken,this.profile값이 할당되어 있습니다.callback다음에 오는 그림login또한 이러한 값은 에서 정의한 특정 Vue 프로토타입 인스턴스와 관련되어 있습니다.mixin첫 페이지로 돌아가면this특정 Vue 인스턴스에 연결되지 않았기 때문에 정의되지 않았습니다.

저도 (React와) 같은 상황이었는데, 해결 방법을 설명하겠습니다.

  1. 먼저 인증에서는 토큰을 클라이언트 측에 저장해야 합니다(localStorage/cookie 사용 가능).

  2. 필요한 것은SecureComponent이 컴포넌트는 저장된 토큰이 존재하는지 여부만 확인합니다.

  3. FetchHandler이 컴포넌트는 각각을 처리합니다.Protected Application는 응답 코드를 검사하고 401(예를 들어)인 경우 사용자를 인증 컴포넌트로 리다이렉트합니다.이 스텝은 에 추가할 수 있습니다.SecureComponent추가 레이어로 사용할 수 있습니다.

언급URL : https://stackoverflow.com/questions/56088916/using-vue-js-and-auth0-how-can-i-skip-the-login-page-if-the-users-already-authe

반응형