OAuth 2.0 ও থার্ড-পার্টি লগইন
এই পাঠে যা শিখবেন
- OAuth 2.0-এর অথোরাইজেশন-কোড ফ্লোর ধারণা ও প্রতিটি ধাপের উদ্দেশ্য
- কেন OAuth অথেন্টিকেশন নয়, অথোরাইজেশন — এই পার্থক্যের ব্যবহারিক গুরুত্ব
- Python দিয়ে ফ্লোর একটি সত্যিকারের স্টেট-মেশিন লেখা, যা ভুল ক্রমে ধাপ কল করলে ব্যর্থ হয়
- প্রতিটি ধাপান্তর (stage transition) ট্র্যাক ও প্রিন্ট করে সম্পূর্ণ ফ্লো যাচাই করা
১ · অথোরাইজেশন-কোড ফ্লো — ধাপে ধাপে
"Google দিয়ে লগইন করুন" বা "Facebook দিয়ে সাইন-ইন করুন" বাটনের পেছনে সাধারণত এই ফ্লোটি কাজ করে। লক্ষ্য করুন — আপনার অ্যাপ কখনো ইউজারের Google পাসওয়ার্ড দেখে না; পুরো প্রক্রিয়াটি Google-এর নিজস্ব পেজে ঘটে।
OAuth 2.0 মূলত অথোরাইজেশন প্রোটোকল — এটি বলে "এই অ্যাপকে আমার প্রোফাইল তথ্য পড়ার অনুমতি দিলাম", পরিচয় যাচাই (অথেন্টিকেশন) নয়। "Google দিয়ে লগইন" ফিচারগুলো সাধারণত OAuth-এর উপর OpenID Connect নামের একটি পাতলা স্তর যোগ করে পরিচয় যাচাইয়ের কাজ করে — এই দুটো শব্দ গুলিয়ে ফেলা একটি সাধারণ ভুল।
২ · Python দিয়ে ফ্লোর স্টেট-মেশিন সিমুলেশন
সত্যিকারের HTTP রিডাইরেক্ট বা প্রোভাইডার-কল এই স্যান্ডবক্সে সম্ভব নয়, তাই নিচের কোডে ফ্লোর
অবস্থা-পরিবর্তন যুক্তিটাই সত্যি করে বাস্তবায়ন করা হয়েছে — প্রতিটি ধাপ শুধু সঠিক আগের
ধাপের পরেই কল করা যায়, নাহলে ValueError রেইজ হয়।
class OAuthFlow:
def __init__(self, client_id):
self.client_id = client_id
self.stage = "start"
self.auth_code = None
self.access_token = None
self.history = [self.stage]
def redirect_to_provider(self):
if self.stage != "start":
raise ValueError(f"'start' পর্যায়ে না থাকলে রিডাইরেক্ট করা যায় না (বর্তমান: {self.stage})")
self.stage = "redirected_to_provider"
self.history.append(self.stage)
return f"https://provider.example.com/authorize?client_id={self.client_id}"
def user_consents(self, approved):
if self.stage != "redirected_to_provider":
raise ValueError("প্রোভাইডারে রিডাইরেক্ট না হয়ে সম্মতি দেওয়া যায় না")
if not approved:
self.stage = "denied"
self.history.append(self.stage)
return False
self.stage = "user_consented"
self.history.append(self.stage)
return True
def receive_code(self, simulated_code):
if self.stage != "user_consented":
raise ValueError("সম্মতি না পেয়ে কোড গ্রহণ করা যায় না")
self.auth_code = simulated_code
self.stage = "code_received"
self.history.append(self.stage)
return self.auth_code
def exchange_token(self, simulated_provider_response):
if self.stage != "code_received":
raise ValueError("কোড না পেয়ে টোকেন এক্সচেঞ্জ করা যায় না")
self.access_token = simulated_provider_response["access_token"]
self.stage = "token_exchanged"
self.history.append(self.stage)
return self.access_token
# --- সঠিক ক্রমে পুরো ফ্লো চালানো ---
flow = OAuthFlow(client_id="abcltech-demo-app")
print("পর্যায় ১:", flow.stage)
auth_url = flow.redirect_to_provider()
print("পর্যায় ২:", flow.stage, "->", auth_url)
consented = flow.user_consents(approved=True)
print("পর্যায় ৩:", flow.stage, "(সম্মতি দিয়েছে:", consented, ")")
code = flow.receive_code(simulated_code="AUTH_CODE_9f8a3c")
print("পর্যায় ৪:", flow.stage, "-> প্রাপ্ত কোড:", code)
provider_response = {"access_token": "ACCESS_TOKEN_7be21d", "token_type": "Bearer", "expires_in": 3600}
token = flow.exchange_token(provider_response)
print("পর্যায় ৫:", flow.stage, "-> অ্যাক্সেস টোকেন:", token)
print("\nসম্পূর্ণ স্টেজ হিস্ট্রি:", " -> ".join(flow.history))
# --- ভুল ক্রমে কল করলে কী হয় তা যাচাই করা ---
print("\n--- ভুল ক্রমে কল করার চেষ্টা ---")
bad_flow = OAuthFlow(client_id="abcltech-demo-app")
try:
bad_flow.receive_code("SHOULD_FAIL")
except ValueError as e:
print("প্রত্যাশিতভাবে ব্যর্থ হয়েছে:", e)
bad_flow-এর ক্ষেত্রে redirect_to_provider() ও user_consents() কখনো
কল না করেই সরাসরি receive_code() কল করা হয়েছে — যেহেতু self.stage তখনও
"start" ("user_consented" নয়), receive_code()-এর ভেতরের চেক ব্যর্থ
হয়ে ValueError রেইজ করে। এটাই দেখায় স্টেট-মেশিনটি সত্যিই ক্রম জোরপূর্বক বজায় রাখে — ঠিক
যেমন বাস্তব OAuth সার্ভার একটি ইস্যু-না-করা বা ইতিমধ্যে-ব্যবহৃত কোড দিয়ে টোকেন-এক্সচেঞ্জ রিকোয়েস্ট
প্রত্যাখ্যান করবে।
OAuth 2.0-এর অথোরাইজেশন-কোড ফ্লো একটি নির্দিষ্ট ক্রমে ধাপে-ধাপে এগোয় — রিডাইরেক্ট, সম্মতি, কোড, টোকেন —
এবং প্রতিটি ধাপ আগের ধাপের উপর নির্ভরশীল। "Google/Facebook দিয়ে লগইন" ফিচার আসলে এই একই ফ্লো, শুধু
প্রোভাইডার আলাদা। বাস্তব ইন্টিগ্রেশনে ব্যাক-চ্যানেল টোকেন-এক্সচেঞ্জ ধাপটিই সবচেয়ে গুরুত্বপূর্ণ — এখানে
গোপন client_secret ব্যবহৃত হয়, যা কখনো ব্রাউজারে প্রকাশ পায় না।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।
প্র ০১ টোকেন-এক্সচেঞ্জ ধাপটি (কোড → টোকেন) কেন ব্রাউজার দিয়ে না করে সরাসরি অ্যাপের সার্ভার থেকে প্রোভাইডারকে কল করে করা হয়?
এই "ব্যাক-চ্যানেল" কলে অ্যাপের গোপন client_secret পাঠাতে হয়, যা প্রমাণ করে রিকোয়েস্টটি
সত্যিই নিবন্ধিত অ্যাপ থেকে এসেছে, কোনো ভুয়া অ্যাপ থেকে নয়। যদি এই কলটি ব্রাউজার থেকে (ফ্রন্ট-এন্ড
JavaScript দিয়ে) করা হতো, তাহলে client_secret-টি পেজের সোর্স কোডে বা নেটওয়ার্ক
রিকোয়েস্টে দৃশ্যমান হয়ে যেত — যে কেউ ব্রাউজারের ডেভেলপার টুলস খুলে সেটি চুরি করতে পারত। তাই এই ধাপ
সবসময় সার্ভার-টু-সার্ভার (ব্যাক-চ্যানেল) হিসেবে করা হয়, যেখানে ব্রাউজার আদৌ জড়িত থাকে না।
প্র ০২
উপরের কোডে যদি user_consents(approved=False) কল করা হয়, তাহলে ফ্লোর পরবর্তী কোন ধাপ
(receive_code()) ব্যর্থ হবে কেন?
user_consents(approved=False) কল করলে self.stage "user_consented"
না হয়ে "denied"-এ চলে যায়। receive_code() ফাংশনের ভেতরের চেক
(if self.stage != "user_consented") তখন সত্য হয়ে যাবে, তাই ValueError রেইজ
হবে — অর্থাৎ ইউজার সম্মতি প্রত্যাখ্যান করলে ফ্লো সেখানেই থেমে যায়, কোনো কোড ইস্যু হয় না, এবং অ্যাপ কোনো
টোকেনও পায় না।
প্র ০৩ OAuth 2.0-এর ফ্লো L34-এর JWT পদ্ধতির সাথে কোথায় মিলে যায়?
অথোরাইজেশন-কোড ফ্লোর শেষ ধাপে প্রোভাইডার যে "অ্যাক্সেস টোকেন" ফেরত দেয়, বাস্তবে সেটি প্রায়ই একটি JWT-ই
হয় (L34-এ দেখা গঠন — header.payload.signature)। অ্যাপ পরবর্তী প্রতিটি API কলে সেই টোকেনটি পাঠায়, এবং
রিসোর্স সার্ভার L34-এর verify_jwt()-এর মতো একটি ফাংশন দিয়ে সিগনেচার যাচাই করে টোকেনটি
বৈধ ও অপরিবর্তিত কি না নিশ্চিত হয় — দুটো লেসনের ধারণা এখানে একসাথে কাজ করে।
অনুশীলন
-
চিন্তা করুন: একটি অ্যাপ যদি নিজের ইউজারনেম-পাসওয়ার্ড সিস্টেমের পাশাপাশি "Google দিয়ে
লগইন"-ও দেয়, তাহলে একজন ইউজার দুইভাবেই লগইন করলে অ্যাপকে কীভাবে নিশ্চিত হতে হবে যে দুটোই একই ব্যক্তি?
সাধারণত প্রোভাইডার (Google) প্রতিটি ইউজারের জন্য একটি অনন্য (unique), স্থায়ী আইডি (যেমন
subক্লেইম) ফেরত দেয়, এবং প্রায়ই একটি যাচাই-করা ইমেইল ঠিকানাও দেয়। অ্যাপ নিজের ডেটাবেসে সেই Google আইডি বা ইমেইলটিকে বিদ্যমান ইউজার অ্যাকাউন্টের সাথে "লিংক" করে রাখে (একটিoauth_provider_idকলামের মতো) — এভাবে ইউজার যেভাবেই লগইন করুক (পাসওয়ার্ড বা Google), অ্যাপ একই অভ্যন্তরীণ ইউজার রেকর্ডে পৌঁছায়। -
পরীক্ষা করুন: উপরের কোড সেলে
flow.exchange_token(provider_response)দুইবার পরপর কল করে দেখুন এবং Run চেপে দেখুন দ্বিতীয়বার কী ঘটে।প্রথমবার
exchange_token()কল করার পরself.stage"token_exchanged"-এ চলে যায়, যা আর"code_received"নয়। দ্বিতীয়বার কল করলে ফাংশনের ভেতরের চেক (if self.stage != "code_received") ব্যর্থ হয়েValueErrorরেইজ করবে — বাস্তব OAuth সার্ভারও একইভাবে একটি অথোরাইজেশন কোড শুধু একবারই ব্যবহারযোগ্য রাখে, দ্বিতীয়বার একই কোড দিয়ে এক্সচেঞ্জের চেষ্টা প্রত্যাখ্যাত হয়।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- কোর্সের সম্পূর্ণ সিলেবাস দেখুন ৫৮টি পাঠ আর্কিটেকচার প্যাটার্ন, ফ্রন্ট-এন্ড/ব্যাক-এন্ড ফ্রেমওয়ার্ক ফান্ডামেন্টাল, স্টেট ম্যানেজমেন্ট, REST API, ORM, অথেন্টিকেশন, রেন্ডারিং স্ট্র্যাটেজি ও ডিপ্লয়মেন্ট।
- Cybersecurity কোর্স সহোদর কোর্স PKCE, state প্যারামিটার দিয়ে CSRF প্রতিরোধ, ও OAuth-সম্পর্কিত আক্রমণ ভেক্টরগুলো সেই কোর্সে বিস্তারিত আলোচিত।
- JavaScript Programming কোর্স সহোদর কোর্স ব্রাউজার রিডাইরেক্ট ও ফ্রন্ট-এন্ড থেকে OAuth ফ্লো শুরু করার ভাষাগত ভিত্তি সেই কোর্সে তৈরি হয়েছে।
- সব Courses দেখুন ABCL TECH C, C++, Python, Java, JavaScript, DSA, DBMS, Discrete Mathematics, System Design, Cybersecurity, Cloud Computing & DevOps, Computer Networks, Operating Systems, Computer Architecture, Programming Languages & Compiler Design, Software Engineering & Git, Theory of Computation, Engineering Economics ও Full-Stack Web Frameworks — সব এক জায়গায়।