كيفية إضافة المصادقة إلى تطبيق Vue باستخدام Auth0 خطوة بخطوة

دقائق القراءة: 8

مقدمة: لماذا تستخدم Auth0 مع Vue؟

تُعد Auth0 منصة مرنة وجاهزة لإضافة المصادقة والتفويض إلى التطبيقات الحديثة دون الحاجة إلى بناء نظام تسجيل دخول من الصفر. إذا كنت تطور تطبيقًا باستخدام Vue، فيمكنك عبر هذه الخدمة تفعيل تسجيل المستخدمين، وتسجيل الدخول، وتسجيل الخروج، وحماية الصفحات الحساسة بطريقة عملية وسريعة.

في هذا الدليل سنبني تطبيق Vue بسيطًا، ثم نربطه مع Auth0 حتى يتمكن المستخدم من إنشاء حساب أو تسجيل الدخول باستخدام البريد الإلكتروني وكلمة المرور. بعد نجاح المصادقة، سيتمكن فقط المستخدم المسجل من الوصول إلى صفحة About.

واجهة توضيحية لشرح إضافة المصادقة إلى تطبيق Vue باستخدام Auth0

ما الذي سنبنيه في هذا التطبيق؟

الفكرة الأساسية هي إنشاء مشروع جديد عبر Vue CLI، ثم تعديل البنية الافتراضية بحيث يتكامل التطبيق مع Auth0. بعد ذلك سنضيف الوظائف التالية:

  • تسجيل مستخدم جديد عبر البريد الإلكتروني وكلمة المرور.
  • تسجيل دخول مستخدم حالي.
  • تسجيل الخروج من التطبيق.
  • إخفاء أو إظهار عناصر الواجهة حسب حالة المصادقة.
  • حماية صفحة محددة بحيث لا تظهر إلا للمستخدم المصرح له.

إنشاء مشروع Vue جديد

تثبيت Vue CLI

إذا لم يكن Vue CLI مثبتًا على جهازك، يمكنك تثبيته بشكل عام باستخدام الأمر التالي:

npm install -g @vue/cli

إنشاء المشروع

بعد اكتمال التثبيت، أنشئ مشروعًا جديدًا بالأمر التالي:

vue create vue-authentication-auth0

أثناء الإعداد اختر Manually select features، ثم فعّل الخيارات التالية:

  • babel
  • Router
  • Linter / Formatter

وعندما يُسألك عن استخدام وضع السجل history mode في الموجه router، اختر Yes. أما بالنسبة لأداة التنسيق، فيمكنك اختيار Eslint + Prettier كما في هذا الشرح.

بعد انتهاء Vue CLI من إنشاء المشروع، شغّل الخادم المحلي واتبع التعليمات التي تظهر في الطرفية، ثم افتح الرابط localhost:8080 داخل المتصفح.

الواجهة الافتراضية لمشروع Vue بعد إنشائه عبر Vue CLI

إنشاء حساب في Auth0 وإعداد التطبيق

فتح حساب جديد

الخطوة الأولى هي إنشاء حساب مجاني في Auth0 إذا لم يكن لديك حساب مسبقًا. بعد تسجيل الدخول إلى لوحة التحكم، توجه من القائمة الجانبية إلى قسم Applications.

لوحة تحكم Auth0 مع تحديد قسم Applications

إنشاء تطبيق جديد في Auth0

اضغط على زر Create Application، ثم أدخل اسم التطبيق الذي تريده، وحدد نوع التطبيق على أنه Single Page Web Application لأننا نعمل على تطبيق Vue أحادي الصفحة.

زر إنشاء تطبيق جديد داخل منصة Auth0إعداد نوع التطبيق في Auth0 كتطبيق ويب أحادي الصفحة

بعد إنشاء التطبيق، ستجد قسم Quick Start الذي يعرض خطوات الدمج مع أشهر أطر JavaScript. بما أننا نستخدم Vue.js، فاختره من القائمة.

اختيار Vue من قسم Quick Start داخل Auth0

ضبط إعدادات التطبيق داخل Auth0

من تبويب Settings ستجد بيانات مهمة مثل Domain وClient ID. ستحتاج إليهما لاحقًا داخل مشروعك، لذا احتفظ بهما.

ضمن قسم Application URIs، اضبط القيم التالية على الرابط المحلي الخاص بالتطوير:

  • Allowed Callback URLs
  • Allowed Logout URLs
  • Allowed Web Origins

في هذا الشرح سنستخدم الرابط:

http://localhost:8080

إذا قررت لاحقًا نشر التطبيق على خدمة مثل Netlify أو Heroku، فيجب تحديث هذه الروابط لتتوافق مع عنوان النطاق الفعلي للتطبيق.

إعداد روابط Allowed Callback و Allowed Logout و Allowed Web Origins في Auth0

تثبيت حزمة Auth0 SDK

داخل مشروع Vue، ثبّت مكتبة عميل Auth0 بالأمر التالي:

npm install @auth0/auth0-spa-js

هذه الحزمة تتيح لتطبيقك تنفيذ عمليات تسجيل الدخول واستقبال الجلسات وإدارة رموز الوصول بطريقة متوافقة مع تطبيقات SPA.

إنشاء طبقة تغليف للمصادقة داخل Vue

تحتاج مكتبة Auth0 إلى تهيئة قبل تشغيل التطبيق عبر new Vue(). لهذا السبب لا يكفي الاعتماد على دورة الحياة مثل beforeCreate داخل App.vue، لأن هذه المرحلة تأتي بعد بدء التطبيق فعليًا.

الحل الأنسب هنا هو بناء إضافة Vue plugin مخصصة تتولى إنشاء عميل Auth0 وتوفير وظائف المصادقة في جميع مكونات التطبيق.

مخطط يوضح دورة حياة Vue وأهمية تهيئة Auth0 قبل إنشاء التطبيق

إنشاء الملف src/auth/index.js

أنشئ مجلدًا جديدًا باسم auth داخل src، ثم أنشئ الملف index.js بداخله، وأضف الكود التالي:

import Vue from "vue";
import createAuth0Client from "@auth0/auth0-spa-js";

const DEFAULT_REDIRECT_CALLBACK = () =>
  window.history.replaceState({}, document.title, window.location.pathname);

let instance;

export const getInstance = () => instance;

export const useAuth0 = ({
  onRedirectCallback = DEFAULT_REDIRECT_CALLBACK,
  redirectUri = window.location.origin,
  ...options
}) => {
  if (instance) return instance;

  instance = new Vue({
    data() {
      return {
        loading: true,
        isAuthenticated: false,
        user: {},
        auth0Client: null,
        popupOpen: false,
        error: null
      };
    },
    methods: {
      async loginWithPopup(options, config) {
        this.popupOpen = true;
        try {
          await this.auth0Client.loginWithPopup(options, config);
        } catch (e) {
          console.error(e);
        } finally {
          this.popupOpen = false;
        }
        this.user = await this.auth0Client.getUser();
        this.isAuthenticated = true;
      },
      async handleRedirectCallback() {
        this.loading = true;
        try {
          await this.auth0Client.handleRedirectCallback();
          this.user = await this.auth0Client.getUser();
          this.isAuthenticated = true;
        } catch (e) {
          this.error = e;
        } finally {
          this.loading = false;
        }
      },
      loginWithRedirect(o) {
        return this.auth0Client.loginWithRedirect(o);
      },
      getIdTokenClaims(o) {
        return this.auth0Client.getIdTokenClaims(o);
      },
      getTokenSilently(o) {
        return this.auth0Client.getTokenSilently(o);
      },
      getTokenWithPopup(o) {
        return this.auth0Client.getTokenWithPopup(o);
      },
      logout(o) {
        return this.auth0Client.logout(o);
      }
    },
    async created() {
      this.auth0Client = await createAuth0Client({
        ...options,
        client_id: options.clientId,
        redirect_uri: redirectUri
      });

      try {
        if (
          window.location.search.includes("code=") &&
          window.location.search.includes("state=")
        ) {
          const { appState } = await this.auth0Client.handleRedirectCallback();
          onRedirectCallback(appState);
        }
      } catch (e) {
        this.error = e;
      } finally {
        this.isAuthenticated = await this.auth0Client.isAuthenticated();
        this.user = await this.auth0Client.getUser();
        this.loading = false;
      }
    }
  });

  return instance;
};

export const Auth0Plugin = {
  install(Vue, options) {
    Vue.prototype.$auth = useAuth0(options);
  }
};

هذا الملف يشكل قلب نظام المصادقة. فهو ينشئ نسخة مشتركة من عميل Auth0، ثم يتيح الوصول إلى حالات مثل loading وisAuthenticated وuser من أي مكون داخل التطبيق.

إنشاء ملف إعدادات Auth0

في جذر المشروع أنشئ ملفًا باسم auth_config.json، ثم أضف القيم الخاصة بتطبيقك:

{
  "domain": "yourAppValuesHere",
  "clientId": "yourAppValuesHere"
}

استبدل القيم السابقة ببيانات Domain وClient ID من لوحة تحكم Auth0.

ورغم أن هذا الملف لا يحتوي على أسرار شديدة الحساسية، فمن الأفضل عدم رفعه إلى مستودع الشيفرة. أضف اسم الملف auth_config.json إلى ملف .gitignore.

ربط الإضافة بملف main.js

افتح الملف main.js وأضف تعليمات الاستيراد التالية:

import { domain, clientId } from "../auth_config.json";
import { Auth0Plugin } from "./auth";

بعد ذلك فعّل الإضافة عبر Vue.use() قبل إنشاء التطبيق:

Vue.use(Auth0Plugin, {
  domain,
  clientId,
  onRedirectCallback: appState => {
    router.push(
      appState && appState.targetUrl
        ? appState.targetUrl
        : window.location.pathname
    );
  }
});

هذه الخطوة تضمن أن التطبيق يعالج العودة من صفحة تسجيل الدخول بشكل صحيح، ويعيد المستخدم إلى المسار المناسب بعد نجاح المصادقة.

إضافة تسجيل الدخول إلى واجهة التطبيق

إظهار أزرار تسجيل الدخول

داخل الملف App.vue، حدّث شريط التنقل ليشمل زري تسجيل دخول، أحدهما يعيد توجيه المستخدم إلى صفحة Auth0 المستضافة، والآخر يفتح نافذة منبثقة داخل التطبيق:

<div id="nav">
  <router-link to="/">Home</router-link> |
  <router-link to="/about">About</router-link> |
  <div v-if="!$auth.loading">
    | <button @click="login" v-if="!$auth.isAuthenticated">Login</button> |
    <button @click="loginPopup" v-if="!$auth.isAuthenticated">Login Popup</button> |
  </div>
</div>

اعتمدنا هنا على المتغير $auth.loading لتأجيل عرض الأزرار حتى تنتهي تهيئة المصادقة، كما استخدمنا $auth.isAuthenticated لإخفاء الأزرار إذا كان المستخدم مسجلًا بالفعل.

تحسين تنسيق شريط التنقل

لضبط العناصر في سطر واحد، يمكنك تحديث التنسيقات بهذا الشكل:

#nav {
  display: flex;
  justify-content: center;
  padding: 30px;
}

#nav a {
  font-weight: bold;
  color: #2c3e50;
  padding: 0 5px;
}

ظهور أزرار تسجيل الدخول داخل شريط التنقل في تطبيق Vue

إضافة الدوال داخل App.vue

أضف الكائن methods التالي:

methods: {
  login() {
    this.$auth.loginWithRedirect();
  },
  loginPopup() {
    this.$auth.loginWithPopup();
  }
}

تعتمد هذه الدوال على الواجهة التي وفرناها سابقًا داخل الإضافة. وعند الضغط على زر Login سيتم تحويل المستخدم إلى صفحة تسجيل الدخول المستضافة من Auth0، بينما يعرض زر Login Popup نافذة تسجيل دخول منبثقة.

صفحة تسجيل الدخول المستضافة من Auth0نافذة تسجيل الدخول المنبثقة داخل تطبيق Vue باستخدام Auth0

بمجرد نجاح المستخدم في تسجيل الدخول أو إنشاء حساب جديد، ستتحول قيمة isAuthenticated إلى true، وبالتالي تختفي أزرار تسجيل الدخول تلقائيًا.

إضافة وظيفة تسجيل الخروج

في الملف App.vue، أضف زر تسجيل الخروج مع شرط ظهوره فقط عندما يكون المستخدم موثقًا:

<button @click="logout" v-if="$auth.isAuthenticated">Logout</button>

ظهور زر تسجيل الخروج بعد نجاح المصادقة في تطبيق Vue

ثم أضف الدالة التالية داخل methods:

logout() {
  this.$auth.logout();
  this.$router.push({ path: '/' });
}

تنفذ هذه الدالة عمليتين: إنهاء جلسة المستخدم عبر Auth0، ثم إعادة توجيهه إلى الصفحة الرئيسية حتى لا يبقى داخل مسار محمي.

إظهار الصفحات للمستخدمين المصرح لهم فقط

إخفاء رابط About من شريط التنقل

إذا أردت أن تكون صفحة About متاحة فقط بعد تسجيل الدخول، فابدأ بإخفاء رابطها من شريط التنقل عندما لا يكون المستخدم مصادقًا:

<router-link v-if="$auth.isAuthenticated" to="/about">About</router-link>

لكن هذه الخطوة وحدها لا تكفي، لأن أي شخص يمكنه كتابة الرابط /about يدويًا داخل المتصفح.

استخدام Route Guard لحماية المسارات

لحماية الصفحة بشكل فعلي، أنشئ ملفًا جديدًا باسم authGuard.js داخل مجلد auth، ثم أضف الكود التالي:

import { getInstance } from "./index";

export const authGuard = (to, from, next) => {
  const authService = getInstance();

  const fn = () => {
    if (authService.isAuthenticated) {
      return next();
    }

    authService.loginWithRedirect({
      appState: { targetUrl: to.fullPath }
    });
  };

  if (!authService.loading) {
    return fn();
  }

  authService.$watch("loading", loading => {
    if (loading === false) {
      return fn();
    }
  });
};

هذا الحارس يفحص أولًا ما إذا كانت عملية التهيئة قد انتهت، ثم يتحقق من حالة المصادقة. إذا لم يكن المستخدم مسجلًا، فسيتم تحويله تلقائيًا إلى صفحة تسجيل الدخول.

ربط الحارس مع Vue Router

افتح ملف router/index.js وأضف الاستيراد التالي:

import { authGuard } from "../auth/authGuard";

ثم حدّث مسار /about ليصبح كالتالي:

{
  path: '/about',
  name: 'About',
  component: () =>
    import(/* webpackChunkName: "about" */ '../views/About.vue'),
  beforeEnter: authGuard
}

الآن إذا حاول مستخدم غير موثق الوصول إلى /about مباشرة، فسيتم منعه وإعادته إلى صفحة تسجيل الدخول.

أفضل الممارسات عند دمج Auth0 مع Vue

  • احفظ إعدادات Domain وClient ID في ملف منفصل لتسهيل الإدارة.
  • لا تكتفِ بإخفاء الروابط من الواجهة، بل استخدم دائمًا Route Guard لحماية المسارات.
  • اختبر إعدادات Allowed Callback URLs وAllowed Logout URLs بدقة، لأن أي خطأ فيها يسبب فشل العودة إلى التطبيق.
  • استخدم طريقة loginWithRedirect() إذا أردت تجربة مستقرة وسهلة التوافق، خاصة في البيئات الإنتاجية.
  • استخدم loginWithPopup() عندما ترغب في إبقاء المستخدم داخل نفس الواجهة دون انتقال كامل إلى صفحة خارجية.

متى تختار Auth0 بدل بناء نظام مصادقة من الصفر؟

المعيار Auth0 بناء نظام مخصص
سرعة التنفيذ سريعة جدًا أبطأ وتحتاج وقت تطوير أكبر
الأمان مدعوم ببنية جاهزة ومختبرة يعتمد على خبرة الفريق
الصيانة أبسط في التحديث والمتابعة تحتاج مراقبة مستمرة
المرونة مرتفعة في أغلب السيناريوهات أعلى نظريًا لكن بكلفة أكبر
الملاءمة للمشاريع الناشئة ممتازة ليست الخيار الأسرع غالبًا

الخلاصة التقنية

يُعد دمج Auth0 مع Vue خيارًا عمليًا لمن يريد إضافة مصادقة قوية وآمنة بأقل تعقيد ممكن. القيمة الحقيقية لا تكمن فقط في تسهيل تسجيل الدخول، بل في توفير بنية احترافية لإدارة الجلسات، حماية المسارات، وتحسين تجربة المستخدم دون استنزاف وقت التطوير في تفاصيل أمنية حساسة. إذا كنت تبني تطبيق SPA وتبحث عن حل سريع وقابل للتوسع، فإن هذا التكامل يمثل نقطة انطلاق ممتازة من الناحية التقنية والعملية.

اترك تعليقاً

لن يتم نشر عنوان بريدك الإلكتروني. الحقول الإلزامية مشار إليها بـ *