ஒரே பயனர் திறந்த ஐந்து டேப்கள் (tabs), காலாவதியான ஒரு JWT-ஐ ஒரே மில்லிசெகண்டில் புதுப்பிக்க முயன்றபோது, அந்த 'token trap' ஒரு நேரடித் தளத்தைப் பாதித்தது. இது ஒரே நேரத்தில் பல நகல் புதுப்பிப்பு கோரிக்கைகளை (duplicate refresh requests) பேக்எண்டிற்கு (backend) அனுப்பி, உடனடியாக அந்தச் சessனாவை (session) செல்லாததாக்கியது. ஒவ்வொரு டேபும் பயனரை வெளியேற்றியது (log out), இதன் மூலம் டோக்கன் புதுப்பித்தலுக்கு ஒரு டேப் மட்டுமே போதுமானது என்ற அணுகுமுறை இனி செல்லாது என்பதை நிரூபித்தது.

ஏன் token trap முக்கியமானது

நவீன single-page apps குறுகிய காலத்திற்குப் பயன்படும் access tokens மற்றும் நீண்ட காலத்திற்குப் பயன்படும் refresh token ஆகியவற்றைப் பயன்படுத்துகின்றன. Access token காலாவதியாகும் போது, கிளையண்ட் ஒரு refresh கோரிக்கையை அனுப்பி, புதிய டோக்கன் ஜோடியைப் பெற்று, அசல் அழைப்பை மீண்டும் முயற்சிக்கும். பெரும்பாலான டெவலப்பர்கள் இந்தச் செயல்பாட்டை ஒரு in-memory flag (உதாரணமாக, isRefreshing = true) அல்லது request queue மூலம் பாதுகாப்பார்கள், மேலும் இதை ஒரு டேப்பில் மட்டும் சோதிப்பார்கள். ஆனால் நிஜ உலகில் பயனர்கள் பல டேப்களைத் திறந்து வைத்திருப்பார்கள்: ஒரு settings page, ஒரு analytics dashboard மற்றும் சில data views. Access token காலாவதியாகும் போது, ஒவ்வொரு டேப்பும் 401 error-ஐத் தனித்தனியாகக் கண்டறிந்து, ஒவ்வொன்றும் ஒரு refresh கோரிக்கையை அனுப்புகிறது. பேக்எண்ட்—குறிப்பாக refresh-token rotation முறையைப் பயன்படுத்தும்போது—இரண்டாவது கோரிக்கையை ஒரு replay தாக்குதலாகக் கருதி, முழு session-ஐயும் ரத்து செய்துவிடும்.

JavaScript isolation சிக்கலை உருவாக்குகிறது

ஒவ்வொரு பிரவுசர் டேப்பும் அதன் சொந்த JavaScript context-இல் இயங்குகிறது. மாறிகள் (Variables), டைமர்கள் (timers) மற்றும் in-memory flags ஆகியவை ஒரே origin-ஐப் பகிர்ந்து கொண்டாலும் மற்ற டேப்களுக்குத் தெரியாது. “ஒரு refresh ஏற்கனவே நடந்து கொண்டிருக்கிறது” என்று கூறும் ஒரு flag, அதை அமைத்த டேப்பிற்குள் மட்டுமே இருக்கும். டோக்கன் வேறு எங்கோ புதுப்பிக்கப்படுகிறது என்பதை மற்ற டேப்களால் அறிய முடியாது, எனவே அவை அனைத்தும் தமக்கெனத் தனித்தனியாக network call-களைத் தொடங்கும். இது interceptor-இல் உள்ள பிழை அல்ல; இது client-side state isolation-இன் அடிப்படை வரம்பு ஆகும்.

Web Locks API தீர்வாகிறது

ஒரு டேப் ஒரு lock-ஐப் பிடித்துக் கொண்டிருக்கும்போது, அதே lock-ஐக் கேட்கும் மற்ற எந்த டேப்பும் அது விடுவிக்கப்படும் வரை காத்திருக்க வேண்டும்.

டோக்கன் புதுப்பித்தலுக்கு இது எவ்வாறு செயல்படுகிறது

  1. 401-ஐக் கண்டறிதல் – அங்கீகரிக்கப்படாத (unauthorized) பதிலைப் பெறும் எந்தத் டேப்பும் navigator.locks.request('auth_token_refresh_lock', async lock => { … }) ஐ அழைக்கும்.
  2. lock-ஐப் பெறுதல் – வேறு எந்தத் டேப்பும் lock-ஐப் பிடித்துக் கொண்டிருக்கவில்லை என்றால், தற்போதைய டேப் தொடரும்; இல்லையெனில் lock விடுவிக்கப்படும் வரை அது காத்திருக்கும்.
  3. ஒருமுறை புதுப்பித்தல் – lock வைத்திருப்பவர் refresh கோரிக்கையை அனுப்பி, புதிய access token மற்றும் ஒரு timestamp-ஐ localStorage-இல் சேமித்து, பின்னர் callback முடிந்ததும் lock-ஐத் தானாகவே விடுவிப்பார்.
  4. நகல் வேலைகளைத் தவிர்த்தல் – காத்திருக்கும் டேப் இறுதியாக lock-ஐப் பெறும்போது, அது localStorage-லிருந்து timestamp-ஐப் படிக்கும். டோக்கன் ஒரு குறிப்பிட்ட காலத்திற்குள் (உதாரணமாக, கடைசி சில வினாடிகளில்) புதுப்பிக்கப்பட்டிருந்தால், அந்த டேப் network call-ஐத் தவிர்த்துவிட்டு, localStorage-லிருந்து அதன் in-memory token-ஐப் புதுப்பித்துக் கொள்ளும்.
  5. செயலிழல்களைக் கையாளுதல் – lock வைத்திருக்கும்போது ஒரு டேப் செயலிழந்தாலோ அல்லது மூடப்பட்டாலோ, பிரவுசர் அந்த lock-ஐ விடுவிக்கும், இதனால் மற்றொரு டேப் புதுப்பிப்பை மீண்டும் முயற்சி செய்யலாம்.

சுருக்கமான நன்மைகள்

  • தேவையற்ற network calls இல்லை – முதல் டேப் மட்டுமே பேக்எண்டுடன் தொடர்பு கொள்ளும்.
  • session அழிப்பு இல்லை – refresh-token rotation ஒரே ஒரு முறையை மட்டுமே காணும், இதனால் session தொடரும்.
  • தடையற்ற மீட்பு (Graceful recovery) – ஒரு டேப் காணாமல் போனாலும், பிரவுசர் மூலம் நிர்வகிக்கப்படும் lock release, deadlocks ஏற்படுவதைத் தடுக்கும்.

செயல்படுத்தும் சரிபார்ப்புப் பட்டியல் (Implementation checklist)

  • உங்கள் Axios (அல்லது fetch) interceptor-இல் lock request-உடன் refresh logic-ஐச் சேர்க்கவும்.
  • புதுப்பிக்கப்பட்ட டோக்கன் மற்றும் மில்லிசெகண்ட் timestamp-ஐ localStorage-இல் (அல்லது ஒவ்வொரு session-க்கும் தனித்தனித் தரவு வேண்டுமெனில் sessionStorage-இல்) சேமிக்கவும்.
  • lock வழங்கப்பட்டதும், சேமிக்கப்பட்ட timestamp-ஐ Date.now() உடன் ஒப்பிடவும். வித்தியாசம் உங்கள் வரம்பிற்கு (threshold) குறைவாக இருந்தால், பேக்எண்டிற்கு அழைப்பதற்குப் பதிலாக storage-லிருந்து டோக்கனைப் படிக்கவும்.
  • அசல் API அழைப்பை மீண்டும் முயற்சிப்பதற்கு முன், interceptor சேமிப்பகத்திலிருந்து பெறப்பட்ட டோக்கனைப் பயன்படுத்தி request headers-ஐப் புதுப்பிப்பதை உறுதி செய்யவும்.
  • ஒரே ஒரு refresh கோரிக்கை மட்டுமே சர்வரைச் சென்றடைகிறது என்பதைச் சரிபார்க்க, network latency-ஐ உருவகப்படுத்தி (simulating), பல டேப்களைக் கொண்டு இந்தச் செயல்பாட்டைச் சோதிக்கவும்.

என்ன தவறாகலாம்

அடுத்து எவற்றைக் கவனிக்க வேண்டும்

சுருக்கம் (Takeaway)

ஒவ்வொரு பிரவுசர் டேப்பையும் ஒரு சிறிய distributed system-இன் ஒரு node ஆகக் கருதுங்கள். JWT புதுப்பிப்புகளைத் தொடர்ச்சியாகச் செய்ய (serialize) Web Locks API-ஐப் பயன்படுத்துவதன் மூலம், நீங்கள் நகல் அழைப்புகளைத் தவிர்க்கலாம், refresh-token rotation-ஐப் பாதுகாக்கலாம் மற்றும் பயனர்கள் திறந்திருக்கும் அனைத்து டேப்களிலும் அவர்களை log in செய்தே வைத்திருக்கலாம்.