दोन टॅब्समध्ये एकाच वेळी रिफ्रेश रिक्वेस्ट गेल्यामुळे बॅकएंडवर ताण येणे आणि सेशन इनव्हॅलिडेट होणे याला 'टोकन ट्रॅप' म्हणतात. जेव्हा एकाच वापरकर्त्याने उघडलेल्या पाच टॅब्सनी एकाच मिलीसेकंदात एक्स्पायर्ड JWT रिफ्रेश करण्याचा प्रयत्न केला, तेव्हा एका लाईव्ह साइटवर ही समस्या निर्माण झाली. यामुळे बॅकएंडवर ड्युप्लिकेट रिफ्रेश रिक्वेस्ट्सचा पाऊस पडला आणि सेशन त्वरित इनव्हॅलिडेट झाले. प्रत्येक टॅबने वापरकर्त्याला लॉग आउट केले, ज्यामुळे हे सिद्ध झाले की टोकन नूतनीकरणासाठी (token renewal) केवळ सिंगल-टॅब सोल्यूशन आता पुरेसे नाही.
टोकन ट्रॅप का महत्त्वाचा आहे
आधुनिक सिंगल-पेज ॲप्समध्ये कमी कालावधीचे access tokens आणि जास्त कालावधीचे refresh token वापरले जातात. जेव्हा access token एक्स्पायर होतो, तेव्हा क्लायंट एक refresh request पाठवतो, नवीन टोकन जोडी प्राप्त करतो आणि मूळ कॉल पुन्हा करण्याचा प्रयत्न करतो. बहुतेक डेव्हलपर्स या फ्लोसाठी इन-मेमरी फ्लॅग (उदा. isRefreshing = true) किंवा रिक्वेस्ट क्यू (request queue) वापरतात आणि त्याची चाचणी एकाच टॅबमध्ये घेतात. वास्तविक जगात वापरकर्ते अनेक टॅब्स उघडे ठेवतात: जसे की सेटिंग्स पेज, ॲनालिटिक्स डॅशबोर्ड आणि काही डेटा व्ह्यूज. जेव्हा access token एक्स्पायर होतो, तेव्हा प्रत्येक टॅब स्वतंत्रपणे 401 एरर शोधतो, प्रत्येक टॅब रिफ्रेश रिक्वेस्ट पाठवतो आणि बॅकएंड—विशेषतः जेव्हा ते refresh-token rotation लागू करते—तेव्हा दुसऱ्या रिक्वेस्टला 'रिप्ले' मानून संपूर्ण सेशन रद्द करते.
JavaScript आयसोलेशनमुळे ही समस्या निर्माण होते
प्रत्येक ब्राउझर टॅब स्वतःचा स्वतंत्र JavaScript कॉन्टेक्स्ट चालवतो. व्हेरिएबल्स, टाइमर्स आणि इन-मेमरी फ्लॅग्स इतर टॅब्सना दिसत नाहीत, जरी ते एकाच 'origin' शेअर करत असले तरीही. "रिफ्रेश आधीच सुरू आहे" असे सांगणारा फ्लॅग फक्त त्याच टॅबमध्ये असतो ज्याने तो सेट केला आहे. इतर टॅब्सना हे कळण्याचा कोणताही मार्ग नसतो की टोकन इतरत्र रिफ्रेश केले जात आहे, त्यामुळे ते सर्व स्वतःची नेटवर्क कॉल सुरू करतात. ही समस्या इंटरसेप्टरमधील (interceptor) बग नाही; तर ती क्लायंट-साइड स्टेट आयसोलेशनची (client-side state isolation) एक मूलभूत मर्यादा आहे.
Web Locks API मदतीला येते
जेव्हा एखादा टॅब लॉक धारण करतो, तेव्हा त्याच लॉकची मागणी करणारा कोणताही दुसरा टॅब तो लॉक रिलीज होईपर्यंत वाट पाहू शकतो.
टोकन रिफ्रेशसाठी हे कसे कार्य करते
- 401 डिटेक्ट करा – कोणताही टॅब ज्याला अनधिकृत (unauthorized) प्रतिसाद मिळतो, तो
navigator.locks.request('auth_token_refresh_lock', async lock => { … })कॉल करतो. - लॉक मिळवा – जर इतर कोणताही टॅब लॉक धारण करत नसेल, तर सध्याचा टॅब पुढे जातो; अन्यथा तो लॉक मोकळा होईपर्यंत थांबतो.
- एकदाच रिफ्रेश करा – लॉक धारण करणारा टॅब रिफ्रेश रिक्वेस्ट पाठवतो, नवीन access token आणि टाइमस्टॅम्प
localStorageमध्ये स्टोअर करतो आणि त्यानंतर कॉल बॅक संपल्यावर लॉक आपोआप रिलीज करतो. - ड्युप्लिकेट काम टाळा – जेव्हा एखादा वाट पाहणारा टॅब शेवटी लॉक मिळवतो, तेव्हा तो
localStorageमधून टाइमस्टॅम्प वाचतो. जर टोकन एका ठराविक वेळेत (उदा. गेल्या काही सेकंदात) रिफ्रेश झाले असेल, तर तो टॅब नेटवर्क कॉल टाळतो आणिlocalStorageमधून त्याचे इन-मेमरी टोकन अपडेट करतो. - क्रॅश हाताळा – जर लॉक धारण करत असताना एखादा टॅब क्रॅश झाला किंवा बंद झाला, तर ब्राउझर तो लॉक रिलीज करतो, ज्यामुळे दुसऱ्या टॅबला रिफ्रेश पुन्हा करण्याचा प्रयत्न करता येतो.
एका दृष्टीक्षेपात फायदे
- शून्य अनावश्यक नेटवर्क कॉल्स – फक्त पहिला टॅब बॅकएंडशी संवाद साधतो.
- सेशन नष्ट होत नाही – रिफ्रेश-टोकन रोटेशनमध्ये फक्त एकदाच वापर दिसून येतो, ज्यामुळे सेशन जिवंत राहते.
- ग्रॅसफुल रिकव्हरी – ब्राउझरद्वारे व्यवस्थापित लॉक रिलीजमुळे टॅब गायब झाल्यास डेडलॉक (deadlock) टाळता येतात.
अंमलबजावणीसाठी चेकलिस्ट (Implementation checklist)
- तुमच्या Axios (किंवा fetch) इंटरसेप्टरमध्ये लॉक रिक्वेस्टसह रिफ्रेश लॉजिक गुंडाळा (wrap).
- रिफ्रेश केलेले टोकन आणि मिलीसेकंद टाइमस्टॅम्प
localStorageमध्ये स्टोअर करा (किंवा तुम्हाला प्रति-सेशन डेटा हवा असल्यासsessionStorageवापरा). - जेव्हा लॉक मंजूर केला जातो, तेव्हा स्टोअर केलेला टाइमस्टॅम्प
Date.now()शी तुलना करा. जर फरक तुमच्या थ्रेशोल्डपेक्षा (threshold) कमी असेल, तर बॅकएंडला कॉल करण्याऐवजी स्टोरेजमधून टोकन वाचा. - मूळ API कॉल पुन्हा करण्याचा प्रयत्न करण्यापूर्वी इंटरसेप्टरने स्टोरेजमधून मिळवलेल्या टोकनसह रिक्वेस्ट हेडर्स अपडेट केले आहेत याची खात्री करा.
- नेटवर्क लॅटन्सी (latency) सिम्युलेट करून अनेक टॅब्ससह या फ्लोची चाचणी घ्या, जेणेकरून केवळ एकच रिफ्रेश रिक्वेस्ट सर्व्हरपर्यंत पोहोचते याची खात्री होईल.
काय चुकीचे होऊ शकते
पुढे काय पाहावे
मुख्य निष्कर्ष (Takeaway)
प्रत्येक ब्राउझर टॅबला एका लहान डिस्ट्रिब्युटेड सिस्टममधील (distributed system) नोडप्रमाणे समजा. JWT रिफ्रेशेसना सिरीयलाईज करण्यासाठी Web Locks API वापरून, तुम्ही ड्युप्लिकेट कॉल्स काढून टाकू शकता, रिफ्रेश-टोकन रोटेशन सुरक्षित करू शकता आणि वापरकर्त्यांना त्यांच्या सर्व उघड्या टॅब्समध्ये लॉग इन ठेवू शकता.
