oauth2-proxy का उपयोग करके किसी भी ऐप में SSO कैसे जोड़ें
बिना लॉगिन वाले ऐप्स को OIDC के पीछे सुरक्षित करें। Nginx या Traefik के साथ oauth2-proxy कॉन्फ़िगर करें और कुकी या रीडायरेक्ट की समस्याओं से बचने के सटीक तरीके यहाँ जानें।
Forward auth: बिना लॉगिन वाले एप्लिकेशन के लिए SSO कैसे प्राप्त करें
oauth2-proxy ऐसे एप्लिकेशन को सिंगल साइन-ऑन (SSO) प्रदान करता है जिसमें अपना कोई लॉगिन सिस्टम नहीं होता। यह इस तरह काम करता है कि एप्लिकेशन के सामने स्थित रिवर्स प्रॉक्सी हर रिक्वेस्ट को रोकती है, oauth2-proxy से पूछती है कि क्या रिक्वेस्ट में एक वैध सेशन है, और केवल तभी रिक्वेस्ट को अपस्ट्रीम भेजती है जब उत्तर 'हाँ' हो। एप्लिकेशन कोड में कभी कोई बदलाव नहीं करना पड़ता, क्योंकि एप्लिकेशन को इस जांच का पता ही नहीं चलता।
यह जांच एक अतिरिक्त HTTP रिक्वेस्ट है। प्रॉक्सी आने वाली रिक्वेस्ट के हेडर्स की एक कॉपी /oauth2/auth को भेजती है और स्टेटस कोड पढ़ती है। 202 का मतलब है कि कॉलर के पास एक सेशन है, इसलिए प्रॉक्सी ओरिजिनल रिक्वेस्ट को ऐप तक फॉरवर्ड कर देती है। 401 का मतलब है कि कोई सेशन नहीं है, इसलिए प्रॉक्सी ब्राउज़र को /oauth2/sign_in पर भेज देती है, जो आपके आइडेंटिटी प्रोवाइडर पर OpenID Connect (OIDC) लॉगिन शुरू करता है। OIDC, OAuth 2.0 के ऊपर बनी आइडेंटिटी लेयर है, और प्रोवाइडर वह है जिसे आप लॉगिन के लिए पहले से उपयोग कर रहे हैं।
इस पैटर्न का हर रिवर्स प्रॉक्सी में एक नाम है। Nginx इस डायरेक्टिव को auth_request कहता है। Traefik इसे forwardAuth मिडिलवेयर कहता है। Caddy इसे forward_auth के रूप में लिखता है। सब-रिक्वेस्ट का उत्तर देने वाली सर्विस भी बदली जा सकती है। oauth2-proxy एक सामान्य विकल्प है क्योंकि यह सीधे OIDC का उपयोग करता है और इसे अपने स्वयं के डेटाबेस की आवश्यकता नहीं होती है।
कॉन्फ़िगरेशन लिखने से पहले ट्रस्ट बाउंड्री (trust boundary) निर्धारित करें
सफल जाँच के बाद, oauth2-proxy पहचान को रिस्पॉन्स हेडर के रूप में लौटाता है, और रिवर्स प्रॉक्सी उन्हें अपस्ट्रीम रिक्वेस्ट पर कॉपी कर देता है। set_xauthrequest सक्षम होने पर आपको X-Auth-Request-User और X-Auth-Request-Email मिलते हैं। एप्लिकेशन इन हेडर्स को पढ़ता है और उन पर भरोसा करता है।
यही पूरा सुरक्षा मॉडल है, इसलिए इसके परिणाम को स्पष्ट रूप से समझें। जो कोई भी एप्लिकेशन पोर्ट पर TCP कनेक्शन खोल सकता है, वह स्वयं उन हेडर्स को सेट कर सकता है और कोई भी उपयोगकर्ता बन सकता है। यदि कोई रिक्वेस्ट सीधे एप्लिकेशन तक पहुँचती है, तो एक एकल curl -H "X-Auth-Request-Email: admin@example.com" http://app-host:3000/ सुरक्षा का पूर्ण उल्लंघन है।
इसलिए, एप्लिकेशन प्रॉक्सी के अलावा कहीं और से एक्सेस नहीं होना चाहिए। Docker Compose में, ऐप सर्विस से ports: मैपिंग हटा दें और उसे केवल इंटरनल नेटवर्क पर रखें, ताकि केवल प्रॉक्सी कंटेनर ही उससे कनेक्ट हो सके। बेयर होस्ट पर, ऐप को 0.0.0.0:3000 के बजाय 127.0.0.1:3000 पर बाइंड करें। फिर जाँचें कि आपने वास्तव में क्या एक्सपोज़ किया है:
sudo ss -tlnp | grep 30000.0.0.0:3000 वाली लाइन का मतलब है कि ऐप पब्लिक IP पर जवाब दे रहा है और आपका गेट केवल दिखावटी है। 127.0.0.1:3000 वह है जो आपको चाहिए। फ़ायरवॉल नियम सुरक्षा की एक उपयोगी दूसरी परत है, लेकिन बाइंड एड्रेस वह है जो आपके रूल्सेट को किसी अन्य टूल द्वारा फ्लश किए जाने पर भी सुरक्षित रहता है।
oauth2-proxy इंस्टॉल करें
अगस्त 2026 तक, वर्तमान release v7.15.3 है, जिसे जून 2026 में जारी किया गया था। बाइनरी इंस्टॉल करें और डाउनलोड की पुष्टि करें:
cd /tmp
curl -fsSLO https://github.com/oauth2-proxy/oauth2-proxy/releases/download/v7.15.3/oauth2-proxy-v7.15.3.linux-amd64.tar.gz
curl -fsSLO https://github.com/oauth2-proxy/oauth2-proxy/releases/download/v7.15.3/oauth2-proxy-v7.15.3.linux-amd64.tar.gz-sha256sum.txt
sha256sum -c oauth2-proxy-v7.15.3.linux-amd64.tar.gz-sha256sum.txt
tar -xzf oauth2-proxy-v7.15.3.linux-amd64.tar.gz
sudo install -m 755 oauth2-proxy-v7.15.3.linux-amd64/oauth2-proxy /usr/local/bin/oauth2-proxy
oauth2-proxy --versionsha256sum -c को एक लाइन प्रिंट करनी चाहिए जो OK पर समाप्त होती हो। यदि यह FAILED प्रिंट करता है, तो रुक जाएं और बाइनरी चलाने के बजाय उसे फिर से डाउनलोड करें।
Docker में image quay.io/oauth2-proxy/oauth2-proxy है, और आपको tag को पिन करना चाहिए: quay.io/oauth2-proxy/oauth2-proxy:v7.15.3। इसे latest पर छोड़ने से एक सामान्य docker compose pull, बॉक्स पर मौजूद हर ऐप की सुरक्षा करने वाली एकमात्र प्रक्रिया के अनियोजित अपग्रेड में बदल सकता है।
Cookie secret जनरेट करें
Session cookie एन्क्रिप्टेड होती है और cookie_secret इसकी key है। यह अनिवार्य रूप से 16, 24 या 32 bytes की होनी चाहिए, क्योंकि यह एक AES (advanced encryption standard) key के रूप में कार्य करती है। किसी अन्य लंबाई की स्थिति में oauth2-proxy स्टार्ट होने से मना कर देगा और startup error में cookie secret का उल्लेख करेगा।
openssl rand -base64 32 | tr -- '+/' '-_'tr केवल दिखावे के लिए नहीं है। यह standard base64 को URL-safe alphabet में बदलता है, ताकि यह मान बिना किसी quoting समस्या के shell, env file और HTTP header में सुरक्षित रहे।
इस मान के लिए दो नियम हैं। हर deployment के लिए एक अलग secret का उपयोग करें। और जब आप एक ही domain के पीछे एक से अधिक oauth2-proxy instances चलाते हैं, तो उन सभी को एक ही secret दें, क्योंकि एक instance द्वारा एन्क्रिप्ट की गई cookie को दूसरों द्वारा पढ़ा जाना आवश्यक है।
oauth2-proxy कॉन्फ़िगरेशन लिखें
सेटिंग्स को एक लंबी कमांड लाइन के बजाय एक फ़ाइल में रखें, ताकि client secret कभी भी ps आउटपुट में दिखाई न दे।
# /etc/oauth2-proxy/oauth2-proxy.cfg
http_address = "127.0.0.1:4180"
reverse_proxy = true
provider = "oidc"
oidc_issuer_url = "https://id.example.com/application/o/myapp/"
client_id = "REPLACE_ME"
client_secret = "REPLACE_ME"
redirect_url = "https://app.example.com/oauth2/callback"
cookie_secret = "REPLACE_ME"
cookie_secure = true
cookie_domains = [".example.com"]
whitelist_domains = [".example.com"]
email_domains = ["*"]
set_xauthrequest = true
upstreams = ["static://202"]reverse_proxy = true, oauth2-proxy को निर्देश देता है कि वह अपने सामने मौजूद प्रॉक्सी से आने वाले X-Forwarded-* हेडर पर भरोसा करे। इसके बिना, oauth2-proxy प्रॉक्सी के स्वयं के पते को क्लाइंट का पता मान लेता है और गलत निर्णय ले सकता है कि अनुरोध HTTPS पर आया है या नहीं।
upstreams = ["static://202"], oauth2-proxy को एक प्रमाणित अनुरोध (authenticated request) पर 202 का उत्तर देने और कुछ भी प्रॉक्सी न करने के लिए मजबूर करता है। फॉरवर्ड ऑथ (forward auth) के लिए यही आवश्यक है, क्योंकि प्रॉक्सी का काम रिवर्स प्रॉक्सी द्वारा किया जाता है। दूसरा डिप्लॉयमेंट तरीका oauth2-proxy को सीधे अनुरोध पथ में upstreams = ["http://127.0.0.1:3000"] के साथ रखता है और इसमें auth_request का उपयोग बिल्कुल नहीं होता। यह एक ऐप के लिए सरल है, लेकिन दस ऐप्स के लिए स्केलेबल नहीं है।
email_domains = ["*"] उन सभी पतों को स्वीकार करता है जिन्हें आपका प्रदाता प्रमाणित करेगा। इसे अपने डोमेन तक सीमित रखें, या बेहतर होगा कि प्रदाता में ग्रुप बाइंडिंग के साथ एक्सेस को प्रतिबंधित करें, क्योंकि प्रदाता ही वह स्थान है जहाँ आप पहले से ही लोगों को मैनेज करते हैं।
इसे systemd के अंतर्गत अपने स्वयं के यूजर के रूप में चलाएं:
# /etc/systemd/system/oauth2-proxy.service
[Unit]
Description=oauth2-proxy
After=network-online.target
Wants=network-online.target
[Service]
User=oauth2-proxy
Group=oauth2-proxy
ExecStart=/usr/local/bin/oauth2-proxy --config=/etc/oauth2-proxy/oauth2-proxy.cfg
Restart=on-failure
ProtectSystem=strict
PrivateTmp=true
NoNewPrivileges=true
[Install]
WantedBy=multi-user.targetsudo useradd --system --no-create-home --shell /usr/sbin/nologin oauth2-proxy
sudo install -d -m 750 /etc/oauth2-proxy
sudo chown -R oauth2-proxy:oauth2-proxy /etc/oauth2-proxy
sudo chmod 600 /etc/oauth2-proxy/oauth2-proxy.cfg
sudo systemctl daemon-reload
sudo systemctl enable --now oauth2-proxy
curl -s http://127.0.0.1:4180/ping/ping का OK प्रिंट करना यह दर्शाता है कि प्रोसेस शुरू हो गई है और उसने अपना कॉन्फ़िगरेशन लोड कर लिया है। यह oauth2-proxy का अपना हेल्थ एंडपॉइंट है और यह कभी भी सेशन के लिए नहीं पूछता है। यदि कोई उत्तर नहीं मिलता है, तो journalctl -u oauth2-proxy -n 50 पढ़ें, क्योंकि गलत issuer URL और गलत लंबाई वाला cookie secret दोनों ही स्टार्टअप पर विफल हो जाते हैं और दोनों ही इसका कारण स्पष्ट करते हैं।
अपने प्रदाता के साथ redirect URI को रजिस्टर करें
अपने प्रदाता में एक OIDC एप्लिकेशन बनाएँ और इसके redirect URI को कॉन्फ़िगरेशन के redirect_url पर सेट करें: https://app.example.com/oauth2/callback। "बिल्कुल" का अर्थ है कि scheme, host, port और path का हर एक अक्षर मेल खाना चाहिए। अंत में लगा एक slash (/) इसे एक अलग URI बना देता है।
पूरे सेटअप में यह सबसे आम विफलता है, और यह oauth2-proxy के शामिल होने से पहले ही विफल हो जाती है। प्रदाता authorization request को अस्वीकार कर देता है और अपना स्वयं का error page दिखाता है, इसलिए oauth2-proxy लॉग में कुछ भी दिखाई नहीं देता है। इसकी पहचान address bar से होती है: ब्राउज़र अभी भी आपके प्रदाता के डोमेन पर है, और query string में सीधे error=invalid_request या पेज का नाम redirect_uri मौजूद है। जब आप ऐसा देखें, तो प्रॉक्सी कॉन्फ़िगरेशन के बजाय प्रदाता में एप्लिकेशन रिकॉर्ड को ठीक करें।
issuer URL को टाइप करने के बजाय प्रदाता से कॉपी करें। oauth2-proxy, oidc_issuer_url में /.well-known/openid-configuration जोड़ता है और स्टार्टअप पर उस discovery document को fetch करता है। इसे पहले स्वयं जाँचें:
curl -s https://id.example.com/application/o/myapp/.well-known/openid-configuration | head -c 400JSON जिसमें authorization_endpoint key हो, उसका मतलब है कि issuer URL सही है। 404 या HTML error page का मतलब है कि यह गलत है, और oauth2-proxy उसी 404 पर स्टार्ट होने में विफल हो जाएगा। यदि आपने अभी तक कोई प्रदाता नहीं चुना है, तो Keycloak, Authentik और Zitadel की तुलना इनके बीच के अंतर को स्पष्ट करती है, और Authentik को अपने SSO सर्वर के रूप में चलाना इस सटीक सेटअप के प्रदाता वाले हिस्से को विस्तार से समझाता है।
Nginx: auth_request
Nginx, auth_request का उपयोग करके forward auth करता है, जो एक internal sub-request भेजता है और उसके status code के आधार पर निर्णय लेता है।
# in the http context, next to your other maps
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 443 ssl;
server_name app.example.com;
location /oauth2/ {
proxy_pass http://127.0.0.1:4180;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Auth-Request-Redirect $request_uri;
}
location = /oauth2/auth {
proxy_pass http://127.0.0.1:4180;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Uri $request_uri;
proxy_set_header Content-Length "";
proxy_pass_request_body off;
}
location / {
auth_request /oauth2/auth;
error_page 401 = @oauth2_signin;
auth_request_set $user $upstream_http_x_auth_request_user;
auth_request_set $email $upstream_http_x_auth_request_email;
proxy_set_header X-User $user;
proxy_set_header X-Email $email;
auth_request_set $auth_cookie $upstream_http_set_cookie;
add_header Set-Cookie $auth_cookie;
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
location @oauth2_signin {
return 302 /oauth2/sign_in?rd=$scheme://$host$request_uri;
}
}इसमें तीन विवरण महत्वपूर्ण हैं। एक खाली Content-Length के साथ proxy_pass_request_body off का उपयोग करने से Nginx हर POST request की body को sub-request में कॉपी करना बंद कर देता है। यह महत्वपूर्ण है क्योंकि oauth2-proxy इसे पढ़ता नहीं है। फाइल अपलोड के दौरान, default configuration फाइल को दो बार भेजती है।
auth_request_set $auth_cookie और add_header Set-Cookie की जोड़ी refreshed session cookie को वापस browser तक पहुँचाती है। यदि इसे हटा दिया जाए, तो cookie_refresh चुपचाप काम करना बंद कर देता है, क्योंकि Nginx sub-request के Set-Cookie को discard कर देता है और session expire होने तक browser पुरानी value ही रखता है।
error_page 401 = @oauth2_signin वह directive है जो failed check को login page पर भेजती है। इसके बिना, unauthenticated visitor को केवल एक खाली 401 Authorization Required पेज दिखाई देगा और आगे बढ़ने का कोई रास्ता नहीं होगा।
Reload करने से पहले हमेशा test करें:
sudo nginx -t && sudo systemctl reload nginxयदि आसपास की directives आपके लिए नई हैं, तो Nginx reverse proxy config की संरचना इस परत के नीचे की जानकारी प्रदान करती है।
Traefik: forwardAuth middleware
Traefik को एक ही कार्य के लिए दो middlewares की आवश्यकता होती है। एक जाँच (check) चलाता है। दूसरा 401 status को ब्राउज़र रीडायरेक्ट में बदल देता है।
# dynamic configuration
http:
middlewares:
oauth-auth:
forwardAuth:
address: https://oauth.example.com/oauth2/auth
trustForwardHeader: true
oauth-errors:
errors:
status:
- "401-403"
service: oauth-backend
query: "/oauth2/sign_in?rd={url}"
statusRewrites:
"401": 302दोनों को उस router से जोड़ें जो आपके app के सामने स्थित है, और oauth2-proxy को उसके अपने router पर oauth.example.com पर प्रकाशित करें, क्योंकि ब्राउज़र को जाँच से गुजरे बिना /oauth2/sign_in और /oauth2/callback तक पहुँचना होता है।
statusRewrites के माध्यम से 401 को 302 पर मैप करना वह हिस्सा है जिसे लोग अक्सर भूल जाते हैं। इसके बिना Traefik 401 status के साथ sign-in रीडायरेक्ट लौटाता है, ब्राउज़र उसका पालन नहीं करता है, और विज़िटर को एक पेज दिखता है जिसमें केवल Found. शब्द लिखा होता है।
trustForwardHeader: true मूल host और URI को oauth2-proxy तक पहुँचाता है, जिसे rd मान बनाने के लिए इनकी आवश्यकता होती है ताकि उपयोगकर्ता को वापस उसी पेज पर भेजा जा सके जिसे उन्होंने माँगा था। whitelist_domains को उस host को कवर करने के लिए सेट करें, अन्यथा oauth2-proxy rd पैरामीटर को open-redirect जोखिम मानकर हटा देता है और लॉगिन के बाद हर कोई / पर पहुँच जाता है। एक Traefik बॉक्स आमतौर पर एक साथ कई apps को संभालता है, और एक Traefik instance के माध्यम से कई Docker Compose apps को रूट करना उस router लेआउट को दर्शाता है जिसमें यह सेटअप फिट बैठता है।
Caddy: forward_auth
app.example.com {
handle /oauth2/* {
reverse_proxy oauth2-proxy.internal:4180 {
header_up X-Real-IP {remote_host}
header_up X-Forwarded-Uri {uri}
}
}
handle {
forward_auth oauth2-proxy.internal:4180 {
uri /oauth2/auth
header_up X-Real-IP {remote_host}
copy_headers X-Auth-Request-User X-Auth-Request-Email
@error status 401
handle_response @error {
redir * /oauth2/sign_in?rd={scheme}://{host}{uri}
}
}
reverse_proxy upstream.internal:3000
}
}यहाँ क्रम महत्वपूर्ण है। /oauth2/* ब्लॉक सबसे पहले आता है और इसमें कोई forward_auth नहीं होता, क्योंकि जो visitor logged in नहीं है, उसे sign-in और callback paths तक पहुँचना होता है। यदि आप इन paths के आगे check लगा देंगे, तो login प्रक्रिया खुद को ही redirect करती रहेगी जब तक कि browser उसे बंद न कर दे।
copy_headers वह है जो identity को upstream request पर भेजता है, और यह केवल तभी values उत्पन्न करता है जब oauth2-proxy को set_xauthrequest = true के साथ चलाया जाए। proxies के बीच चुनाव करना अपने आप में एक अलग विषय है, और nginx, Caddy और Traefik की तुलना इस पर विस्तार से चर्चा करती है।
लॉगिन पेज पर वापस लूप क्यों होता है?
आप प्रदाता (provider) पर साइन इन करते हैं, वह आपको वापस भेजता है, और oauth2-proxy आपको सीधे फिर से प्रदाता के पास भेज देता है। इस लूप का मतलब है कि कॉलबैक अनुरोध उस कुकी के बिना आया है जिसे oauth2-proxy ने बाहर जाते समय सेट किया था। इसके लॉग में यह स्थिति इस प्रकार दिखाई देती है:
No cookies were found in OAuth callback.या, जब कोई अन्य कुकी तो पहुँच गई लेकिन सही वाली नहीं पहुँची:
Cookies were found in OAuth callback, but none was a CSRF cookie.CSRF का अर्थ है क्रॉस-साइट रिक्वेस्ट फोर्जरी, और यह कुकी इसलिए होती है ताकि कॉलबैक को उस लॉगिन से जोड़ा जा सके जिसने इसे शुरू किया था। ब्राउज़र को वही विफलता इस प्रकार दिखती है:
Login Failed: Unable to find a valid CSRF token. Please try again.इन चार कारणों की जाँच इसी क्रम में करें।
cookie_secure = trueजबकि ब्राउज़र plain HTTP के माध्यम से साइट तक पहुँचा। ब्राउज़रSecureचिह्नित कुकी कोhttp://ऑरिजिन पर स्टोर नहीं करेगा, इसलिए इसे कभी वापस नहीं भेजा जाता है। TLS (ट्रांसपोर्ट लेयर सिक्योरिटी) को ठीक से टर्मिनेट करें, या केवल localhost पर परीक्षण करते समयcookie_secure = falseसेट करें।- एक
cookie_domainsमान जो एड्रेस बार में होस्टनेम को कवर नहीं करता है।.example.comकेवलapp.example.comको कवर करता है औरapp.example.netके लिए कुछ भी नहीं करता है। - ब्राउज़र कुकी को ड्रॉप कर रहा है। एक सख्त प्राइवेसी एक्सटेंशन या थर्ड-पार्टी कुकी ब्लॉकिंग आउटबाउंड रीडायरेक्ट और कॉलबैक के बीच
_oauth2_proxy_csrfको हटा सकती है। - क्लॉक ड्रिफ्ट। यदि सर्वर की घड़ी प्रदाता की घड़ी से काफी अलग है, तो ID टोकन का
iatऔरexpस्वीकार्य विंडो से बाहर हो जाते हैं और सत्र (session) आने पर अस्वीकार कर दिया जाता है।timedatectlकोSystem clock synchronized: yesरिपोर्ट करना चाहिए।
अनुमान लगाने के बजाय सर्वर साइड से इसे होते हुए देखें:
sudo journalctl -u oauth2-proxy -fऐप को प्राइवेट विंडो में लोड करें। प्रत्येक अनुरोध को उसके स्टेटस के साथ लॉग किया जाता है, इसलिए कॉलबैक जिसके तुरंत बाद प्रदाता के पास एक और रीडायरेक्ट हो, वह लूप है, जो रिकॉर्ड पर दर्ज हो जाता है।
वे पाथ जिन्हें लॉगिन से बाहर रखना आवश्यक है: APIs, webhooks और websockets
Forward auth यह मानकर चलता है कि ब्राउज़र में cookie मौजूद है। बिना ब्राउज़र वाले callers के लिए यह प्रक्रिया विफल हो जाती है।
जो API client Authorization: Bearer <token> भेजता है, उसके पास cookie नहीं होती है। इसलिए उसे आपके provider के लॉगिन पेज पर 302 redirect मिलता है और फिर वह HTML को JSON के रूप में पार्स करने का प्रयास करता है। इसके दो स्पष्ट समाधान हैं। skip_jwt_bearer_tokens = true सेट करने से oauth2-proxy उसी issuer से प्राप्त वैध JWT (JSON web token) bearer token को स्वीकार करने लगता है, जो तब सही होता है जब आपके API clients पहले से ही provider से tokens प्राप्त कर रहे हों। अन्यथा, पाथ को छूट (exempt) दें:
skip_auth_routes = [
"^/api/",
"POST=^/webhook/",
"GET=^/healthz$"
]प्रत्येक मान एक regular expression है जिसका मिलान normalized पाथ के साथ किया जाता है, जिसे वैकल्पिक रूप से HTTP method और = के साथ prefix किया जा सकता है। POST=^/webhook/ webhook receiver को POST के लिए खुला छोड़ देता है, जबकि उसी पाथ पर ब्राउज़ करने वाला व्यक्ति अभी भी लॉगिन पेज पर ही जाता है। प्रत्येक प्रविष्टि आपके गेट में एक छेद के समान है, इसलिए expressions को ^ के साथ anchor करें और उन्हें उतना ही सीमित रखें जितना caller अनुमति देता है।
Websockets वह मामला है जिसे लोग अक्सर गलत समझते हैं। Upgrade request एक सामान्य HTTP GET होती है जिसमें अन्य requests की तरह ही cookies होती हैं, इसलिए यह सामान्य रूप से चेक पास कर लेती है और इसे किसी छूट की आवश्यकता नहीं होती है। समस्या इसके चारों ओर proxying करने में आती है। यदि protected location पर Upgrade और Connection headers न हों, तो upgrade कभी पूरा नहीं होता और app का client ब्राउज़र कंसोल में WebSocket connection ... failed संदेश के साथ बार-बार retry करता रहता है। पाथ को छूट देने से वहां कुछ ठीक नहीं होता, क्योंकि request पहले ही authorize हो चुकी होती है।
एक वास्तविक सीमा लागू होती है। चेक केवल एक बार, upgrade के समय चलता है। जो websocket घंटों तक खुला रहता है, उसे दोबारा चेक नहीं किया जाता है, इसलिए अपने provider से किसी user को हटाने पर भी उनका पहले से खुला socket बंद नहीं होता है। लाइव connections को काटने के लिए app को restart करें।
Session storage, और वह cookie जो बहुत बड़ी हो जाती है
डिफ़ॉल्ट रूप से, पूरा session cookie के अंदर रहता है, जिसे आपके cookie_secret से encrypt किया जाता है। यह oauth2-proxy को stateless रखता है और इसके लिए किसी अतिरिक्त service की आवश्यकता नहीं होती है। इसकी एक सीमा भी है, क्योंकि browsers cookie को 4 KB के आसपास सीमित कर देते हैं। जब ID token में group claims की एक लंबी सूची होती है, तो oauth2-proxy session को _oauth2_proxy_0, _oauth2_proxy_1 और उसके आगे के हिस्सों में विभाजित कर देता है। कुछ हिस्सों के बाद, request headers इतने बड़े हो जाते हैं कि app के request देखने से पहले ही nginx 400 Request Header Or Cookie Too Large error दे देता है।
जब ऐसा हो, तो session को server-side पर ले जाएँ:
session_store_type = "redis"
redis_connection_url = "redis://127.0.0.1:6379"इसके बाद browser एक छोटा ticket रखता है, और encrypted session Redis में रहता है। इसकी कीमत एक ऐसी service है जिसे चालू रहना पड़ता है: यदि Redis down हो जाता है, तो हर session अमान्य हो जाता है और सभी users एक साथ logout हो जाते हैं। cookie store की अपनी एक कीमत है, वह यह कि दो requests जो एक ही समय में एक ही session को refresh कर रही हों, उनमें conflict हो सकता है और user को दोबारा login करने के लिए मजबूर होना पड़ सकता है।
Forward auth आपको क्या नहीं देता है
यह दरवाजे पर लगा एक गेट है। यह एप्लिकेशन के भीतर का ऑथोराइजेशन (authorisation) नहीं है, और यही अंतर यह तय करता है कि यह तरीका आपके मामले के लिए उपयुक्त है या नहीं।
एक बार जब कोई यूजर अंदर आ जाता है, तो एप्लिकेशन को वही दिखता है जो उसे हमेशा दिखता था। यदि ऐप की अपनी भूमिकाएँ (roles) हैं, तो forward auth उन्हें नहीं भरता, जब तक कि ऐप header-based authentication का समर्थन न करता हो और किसी हेडर को अकाउंट से मैप न करता हो। Grafana ऐसा करता है, अपने auth.proxy सेटिंग्स के माध्यम से। अधिकांश self-hosted ऐप्स ऐसा नहीं करते हैं, इसलिए गेट से गुजरने वाला हर व्यक्ति एप्लिकेशन के लिए एक ही पहचान रखता है, और वह पहचान अक्सर एक एडमिन की होती है।
यह एप्लिकेशन के अपने API टोकन की भी सुरक्षा नहीं करता है। ऐप द्वारा जारी किया गया एक personal access token ऐप के लिए ऑथेंटिकेट करता है, न कि oauth2-proxy के लिए, इसलिए जैसे ही गेट उसके सामने आता है, टोकन काम करना बंद कर देता है। API पाथ को छूट (exempt) दें ताकि वह फिर से काम करने लगे, और तब वह टोकन उस पाथ की रक्षा करने वाली एकमात्र चीज बन जाता है। अब आप एक ही सर्विस पर दो ऑथेंटिकेशन सिस्टम चला रहे हैं, और SSO उनमें से केवल एक को कवर करता है।
Revocation (अधिकार वापस लेना) तीसरी कमी है। अपने प्रोवाइडर में किसी यूजर को डिलीट करने से नए लॉगिन रुक जाते हैं और वह टोकन रिफ्रेश भी रुक जाता है जिसे cookie_refresh परफॉर्म करता है, लेकिन मौजूदा सेशन कुकी उसके एक्सपायर होने तक वैध रहती है। cookie_expire डिफ़ॉल्ट रूप से 168 घंटे का होता है, जो उस व्यक्ति के लिए एक सप्ताह का एक्सेस है जिसे आपने अभी हटाया है। cookie_refresh को किसी कम समय पर सेट करें, जैसे कि एक घंटा, ताकि revocation उस विंडो के भीतर ही प्रभावी हो जाए।
ऑडिट ट्रेल भी गेट पर ही रुक जाता है। oauth2-proxy लॉग करता है कि कौन आया और कब आया। एप्लिकेशन एक अनाम सेशन को लॉग करता है। यदि आपको यह जवाब देना हो कि सेटिंग किसने बदली, तो ऐप में हेडर आइडेंटिटी न्यूनतम आवश्यकता है, और वास्तविक per-user अकाउंट ही इसका सही समाधान है।
जब SSO tax का भुगतान करना बेहतर विकल्प हो
Forward auth तब सही उपकरण है जब किसी app में कोई login न हो, या केवल एक shared password हो, और आप लोगों को जोड़ने और हटाने के लिए एक ही स्थान चाहते हों। इसमें एक दोपहर का समय और एक अतिरिक्त process लगती है, और यह HTTP का समर्थन करने वाली किसी भी app के साथ काम करता है।
यह तब गलत उपकरण है जब एक ही app के भीतर अलग-अलग लोगों को अलग-अलग permissions की आवश्यकता हो। एक gate यह नहीं बता सकता कि "Ana dashboards को edit कर सकती है और Bo उन्हें केवल पढ़ सकता है"। यदि vendor SSO tier बेचता है, तो group-to-role mapping ही वह चीज़ है जिसे आप वास्तव में खरीद रहे होते हैं, और उसे headers तथा proxy rules के साथ फिर से बनाना, भुगतान करने की तुलना में अधिक नाजुक होता है। उन SSO tiers के पीछे का pricing pattern किसी भी निर्णय पर पहुँचने से पहले पढ़ना उचित है।
दो अन्य स्थितियाँ भी इसी दिशा की ओर इशारा करती हैं। Compliance का काम, जिसे application के भीतर प्रति-उपयोगकर्ता (per-user) audit records की आवश्यकता होती है, वह प्रमाण के रूप में proxy access log को स्वीकार नहीं करेगा। और कोई भी app जिसमें mobile या desktop client हो जो browser cookies का उपयोग नहीं करता, वह हर request पर gate के साथ संघर्ष करेगा।
FAQ
Forward auth क्या है?
Forward auth एक ऐसा पैटर्न है जिसमें reverse proxy हर incoming request को upstream भेजने से पहले एक अलग authentication service से verify करता है। Proxy request headers को /oauth2/auth जैसे endpoint पर भेजता है और status code पढ़ता है। 202 का मतलब है कि access की अनुमति है, इसलिए मूल request application तक पहुँच जाती है। 401 का मतलब है कि session मौजूद नहीं है, इसलिए proxy browser को login page पर redirect कर देता है। Nginx इसे auth_request directive के साथ, Traefik forwardAuth middleware के साथ, और Caddy forward_auth के साथ लागू करता है।
oauth2-proxy मुझे login page पर बार-बार redirect क्यों कर रहा है?
Callback request बिना CSRF cookie के oauth2-proxy तक पहुँची है, इसलिए oauth2-proxy flow को फिर से शुरू कर देता है। Server log में No cookies were found in OAuth callback. दिखाई देता है और browser Login Failed: Unable to find a valid CSRF token. Please try again. दिखाता है। इसका सामान्य कारण plain HTTP पर पहुँचने वाली साइट पर cookie_secure = true है, क्योंकि browser http:// origin पर Secure cookie store नहीं करेगा। दूसरा सबसे आम कारण cookie_domains value है जो address bar में मौजूद hostname को cover नहीं करती है।
मैं API client या webhook को oauth2-proxy से होकर जाने की अनुमति कैसे दूँ?
skip_auth_routes का उपयोग एक anchored regular expression के साथ करें, जिसे वैकल्पिक रूप से एक HTTP method तक सीमित किया जा सकता है, उदाहरण के लिए POST=^/webhook/। यदि आपके API clients के पास पहले से ही उसी provider द्वारा जारी किए गए JWTs हैं, तो skip_jwt_bearer_tokens = true उन tokens को cookie की जगह स्वीकार कर लेता है और path को सुरक्षित रखता है। skip_auth_routes में सूचीबद्ध कोई भी चीज़ सभी के लिए unauthenticated होती है, इसलिए प्रत्येक expression को उतना ही सीमित रखें जितना caller अनुमति देता है।
क्या oauth2-proxy application को per-user permissions देता है?
नहीं। यह एक gate है, न कि कोई authorisation system। यह तय करता है कि application तक कौन पहुँचेगा, और जो भी वहाँ पहुँचता है वह application के लिए एक जैसा ही दिखता है, जब तक कि वह application identity headers को पढ़कर उन्हें accounts के साथ map न करे। Grafana इसे अपनी auth.proxy settings के माध्यम से कर सकता है। अधिकांश self-hosted apps ऐसा नहीं कर सकतीं, इसलिए gate पार करने वाला हर व्यक्ति उसी identity को साझा करता है जिसके साथ app चल रही है।
क्या कोई identity header को खुद set करके oauth2-proxy को bypass कर सकता है?
हाँ, यदि वे सीधे application तक पहुँच सकते हैं। Identity एक plain header जैसे X-Auth-Request-Email के रूप में आती है, और application उसे प्राप्त होने वाली हर चीज़ पर भरोसा करता है। जो कोई भी application port से connection खोल सकता है, वह उस header को भेजकर कोई भी user बन सकता है। App को 127.0.0.1 पर bind करें, या इसे बिना किसी published port के internal Docker network पर रखें, और sudo ss -tlnp के साथ इसकी पुष्टि करें।