Web Locks API ਪੰਜ ਖੁੱਲ੍ਹੇ ਟੈਬਾਂ ਨੂੰ ਇੱਕੋ ਸਮੇਂ refresh-token ਕਾਲਾਂ ਨਾਲ auth ਸਰਵਰ 'ਤੇ ਹਮਲਾ ਕਰਨ ਤੋਂ ਰੋਕ ਸਕਦੀ ਹੈ, ਜਿਸ ਨਾਲ ਉਪਭੋਗਤਾਵਾਂ ਨੂੰ ਅਚਾਨਕ logouts ਤੋਂ ਬਚਾਇਆ ਜਾ ਸਕਦਾ ਹੈ। ਟੈਬਾਂ ਵਿੱਚ ਰਿਫ੍ਰੈਸ਼ਾਂ ਨੂੰ ਤਾਲਮੇਲ (coordinate) ਕਰਕੇ, ਇੱਕ ਸਿੰਗਲ ਰਿਕੁਐਸ ਉਸ ਭੜਕਾਅ (flood) ਦੀ ਜਗ੍ਹਾ ਲੈ ਲੈਂਦੀ ਹੈ ਜੋ ਆਮ ਤੌਰ 'ਤੇ Refresh Token Rotation ਦੀ ਵਰਤੋਂ ਕਰਦੇ ਸਮੇਂ session-kill ਨੂੰ ਟ੍ਰਿਗਰ ਕਰਦੀ ਹੈ।
ਮਲਟੀ-ਟੈਬ ਸੈਸ਼ਨ ਵਿੱਚ ਲੁਕਿਆ ਹੋਇਆ ਓਵਰਲੋਡ
ਇੱਕ ਆਮ ਸਿੰਗਲ-ਪੇਜ ਐਪ (SPA) ਇੱਕ Axios interceptor ਜੋੜਦੀ ਹੈ ਜੋ 401 ਰਿਸਪਾਂਸ 'ਤੇ ਨਜ਼ਰ ਰੱਖਦੀ ਹੈ, ਇੱਕ Boolean isRefreshing ਫਲੈਗ ਨੂੰ ਬਦਲਦੀ ਹੈ, ਅਤੇ ਨਵਾਂ JWT ਆਉਣ ਤੱਕ ਕਿਸੇ ਵੀ ਬਾਹਰ ਜਾਣ ਵਾਲੀ ਰਿਕੁਐਸ ਨੂੰ ਕਿਊ (queue) ਕਰਦੀ ਹੈ। ਇੱਕ ਟੈਬ ਵਿੱਚ ਟੈਸਟ ਕਰਨ 'ਤੇ, ਇਹ ਪ੍ਰਕਿਰਿਆ ਬਿਨਾਂ ਕਿਸੇ ਨੁਕਸ ਦੇ ਕੰਮ ਕਰਦੀ ਹੈ।
ਉਸੇ ਐਪ ਨੂੰ ਪੰਜ ਟੈਬਾਂ ਵਿੱਚ ਖੋਲ੍ਹੋ, access token ਨੂੰ ਖਤਮ ਹੋਣ ਦਿਓ, ਅਤੇ ਪੰਜੇ ਟੈਬ ਇੱਕੋ ਮਿਲੀਸੈਕੰਡ ਵਿੱਚ 401 ਨੂੰ ਨੋਟ ਕਰਨਗੇ। ਹਰ ਟੈਬ ਨੂੰ ਲੱਗਦਾ ਹੈ ਕਿ ਉਸਨੂੰ ਰਿਫ੍ਰੈਸ਼ ਕਰਨਾ ਚਾਹੀਦਾ ਹੈ, ਇਸ ਲਈ ਪੰਜ ਇੱਕੋ ਜਿਹੇ refresh-token ਰਿਕੁਐਸ auth ਸਰਵਰ ਨਾਲ ਮੁਕਾਬਲਾ ਕਰਦੇ ਹਨ। Refresh Token Rotation ਦੇ ਨਾਲ—ਇੱਕ ਸੁਰੱਖਿਆ ਉਪਾਅ ਜੋ ਨਵਾਂ ਟੋਕਨ ਜਾਰੀ ਹੁੰਦੇ ਹੀ ਪੁਰਾਣੇ refresh token ਨੂੰ ਅਵੈਧ (invalidate) ਕਰ ਦਿੰਦਾ ਹੈ—ਸਰਵਰ ਦੂਜੀ ਰਿਕੁਐਸ ਨੂੰ replay attack ਵਜੋਂ ਦੇਖਦਾ ਹੈ, ਸੈਸ਼ਨ ਨੂੰ ਖਤਰੇ ਵਿੱਚ (compromised) ਮੰਨਦਾ ਹੈ, ਅਤੇ ਇਸਨੂੰ ਰੱਦ ਕਰ ਦਿੰਦਾ ਹੈ। ਉਪਭੋਗਤਾ ਇੱਕ ਪਲ ਵਿੱਚ ਹਰ ਟੈਬ ਤੋਂ log out ਹੋ ਜਾਂਦਾ ਹੈ।
ਇਸਦਾ ਮੂਲ ਕਾਰਨ JavaScript ਦਾ isolation model ਹੈ। isRefreshing ਵਰਗਾ ਵੇਰੀਏਬਲ ਸਿਰਫ਼ ਉਸੇ ਟੈਬ ਵਿੱਚ ਰਹਿੰਦਾ ਹੈ ਜਿਸਨੇ ਇਸਨੂੰ ਸੈੱਟ ਕੀਤਾ ਹੈ; ਦੂਜੇ ਟੈਬਾਂ ਕੋਲ ਇਹ ਜਾਣਨ ਦਾ ਕੋਈ ਤਰੀਕਾ ਨਹੀਂ ਹੈ ਕਿ ਰਿਫ੍ਰੈਸ਼ ਪਹਿਲਾਂ ਹੀ ਚੱਲ ਰਿਹਾ ਹੈ। ਨਤੀਜਾ ਇੱਕ ਕਲਾਸਿਕ concurrency ਸਮੱਸਿਆ ਹੈ, ਪਰ ਇੱਥੇ "processes" ਥ੍ਰੈਡਸ (threads) ਦੀ ਬਜਾਏ ਬ੍ਰਾਊਜ਼ਰ ਟੈਬ ਹਨ।
ਕ੍ਰਾਸ-ਟੈਬ ਲੌਕ (cross-tab lock) ਹੀ ਸਹੀ ਸਾਧਨ ਕਿਉਂ ਹੈ
ਸਾਨੂੰ ਇੱਕ ਅਜਿਹੇ ਤਰੀਕੇ ਦੀ ਲੋੜ ਹੈ ਜਿਸ ਨਾਲ ਟੈਬ ਇੱਕ ਸਾਂਝੇ ਸਰੋਤ (shared resource)—ਇਸ ਮਾਮਲੇ ਵਿੱਚ, ਤਾਜ਼ਾ JWT—ਬਾਰੇ ਇੱਕ ਦੂਜੇ ਨਾਲ ਗੱਲ ਕਰ ਸਕਣ। Web Locks API, ਜੋ navigator.locks ਵਜੋਂ ਉਪਲਬਧ ਹੈ, ਬਿਲਕੁਲ ਇਹੀ ਕਰਦੀ ਹੈ। ਇਹ ਸਕ੍ਰਿਪਟਾਂ ਨੂੰ ਇੱਕ ਨਾਮਿਤ ਲੌਕ (named lock) ਦੀ ਮੰਗ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਦਿੰਦੀ ਹੈ ਜਿਸ ਨੂੰ ਬ੍ਰਾਊਜ਼ਰ ਇੱਕੋ origin ਨਾਲ ਸਬੰਧਤ ਸਾਰੇ ਸੰਦਰਭਾਂ (contexts) ਵਿੱਚ ਲਾਗੂ ਕਰਦਾ ਹੈ। ਜੇਕਰ ਲੌਕ ਪਹਿਲਾਂ ਹੀ ਕਿਸੇ ਕੋਲ ਹੈ, ਤਾਂ ਦੂਜੇ ਕਾਲਰ ਉਦੋਂ ਤੱਕ ਕਿਊ ਵਿੱਚ ਰਹਿਣਗੇ ਜਦੋਂ ਤੱਕ ਲੌਕ ਧਾਰਕ ਇਸਨੂੰ ਛੱਡ ਨਹੀਂ ਦਿੰਦਾ ਜਾਂ ਬ੍ਰਾਊਜ਼ਰ ਇਸਨੂੰ ਰੱਦ ਨਹੀਂ ਕਰ ਦਿੰਦਾ (ਉਦਾਹਰਨ ਲਈ, ਜਦੋਂ ਟੈਬ ਕ੍ਰੈਸ਼ ਹੋ ਜਾਂਦਾ ਹੈ)। ਕੋਈ ਬਾਹਰੀ ਸਰਵਰ ਨਹੀਂ, ਕੋਈ polling ਨਹੀਂ, ਸਿਰਫ਼ ਨੇਟਿਵ ਬ੍ਰਾਊਜ਼ਰ ਤਾਲਮੇਲ।
ਲੌਕ-ਅਧਾਰਤ ਰਿਫ੍ਰੈਸ਼ ਫਲੋ (lock-based refresh flow) ਨੂੰ ਲਾਗੂ ਕਰਨਾ
- 401 ਦਾ ਪਤਾ ਲਗਾਓ – Axios interceptor ਪਹਿਲਾਂ ਵਾਂਗ ਹੀ ਅਣਅਧਿਕਾਰਤ (unauthorized) ਰਿਸਪਾਂਸ ਨੂੰ ਫੜ ਲੈਂਦਾ ਹੈ।
- ਇੱਕ exclusive ਲੌਕ ਦੀ ਮੰਗ ਕਰੋ – ਟੈਬ
navigator.locks.request('auth_token_refresh_lock', async lock => { … })ਨੂੰ ਕਾਲ ਕਰਦਾ ਹੈ। ਇੱਕ ਸਮੇਂ ਵਿੱਚ ਸਿਰਫ਼ ਇੱਕ ਹੀ ਟੈਬ ਕਾਲਬੈਕ (callback) ਵਿੱਚ ਦਾਖਲ ਹੋ ਸਕਦਾ ਹੈ। - ਨੈੱਟਵਰਕ 'ਤੇ ਜਾਣ ਤੋਂ ਪਹਿਲਾਂ ਦੁਬਾਰਾ ਚੈੱਕ ਕਰੋ – ਲੌਕ ਦੇ ਅੰਦਰ,
localStorageਤੋਂ ਇੱਕ ਟਾਈਮਸਟੈਂਪ (ਜਾਂ ਖੁਦ ਟੋਕਨ) ਪੜ੍ਹੋ। ਜੇਕਰ ਟਾਈਮਸਟੈਂਪ ਕੁਝ ਸੈਕਿੰਡਾਂ ਤੋਂ ਘੱਟ ਪੁਰਾਣਾ ਹੈ, ਤਾਂ ਕਿਸੇ ਹੋਰ ਟੈਬ ਨੇ ਪਹਿਲਾਂ ਹੀ ਟੋਕਨ ਰਿਫ੍ਰੈਸ਼ ਕਰ ਲਿਆ ਹੈ; ਮੌਜੂਦਾ ਟੈਬ ਨੈੱਟਵਰਕ ਕਾਲ ਨੂੰ ਛੱਡ ਦਿੰਦਾ ਹੈ ਅਤੇ ਸਿਰਫ਼ ਸਟੋਰੇਜ ਤੋਂ ਨਵਾਂ JWT ਪੜ੍ਹਦਾ ਹੈ। - ਜੇਕਰ ਲੋੜ ਹੋਵੇ ਤਾਂ ਰਿਫ੍ਰੈਸ਼ ਕਰੋ – ਜੇਕਰ ਸਟੋਰਡ ਟਾਈਮਸਟੈਂਪ ਪੁਰਾਣਾ ਹੈ, ਤਾਂ ਰਿਫ੍ਰੈਸ਼ ਰਿਕੁਐਸ ਭੇਜੋ,
localStorageਵਿੱਚ ਨਵਾਂ ਟੋਕਨ ਅਤੇ ਮੌਜੂਦਾ ਸਮਾਂ ਸਟੋਰ ਕਰੋ, ਫਿਰ ਕਾਲਬੈਕ ਤੋਂ ਵਾਪਸ ਮੁੜ ਕੇ ਲੌਕ ਨੂੰ ਰਿਲੀਜ਼ ਕਰੋ। - ਕਿਊ ਕੀਤੀਆਂ ਰਿਕੁਐਸਾਂ ਨੂੰ ਜਾਰੀ ਰੱਖੋ – ਬਾਕੀ ਸਾਰੇ ਟੈਬ ਜੋ ਉਡੀਕ ਕਰ ਰਹੇ ਸਨ, ਇੱਕ ਤੋਂ ਬਾਅਦ ਇੱਕ ਲੌਕ ਪ੍ਰਾਪਤ ਕਰਦੇ ਹਨ, ਤਾਜ਼ਾ ਟਾਈਮਸਟੈਂਪ ਦੇਖਦੇ ਹਨ, ਅਤੇ ਬਿਨਾਂ ਕੋਈ ਹੋਰ ਰਿਕੁਐਸ ਕੀਤੇ ਮੁਕੰਮਲ ਕਰ ਲੈਂਦੇ ਹਨ।
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());
});
}
ਇਹ ਪੈਟਰਨ ਗਾਰੰਟੀ ਦਿੰਦਾ ਹੈ ਕਿ ਕਿੰਨੇ ਵੀ ਟੈਬ ਖੁੱਲ੍ਹੇ ਹੋਣ, ਸਿਰਫ਼ ਇੱਕ ਹੀ ਰਿਫ੍ਰੈਸ਼ ਰਿਕੁਐਸ ਸਰਵਰ ਤੱਕ ਪਹੁੰਚਦੀ ਹੈ।
ਲਾਭ ਜਿਨ੍ਹਾਂ ਨੂੰ ਤੁਸੀਂ ਮਾਪ ਸਕਦੇ ਹੋ
- ਨੈੱਟਵਰਕ ਕੁਸ਼ਲਤਾ (Network efficiency) – ਇੱਕ ਰਿਕੁਐਸ ਪੰਜ ਦੀ ਜਗ੍ਹਾ ਲੈ ਲੈਂਦੀ ਹੈ, ਜਿਸ ਨਾਲ ਬੈਂਡਵਿਡਥ ਅਤੇ ਸਰਵਰ ਲੋਡ ਵਿੱਚ ਭਾਰੀ ਕਮੀ ਆਉਂਦੀ ਹੈ।
- ਸੈਸ਼ਨ ਸੁਰੱਖਿਆ (Session safety) – Refresh Token Rotation ਦੇ ਨਾਲ, ਸਰਵਰ ਪੁਰਾਣੇ ਰਿਫ੍ਰੈਸ਼ ਟੋਕਨ ਦੀ ਸਿਰਫ਼ ਇੱਕ ਵਾਰ ਵਰਤੋਂ ਦੇਖਦਾ ਹੈ, ਇਸ ਲਈ ਇਹ ਕਦੇ ਵੀ ਸੈਸ਼ਨ ਨੂੰ ਖਤਰੇ ਵਿੱਚ (compromised) ਨਹੀਂ ਮੰਨਦਾ।
- ਲਚਕਤਾ (Resilience) – ਜੇਕਰ ਲੌਕ ਫੜਨ ਵਾਲਾ ਟੈਬ ਕ੍ਰੈਸ਼ ਹੋ ਜਾਂਦਾ ਹੈ, ਤਾਂ ਬ੍ਰਾਊਜ਼ਰ ਆਪਣੇ ਆਪ ਲੌਕ ਨੂੰ ਰਿਲੀਜ਼ ਕਰ ਦਿੰਦਾ ਹੈ, ਜਿਸ ਨਾਲ ਡੈੱਡਲੌਕ (deadlock) ਤੋਂ ਬਚਿਆ ਜਾ ਸਕਦਾ ਹੈ ਜੋ ਹੋਰਨਾਂ ਟੈਬਾਂ ਨੂੰ ਰੋਕ ਸਕਦਾ ਸੀ।
- ਸਕੇਲੇਬਿਲਟੀ (Scalability) – ਉਪਭੋਗਤਾ ਕੈਸਕੇਡ ਲੌਗਆਊਟ (cascade logout) ਦੇ ਜੋਖਮ ਤੋਂ ਬਿਨਾਂ ਦਰਜਨਾਂ ਟੈਬ ਖੋਲ੍ਹ ਸਕਦੇ ਹਨ, ਕਿਉਂਕਿ ਤਾਲਮੇਲ ਬ੍ਰਾਊਜ਼ਰ ਦੇ ਅੰਦਰ ਹੀ ਰਹਿੰਦਾ ਹੈ।
ਦੂਜਾ ਪਹਿਲੂ: ਬ੍ਰਾਊਜ਼ਰ ਸਪੋਰਟ ਅਤੇ ਫਾਲਬੈਕਸ (fallbacks)
Web Locks API ਇੱਕ ਕਾਫ਼ੀ ਨਵਾਂ ਫੀਚਰ ਹੈ। ਆਧੁਨਿਕ Chromium-ਅਧਾਰਤ ਬ੍ਰਾਊਜ਼ਰ ਅਤੇ Firefox ਦੇ ਹਾਲੀਆ ਵਰਜ਼ਨ ਇਸਨੂੰ ਲਾਗੂ ਕਰਦੇ ਹਨ, ਪਰ ਪੁਰਾਣੇ ਬ੍ਰਾਊਜ਼ਰਾਂ ਅਤੇ Safari ਵਿੱਚ ਇਸਦਾ ਨੇਟਿਵ ਸਪੋਰਟ ਨਹੀਂ ਹੈ। ਜਿਹੜੇ ਵਾਤਾਵਰਣਾਂ ਵਿੱਚ API ਉਪਲਬਧ ਨਹੀਂ ਹੈ, ਉੱਥੇ ਡਿਵੈਲਪਰਾਂ ਨੂੰ ਘੱਟ ਭਰੋਸੇਯੋਗ ਤਕਨੀਕਾਂ ਦੀ ਵਰਤੋਂ ਕਰਨੀ ਪਵੇਗੀ—ਜਿਵੇਂ ਕਿ localStorage ਰਾਹੀਂ ਇੱਕ ਕਸਟਮ ਈਵੈਂਟ ਪ੍ਰਸਾਰਿਤ ਕਰਨਾ ਜਾਂ shared worker ਦੀ ਵਰ
- ਮਿਆਰੀਕਰਨ ਦੀ ਪ੍ਰਗਤੀ – API ਦੇ ਅਪਣਾਉਣ ਦੇ ਕਰਵ (adoption curve) 'ਤੇ ਨਜ਼ਰ ਰੱਖੋ; ਵਿਆਪਕ ਸਹਾਇਤਾ ਕਿਸੇ ਵੀ ਕਰਾਸ-ਟੈਬ ਤਾਲਮੇਲ ਲਈ ਲੌਕ-ਅਧਾਰਤ ਪਹੁੰਚ ਨੂੰ ਡਿਫੌਲਟ ਬਣਾ ਦੇਵੇਗੀ।
- Library wrappers – ਕੁਝ ਓਪਨ-ਸੋਰਸ ਯੂਟੀਲਿਟੀਜ਼ ਪਹਿਲਾਂ ਹੀ ਲੌਕ ਰਿਕਵੈਸਟ ਪੈਟਰਨ ਨੂੰ ਐਬਸਟਰੈਕਟ ਕਰ ਰਹੀਆਂ ਹਨ, ਜਿਸ ਨਾਲ ਮੌਜੂਦਾ Axios interceptors ਵਿੱਚ ਇਸਨੂੰ ਜੋੜਨਾ ਆਸਾਨ ਹੋ ਜਾਂਦਾ ਹੈ।
- ਸੁਰੱਖਿਆ ਆਡਿਟ – ਹਾਲਾਂਕਿ ਲੌਕ ਕੰਕਰੈਂਸੀ (concurrency) ਦੀ ਸਮੱਸਿਆ ਨੂੰ ਹੱਲ ਕਰਦਾ ਹੈ, ਫਿਰ ਵੀ ਰਿਫ੍ਰੈਸ਼ ਐਂਡਪੁਆਇੰਟ ਨੂੰ ਸਹੀ ਟੋਕਨ ਰੋਟੇਸ਼ਨ ਅਤੇ ਰੇਟ ਲਿਮਿਟਿੰਗ ਨੂੰ ਲਾਗੂ ਕਰਨਾ ਚਾਹੀਦਾ ਹੈ, ਕਿਉਂਕਿ ਜੇਕਰ ਲੌਕ ਨੂੰ ਬਾਈਪਾਸ ਕਰ ਦਿੱਤਾ ਜਾਂਦਾ ਹੈ, ਤਾਂ ਇੱਕ ਹਾਨੀਕਾਰਕ ਟੈਬ ਅਜੇ ਵੀ ਸਰਵਰ ਨੂੰ ਰਿਕਵੈਸਟਾਂ ਨਾਲ ਭਰ ਸਕਦਾ ਹੈ।
ਸਿੱਟਾ ਸਧਾਰਨ ਹੈ: ਖੁੱਲ੍ਹੇ ਹੋਏ ਟੈਬਾਂ ਦੇ ਸਮੂਹ ਨੂੰ ਇੱਕ ਡਿਸਟ੍ਰੀਬਿਊਟਿਡ ਸਿਸਟਮ ਵਜੋਂ ਮੰਨੋ ਅਤੇ ਉਹਨਾਂ ਨੂੰ ਇੱਕ ਨੇਟਿਵ ਸਿੰਕ੍ਰੋਨਾਈਜ਼ੇਸ਼ਨ ਪ੍ਰਿਮੇਟਿਵ ਦਿਓ। Web Locks API ਨੂੰ ਟੋਕਨ-ਰਿਫ੍ਰੈਸ਼ ਫਲੋਅ ਵਿੱਚ ਜੋੜ ਕੇ, ਡਿਵੈਲਪਰ "ਬ੍ਰਾਊਜ਼ਰ ਟੈਬ ਟੋਕਨ ਟ੍ਰੈਪ" ਨੂੰ ਖਤਮ ਕਰਦੇ ਹਨ ਅਤੇ ਉਪਭੋਗਤਾਵਾਂ ਨੂੰ ਲੌਗਇਨ ਰੱਖਦੇ ਹਨ, ਚਾਹੇ ਉਹ ਕਿੰਨੇ ਵੀ ਟੈਬਾਂ ਦੀ ਵਰਤੋਂ ਕਰ ਰਹੇ ਹੋਣ।
