पाँच गलत पासवर्ड खाते को लॉक कर देते हैं। जाँच सही थी, सीमा सही थी, फिर भी आप दस हज़ार गलत पासवर्ड भेज सकते थे और वह कभी नहीं छिड़ता, बशर्ते आप उन्हें एक ही बार में भेजें। काउंटर बिना सुरक्षा के एक पढ़ो-बदलो-लिखो था, इसलिए समवर्ती प्रयास एक-दूसरे की वृद्धियों को अधिलेखित कर देते और वह मुश्किल से चढ़ता। समाधान: बिना वृद्धि संक्रिया वाले की-वैल्यू स्टोर पर परमाणविक काउंटर कैसे बनाएँ, और इसके साथ आई पुनर्रचना: मान लें कि कोई समानांतर में गिन रहा है।
सिंगल साइन ऑन बीस साल से भी ज़्यादा समय से तय हो चुका मानक है, फिर भी SAML आज भी वही मद है जो सौदा अटका देती है या सेल्स को फ़ोन करवा देती है। एंटरप्राइज़ ऑथ महँगा अपसेल कैसे बना, वह दुनिया किसने बनाई और किसने हमें चेताया था, यह खंदक असल में कितनी चौड़ी है और किस चीज़ से बनी है, और वह कौन सी ताक़त है जो चुपचाप उसका आधा हिस्सा सुखा रही है।
मॉनिटरिंग वह कोड है जो केवल किसी आपदा के दौरान चलता है, वही एक क्षण जब उसका ख़राब होना आप सह नहीं सकते। इसलिए हमने हार्नेस बनाया जो हर अलर्ट नियम के लिए वास्तविक संकेत भेजता है और तब तक पास नहीं होता जब तक सूचना किसी कैप्चर रिले में न पहुँच जाए। पहली रन ने हमारे प्रमुख त्रुटि-दर अलर्ट को मरा हुआ पाया, फिर उससे भी बुरा: रिटेंशन सेटिंग ने चुपचाप हमारे लॉग्स पर तुरंत समाप्ति की मुहर लगा दी, जिससे अलर्ट की पूरी श्रेणी शून्य पर मूल्यांकन करती रही जबकि इंजेशन स्वस्थ दिख रहा था।
सौ मेगाबाइट दस्तावेज़-स्क्रीनशॉट Cloudflare R2 पर चले गए, एक CDN बेस के पीछे जिसे ऐप रनटाइम पर अपने होस्टनेम से निकालता है। यह ब्राउज़र में चला पर ऑरिजिन के ट्रैफ़िक पर असर नहीं पड़ा, क्योंकि क्रॉलर के पेज 127.0.0.1 पर प्रीरेंडर होते हैं, जहाँ होस्टनेम कुछ नहीं बताता। हल एक प्लेसहोल्डर है जिसे HTML बिल्ड से बाहर ले जाता है और एज हर रिक्वेस्ट पर भरता है, और वजह एक रीड-ओनली फ़ाइलसिस्टम है।
जस्ट-इन-टाइम प्रोविज़निंग डिफ़ॉल्ट रूप से चालू थी, इसलिए कोई फ़ेडरेटेड आइडेंटिटी प्रोवाइडर बस यह दावा करके कि कोई व्यक्ति मौजूद है, ग्राहक के tenant में खाते बना सकता था। उस डिफ़ॉल्ट को बंद करना एक शब्द का बदलाव है। पर उसे यूँ बंद करना कि पहले से संग्रहित हर कनेक्शन का व्यवहार चुपचाप न बदले, जबकि किसी ने वह column कभी लिखा ही नहीं, असली समस्या है, और जिस तरकीब ने इसे हल किया वही बताती है कि डिफ़ॉल्ट API सतह क्यों है।
एक backend-for-frontend, OAuth टोकन को ब्राउज़र से बाहर निकाल देता है और उसके हाथ में एक अपारदर्शी कुकी के सिवा कुछ नहीं छोड़ता। एक ही BFF से कई टेनेंट को सेवा देना ज़्यादातर पाइपलाइन बिछाने जैसी समस्या है, जब तक बैक-चैनल लॉगआउट नहीं आता: एक सर्वर-से-सर्वर POST, जिसमें न कुकी है, न सत्र, न ब्राउज़र, और टेनेंट की पहचान बताने वाली एकमात्र चीज़ एक ऐसे टोकन के भीतर मौजूद दावा है जिसे आपने अभी तक सत्यापित नहीं किया है।
हमने अपने Kubernetes क्लस्टर में एक हस्ताक्षर-जाँचने वाला एडमिशन वेबहुक जोड़ा ताकि बिना हस्ताक्षर वाली इमेज कभी न चल सकें। कुछ घंटे बाद एक Deployment के पास 2,242 ReplicaSet थे, हर तीन सेकंड में एक नया, और कोई उपलब्ध पॉड नहीं। वेबहुक के लॉग दिखा रहे थे कि वह हर एक अनुरोध को एडमिट कर रहा है। नुकसान "mutating admission webhook" के उस एक शब्द से आया जिसे हमने पढ़ना बंद कर दिया था।
एक लौटता हुआ उपयोगकर्ता हर बार लॉगिन दिखने से पहले ठीक दस सेकंड तक "Loading…" देखता रहता था। गोल संख्या ही असली सुराग थी: हमारी SSO लाइब्रेरी ने एक छिपा हुआ silent-renew iframe उस auth होस्ट के विरुद्ध खोला जो X-Frame-Options: DENY और frame-ancestors 'none' परोसता है, इसलिए वह फ़्रेम कभी लोड नहीं हुआ, कभी त्रुटि नहीं दी, और लाइब्रेरी अपने पूरे टाइमआउट के ख़त्म होने तक इंतज़ार करती रही। क्लिकजैकिंग बचाव और OIDC silent renew, दोनों अपने-अपने में सही, उनके बीच की सीवन में अटक गए।
हमने हर email address को at rest encrypt किया, और admin search box ने चुपचाप autocomplete करना बंद कर दिया। Blind index ciphertext पर exact-match lookup लौटाता है, लेकिन HMAC का काम ही वह ordering नष्ट करना है जो prefix search को चाहिए। यह रहा वह design जिसने type-ahead वापस दिलाया, और तीन traps जिन्होंने सिखाया कि searchable encryption एक systems problem है, crypto problem नहीं।
हमने JWT signing को RS256 से ES256 पर ले जाया और हमारा जारी किया हर टोकन क्रिप्टोग्राफ़िक रूप से वैध था, और फिर भी अस्वीकृत। तीन बार, तीन अलग-अलग वजहों से: वहाँ DER जहाँ JWS को raw R‖S चाहिए, integer padding जो हर 128 में से एक टोकन पर फेल होती है, और एक base64 बोली जो हमारे पैरों तले बदल गई। यहाँ बताया गया है कि बिगड़े हुए बाइट्स को कैसे पढ़ें और केवल सिग्नेचर की लंबाई से दोषी लेयर का नाम कैसे बताएँ।
हमारे किसी JWT को validate करने के लिए कोई service दो दस्तावेज़ पढ़ती है — discovery doc और JWKS — और यही वे चीज़ें हैं जिनकी सबसे ज़्यादा माँग होती है और जो सबसे कम गोपनीय हैं, फिर भी हर fetch पहले पूरा सफ़र तय करके वापस origin तक आती थी। इन्हें Cloudflare पर cache करना तो आसान हिस्सा था। मुश्किल हिस्सा यह है कि एक बासी public-key cache को कभी भी किसी वैध token को अस्वीकार नहीं करना चाहिए — और यहाँ वह rotation-safe क्रम है जो एक आक्रामक ढंग से cache किए गए JWKS को सुरक्षित बनाता है।
Duende का LITE टियर $5,750/yr का है, लेकिन यह सिर्फ 2 client apps तक सीमित है और इसमें SAML बिल्कुल नहीं आता। जिस पल आपको enterprise SSO या तीसरे app की ज़रूरत पड़ती है, आप $12,500 वाले STANDARD पर पहुँच जाते हैं। एक नज़र इस बात पर कि Duende असल में किसी वास्तविक product को कितना महँगा पड़ता है, और उस client-count टैक्स पर जिसके बारे में कोई आगाह नहीं करता।
हमारा auth server जो भी token जारी करता है, उस हर token पर एक ही private key हस्ताक्षर करती है, और अपने जीवन में यह दो बार अपनी जगह बदल चुकी है: जन्म हुआ process के भीतर, फिर निर्वासन एक ऐसे vault में जहाँ से यह निकल नहीं सकती, और फिर पुनर्जन्म एक बिल्कुल छोटी और तेज़ किस्म की key के रूप में। हर बदलाव एक जीवित issuer पर हुआ, जब token हवा में उड़ रहे थे।
आपके SP ने assertion signature validate की, certificate pin किया, और user को login करा दिया। हर check pass हुआ। फिर भी मैंने एक घंटे पहले capture किया हुआ login दोबारा replay कर दिया। ट्रिक signature तोड़ने की नहीं थी। ट्रिक थी उस field को delete करने की जिसे signature ने कभी cover ही नहीं किया।
हमने login page पर second factor का challenge दिया, लेकिन token endpoint पर उसे दोबारा कभी नहीं जांचा। नतीजा यह कि एक सही password और हाथ से एडिट किया गया return URL सीधे MFA के पार निकल गया। फिक्स, और उससे मिला नियम: MFA उस session की property है जो आप issue करते हैं, login screen का कोई step नहीं।
SCIM एक standardized CRUD API है जिसकी सबसे अहम verb वही है जो किसी पूर्व-कर्मचारी का अकाउंट बंद करती है। वेंडर इसकी कीमत $125 प्रति माह प्रति कनेक्शन रखते हैं, या इसे SSO के बगल में एंटरप्राइज़ टियर में बिठा देते हैं। यहाँ बताया गया है कि एक SCIM कनेक्शन को सर्व करने की असली लागत क्या है, और डीप्रोविज़निंग के पैसे लेना दरअसल सुरक्षा के पैसे लेना क्यों है।
Azure Table Storage और DynamoDB में delete markers नहीं होते, इसलिए डिलीट की गई row बस ग़ायब हो जाती है, जिसका मतलब है कि आपका incremental बैकअप उस delete को कभी देखता ही नहीं। वह बैकअप restore कीजिए और जिस यूज़र को आपने ऑफ़बोर्ड किया था वह वापस आ जाता है, उसी पासवर्ड के साथ जिसे आपने बदलकर पीछे छोड़ दिया था। यहाँ बताया गया है कि deletes को first-class क्यों होना चाहिए, और वह एक टेबल कौन-सी है जो उन्हें ऐसा बनाती है।
हमारा rate limiter और lockout counter client IP पर keyed थे। nginx के पीछे वो IP एक ऐसे header से आता था जिसे client खुद set कर सकता था, तो हर request पर header घुमाते ही हर request को नया IP मिल जाता और हर per-IP control धराशायी हो जाता। वो one-line spoof, और वो fix जो Kubernetes पर दिखने से कहीं ज़्यादा पेचीदा है।
Azure Table Storage आपको एक partition key, एक row key, और लगभग इसके अलावा कुछ नहीं देता। कोई joins नहीं, नाम लायक कोई secondary index नहीं, कोई increment नहीं। फिर भी हम उस पर एक multi-tenant auth system चलाते हैं। वे patterns जो इसे काम करने लायक बनाते हैं, और SQL की तुलना में आप क्या खोते हैं।
बिना per-email rate limit वाला एक unauthenticated पासवर्ड-रीसेट endpoint सिर्फ आपकी समस्या नहीं है। इसे किसी और के इनबॉक्स की तरफ ताक दीजिए, और आपने उनके रीसेट फॉर्म को एक स्पैम तोप में बदल दिया, जिसका बिल आपकी sender reputation पर चढ़ता है। इसका फिक्स, वो भी बिना यह लीक किए कि किन ईमेल के अकाउंट हैं।
ज़्यादातर auth वेंडर SSO, SCIM, MFA और audit logs को Enterprise प्लान के पीछे बंद रखते हैं। हमने ये सब $0 टियर पर रख दिए। फीचर मुफ्त बांटना जिस unit economics की वजह से टिकाऊ है, वह यह है: दीवार कभी फीचर्स थी ही नहीं, दीवार है active users और support।
सिंगल साइन-ऑन का मतलब है पहचान प्रदाता पर भरोसा करना। हमने उस पर एक चीज़ का भरोसा कुछ ज़्यादा ही कर लिया: हम लौटने वाले फ़ेडरेटेड उपयोगकर्ताओं को assertion में मौजूद ईमेल से हल करते थे, सो कोई भी कनेक्शन किसी और का पता बता सकता था और उसके खाते पर जा पहुँचता था। हल और जाँच लगाना नहीं था। हल था पहचान की जोड़ने वाली कुंजी को ईमेल से बदलकर प्रति-प्रदाता subject कर देना, जिसे दावा करने वाला पक्ष जाली नहीं बना सकता।
Azure Container Apps ने वादा किया था कि हमें एक निष्क्रिय ऑथ क्लस्टर के लिए भुगतान नहीं करना पड़ेगा। हमने माइग्रेशन की लागत आँकी और उसे रद्द कर दिया, कीमत या कोल्ड स्टार्ट की वजह से नहीं, बल्कि इसलिए कि सर्वरलेस में वही एक संख्या मौजूद नहीं है जिसकी लीडर-चुनाव वाले ऑथ बैकप्लेन को वाकई ज़रूरत है।
"फ़ीचर टैक्स" को एक असली बिल के रूप में देखें। खरीदार के तीन परिदृश्य (पहला enterprise सौदा, मुट्ठी भर ग्राहक, SSO का अनिवार्य बन जाना) हर वेंडर के सार्वजनिक रेट के विरुद्ध आँके गए। साथ ही वह चुपचाप लगने वाला अधिभार जिसकी कोई चेतावनी नहीं देता: इंटीग्रेशन के बीच में OAuth क्लाइंट खत्म हो जाना।
तीन रेप्लिका वाला एक क्लस्टर लगातार खुद से ही असहमत रहता था। वजह हमारे अपने कोड में थी: पीयर-डिस्कवरी रूटीन में एक catch ब्लॉक था जो multicast के विफल होने पर डिस्कवरी को बंद कर देता था, और मैनेज्ड Kubernetes पर multicast हमेशा विफल होता है। यह कहानी है कि हमने gossip को पूरी तरह क्यों हटा दिया और उसकी जगह blob lease से एक leader क्यों चुना, और क्यों वह मेंबरशिप जिसे आप Storage Explorer में खोलकर देख सकते हैं, उस मेंबरशिप से बेहतर है जिसका आपको अनुमान लगाना पड़े।
लॉगिन एक वीकेंड का काम है। बिल बाद में आता है: सिग्नेचर वेरिफ़िकेशन, SCIM डिप्रोविज़निंग, MFA, ऑडिट लॉग, key रोटेशन, ब्रीच की देनदारी, और एक tier-zero सिस्टम के लिए ऑन-कॉल। एक ईमानदार बनाएँ-बनाम-ख़रीदें, यह भी कि कब बनाना सही है।
OIDC या SAML? चुनने का मौका आपको शायद ही मिलता है। हर प्रोटोकॉल क्या है, कब कौन जीतता है, वे XML-सिग्नेचर वाले जोखिम जो SAML को खुद बनाना खतरनाक बना देते हैं, और क्यों एक B2B प्रोडक्ट को आखिरकार दोनों की ज़रूरत पड़ती है।
Authagonal का सपोर्ट सिस्टम: बिना रिफ़्रेश के रियल-टाइम मैसेजिंग, ग्राहक के पहले मैसेज से ही पिन की गई पारदर्शी दो-तरफ़ा AI अनुवाद, और एक ऐसी थ्रेड जो ईमेल पर भी पूरी तरह अनूदित रहती है। सरल, खूबसूरत, और सचमुच सक्षम।
हमारे दो टेनेंट चुपचाप एक ही टेनेंट थे: एक ही डेटाबेस, एक ही टोकन-साइनिंग की, अलग-अलग नाम। वजह थी एक एक-लाइन का हेल्पर जो स्लग को हाइफ़न हटाकर सैनिटाइज़ करता था, जिससे एक यूनीक पहचानकर्ता गैर-यूनीक बन जाता था। यह रही कहानी कि कैसे एक लॉसी स्ट्रिंग ट्रांसफ़ॉर्म एक क्रॉस-टेनेंट टेकओवर बन गया, और क्यों हमने इसे आपसे पहले पकड़ लिया।
हम ऑथेंटिकेशन बेचते हैं, इसलिए हमारा सबसे महत्वपूर्ण टेस्ट वही है जो एक ग्राहक बन जाता है: एक अकेला Playwright रन जो एक टेनेंट साइन अप करता है, हर फ़ीचर कॉन्फ़िगर करता है, उस पर एक असली ऐप को इंगित करता है, सही क्लेम्स के साथ एक असली यूज़र को लॉग इन करवाता है, फिर बैकअप लेता है, रिस्टोर करता है और खुद को डिलीट कर देता है। कैसे संपूर्ण एंड-टू-एंड टेस्टिंग ने हमें गो-लाइव का भरोसा दिलाया।
हमारा स्टाफ़ कंसोल खुद Authagonal के ज़रिए ऑथेंटिकेट करता है, जिसमें प्रोविज़निंग का काम Entra SSO और SCIM करते हैं। अपना ही ग्राहक होना उन किनारे के मामलों को सामने ले आता है जिन्हें लिखने का ख़याल किसी टेस्ट सूट को नहीं आता। यहाँ वे हैं जो मायने रखते थे।
आपकी Terraform स्टेट आपके पूरे क्लाउड का नक़्शा है, साथ ही सीक्रेट्स का एक ढेर भी, और डिफ़ॉल्ट रूप से यह एक सार्वजनिक एंडपॉइंट के पीछे रहती है। यह रहा कि कैसे हमने शून्य से रिमोट स्टेट को बूटस्ट्रैप किया, उसे एक प्राइवेट एंडपॉइंट के पीछे रखा, और VPN गेटवे का टैक्स चुकाए बिना एक ज़ीरो-ट्रस्ट कनेक्टर के ज़रिए उस तक पहुँचे, बिना कभी खुद को बाहर ताला लगाए।
"हर कोई अपना पासवर्ड रीसेट करता है" एक माइग्रेशन का चुनाव है, भौतिकी का कोई नियम नहीं। आइडेंटिटी-प्रोवाइडर बदलते समय अपने यूज़र के मौजूदा पासवर्ड हैश को कैसे साथ ले जाएँ ताकि कटओवर अदृश्य रहे, और वह एक मामला जहाँ आप सचमुच नहीं ले जा सकते।
Auth उद्योग अनुमति को मीटर करता है, लागत को नहीं। यह SSO, MFA, और provisioning को टियरों और per-connection शुल्कों के पीछे रोक देता है, ऐसे फ़ीचर्स के लिए जिन्हें देने में असल में कोई लागत नहीं आती। यहाँ वह फ़र्क़ है जो pricing pages चाहती हैं कि आप कभी न समझें।
सेल्फ़-होस्टेड Duende IdentityServer से managed auth की ओर बढ़ना — SQL config और ASP.NET Identity से असल में क्या माइग्रेट होता है, और वह ऑपरेशनल बोझ जिसे ढोना आप बंद कर देते हैं।
Auth0 माइग्रेशन में असल में क्या शामिल होता है, इस पर एक व्यावहारिक नज़र: क्या आसान है, क्या झंझट भरा है, और वह एक चीज़ जो Auth0 सचमुच आपको नहीं देगा।