Web Locks API پانچ کھلی ٹیبز کو ایک ساتھ آتھ (auth) سرور پر ریفریش ٹوکن (refresh-token) کالز کی بھرمار کرنے سے روک سکتا ہے، جس سے صارفین کو اچانک لاگ آؤٹ ہونے سے بچایا جا سکتا ہے۔ ٹیبز کے درمیان ریفریشز کو ہم آہنگ (coordinate) کر کے، ایک ہی درخواست اس سیلاب کی جگہ لے لیتی ہے جو عام طور پر ریفریش ٹوکن روٹیشن (Refresh Token Rotation) کے استعمال کے دوران سیشن ختم کرنے کا سبب بنتا ہے۔

ملٹی ٹیب سیشن میں چھپا ہوا اوور لوڈ

ایک عام سنگل پیج ایپ (single-page app) ایک Axios انٹرسیپٹر (interceptor) شامل کرتی ہے جو 401 رسپانس پر نظر رکھتی ہے، ایک بولین (Boolean) isRefreshing فلیگ کو تبدیل کرتی ہے، اور نئے JWT کے آنے تک تمام باہر جانے والی درخواستوں کو قطار (queue) میں لگا دیتی ہے۔ ایک ٹیب میں ٹیسٹ کرنے پر، یہ عمل بالکل درست کام کرتا ہے۔

اسی ایپ کو پانچ ٹیبز میں کھولیں، ایکسیس ٹوکن (access token) کو ختم ہونے دیں، اور تمام پانچ ٹیبز ایک ہی ملی سیکنڈ میں 401 کو نوٹ کریں گی۔ ہر ٹیب سمجھے گی کہ اسے ریفریش کرنا چاہیے، اس لیے پانچ ایک جیسی ریفریش ٹوکن درخواستیں آتھ سرور کے ساتھ مقابلہ کریں گی۔ ریفریش ٹوکن روٹیشن (Refresh Token Rotation) کے ساتھ—جو کہ ایک حفاظتی اقدام ہے جو نیا ٹوکن جاری ہوتے ہی پچھلے ریفریش ٹوکن کو کالعدم کر دیتا ہے—سرور دوسری درخواست کو ری پلے اٹیک (replay attack) کے طور پر دیکھتا ہے، سیشن کو خطرے میں سمجھتا ہے، اور اسے منسوخ کر دیتا ہے۔ صارف فوری طور پر ہر ٹیب سے لاگ آؤٹ ہو جاتا ہے۔

اس کی بنیادی وجہ جاوا اسکرپٹ (JavaScript) کا آئسولیشن ماڈل (isolation model) ہے۔ isRefreshing جیسا ویری ایبل صرف اسی ٹیب میں رہتا ہے جس نے اسے سیٹ کیا ہو؛ دوسری ٹیبز کے پاس یہ جاننے کا کوئی طریقہ نہیں ہوتا کہ ریفریش کا عمل پہلے سے جاری ہے۔ اس کا نتیجہ ایک کلاسک کنکرنسی (concurrency) کا مسئلہ ہے، لیکن یہاں "پروسیسز" تھریڈز کے بجائے براؤزر ٹیبز ہیں۔

کراس ٹیب لاک (cross-tab lock) کیوں ایک درست ٹول ہے

ہمیں ایک ایسے طریقے کی ضرورت ہے جس سے ٹیبز ایک مشترکہ وسائل (shared resource)—اس معاملے میں، تازہ JWT—کے بارے میں ایک دوسرے سے بات کر سکیں۔ Web Locks API، جو navigator.locks کے طور پر دستیاب ہے، بالکل یہی کام فراہم کرتا ہے۔ یہ اسکرپٹس کو ایک نامزد لاک (named lock) کی درخواست کرنے کی اجازت دیتا ہے جسے براؤزر ایک ہی اوریجن (origin) سے تعلق رکھنے والے تمام سیاق و سباق (contexts) میں نافذ کرتا ہے۔ اگر لاک پہلے سے موجود ہو، تو دیگر کالرز کو قطار میں لگا دیا جاتا ہے جب تک کہ ہولڈر اسے چھوڑ نہ دے یا براؤزر اسے ختم نہ کر دے (مثال کے طور پر، جب ٹیب کریش ہو جائے)۔ کوئی بیرونی سرور نہیں، کوئی پولنگ (polling) نہیں، صرف نیٹیو براؤزر کوآرڈینیشن۔

لاک پر مبنی ریفریش فلو کا نفاذ

  1. 401 کا پتہ لگائیں – Axios انٹرسیپٹر پہلے کی طرح غیر مجاز (unauthorized) رسپانس کو پکڑ لیتا ہے۔
  2. ایک خصوصی لاک (exclusive lock) کے لیے درخواست کریں – ٹیب navigator.locks.request('auth_token_refresh_lock', async lock => { … }) کو کال کرتی ہے۔ ایک وقت میں صرف ایک ٹیب ہی کال بیک (callback) میں داخل ہو سکتی ہے۔
  3. نیٹ ورک پر جانے سے پہلے دوبارہ چیک کریں – لاک کے اندر، localStorage سے ٹائم اسٹیمپ (یا خود ٹوکن) پڑھیں۔ اگر ٹائم اسٹیمپ چند سیکنڈ سے زیادہ پرانا نہیں ہے، تو اس کا مطلب ہے کہ کسی دوسری ٹیب نے پہلے ہی ٹوکن ریفریش کر لیا ہے؛ موجودہ ٹیب نیٹ ورک کال کو چھوڑ دے گی اور صرف اسٹوریج سے نیا JWT پڑھ لے گی۔
  4. اگر ضرورت ہو تو ریفریش کریں – اگر اسٹور شدہ ٹائم اسٹیمپ پرانا ہو چکا ہے، تو ریفریش کی درخواست بھیجیں، نیا ٹوکن اور موجودہ وقت localStorage میں محفوظ کریں، پھر کال بیک سے واپس آ کر لاک کو چھوڑ دیں۔
  5. قطار میں موجود درخواستوں کو دوبارہ شروع کریں – تمام دوسری ٹیبز جو انتظار کر رہی تھیں، ایک کے بعد ایک لاک حاصل کریں گی، تازہ ٹائم اسٹیمپ دیکھیں گی، اور مزید کوئی درخواست کیے بغیر کام مکمل کر لیں گی۔
async function refreshIfNeeded() {
  await navigator.locks.request('auth_token_refresh_lock', async lock => {
    const lastRefresh = Number(localStorage.getItem('token_refreshed_at') || 0);
    const now = Date.now();
    if (now - lastRefresh < 5_000) return; // another tab already refreshed

    const newToken = await callRefreshEndpoint(); // actual network call
    localStorage.setItem('jwt', newToken);
    localStorage.setItem('token_refreshed_at', now.toString());
  });
}

یہ پیٹرن اس بات کی ضمانت دیتا ہے کہ چاہے کتنی ہی ٹیبز کھلی ہوں، صرف ایک ریفریش درخواست ہی سرور تک پہنچتی ہے۔

فوائد جنہیں آپ ناپ سکتے ہیں

  • نیٹ ورک کی کارکردگی – ایک درخواست پانچ کی جگہ لے لیتی ہے، جس سے بینڈوڈتھ اور سرور لوڈ میں نمایاں کمی آتی ہے۔
  • سیشن کی حفاظت – ریفریش ٹوکن روٹیشن کے ساتھ، سرور پرانے ریفریش ٹوکن کا صرف ایک بار استعمال دیکھتا ہے، اس لیے یہ کبھی بھی سیشن کو خطرے میں نہیں سمجھتا۔
  • لچک (Resilience) – اگر لاک رکھنے والی ٹیب کریش ہو جائے، تو براؤزر خود بخود لاک کو چھوڑ دیتا ہے، جس سے ڈیڈ لاک (deadlock) سے بچا جا سکتا ہے جو ورنہ تمام ٹیبز کو روک سکتا تھا۔
  • اسکیل ایبلٹی (Scalability) – صارفین درجنوں ٹیبز کھول سکتے ہیں بغیر اس خطرے کے کہ وہ لاگ آؤٹ ہو جائیں، کیونکہ کوآرڈینیشن براؤزر کے اندر ہی رہتی ہے۔

دوسرا رخ: براؤزر سپورٹ اور فال بیکس (fallbacks)

Web Locks API ایک نسبتاً نیا فیچر ہے۔ جدید Chromium پر مبنی براؤزرز اور Firefox کے حالیہ ورژن اسے نافذ کرتے ہیں، لیکن پرانے براؤزرز اور Safari میں اس کی نیٹیو سپورٹ موجود نہیں ہے۔ ایسے ماحول میں جہاں API دستیاب نہ ہو، ڈویلپرز کو کم قابل اعتماد تکنیک کا سہارا لینا پڑتا ہے—جیسے کہ localStorage کے ذریعے کسٹم ایونٹ براڈکاسٹ کرنا یا شیئرڈ ورکر (shared worker) کا استعمال کرنا—تاکہ کراس ٹیب سگنلنگ کے قریب پہنچا جا سکے۔ ان متبادل طریقوں میں وہ خودکار ڈیڈ لاک پروٹیکشن نہیں ہوتی جو navigator.locks فراہم کرتا ہے، اس لیے انہیں احتیاط سے استعمال کیا جانا چاہیے۔

آگے کیا دیکھنا ہے

  • معیار بندی کی پیشرفت – API کے اپنائے جانے کے رجحان پر نظر رکھیں؛ وسیع تر سپورٹ لاک پر مبنی طریقہ کار کو کسی بھی کراس ٹیب کوآرڈینیشن کے لیے ڈیفالٹ بنا دے گی۔
  • لائبریری ریپرز – چند اوپن سورس یوٹیلیٹیز پہلے ہی لاک ریکوسٹ پیٹرن کو ایبسٹریکٹ کر رہی ہیں، جس سے انہیں موجودہ Axios interceptors میں شامل کرنا آسان ہو جاتا ہے۔
  • سیکیورٹی آڈٹ – اگرچہ لاک کنکرنسی (concurrency) کے مسئلے کو حل کرتا ہے، لیکن ریفریش اینڈ پوائنٹ کو پھر بھی مناسب ٹوکن روٹیشن اور ریٹ لمٹنگ کو نافذ کرنا چاہیے، کیونکہ اگر لاک کو بائی پاس کر دیا جائے تو ایک ہی بدنیتی پر مبنی ٹیب سرور کو درخواستوں سے بھر سکتا ہے۔

خلاصہ سادہ ہے: کھلی ٹیبز کے ایک سیٹ کو ایک ڈسٹریبیوٹڈ سسٹم کے طور پر سمجھیں اور انہیں ایک نیٹیو سنکرونائزیشن پرائمٹیو فراہم کریں۔ ٹوکن ریفریش فلو میں Web Locks API کو شامل کر کے، ڈویلپرز "براؤزر ٹیب ٹوکن ٹریپ" کو ختم کر سکتے ہیں اور صارفین کو لاگ ان رکھتے ہیں، چاہے وہ کتنی ہی ٹیبز کا استعمال کر رہے ہوں۔