Django

⌘K
  1. Home
  2. Django
  3. Django Rest Framework
  4. token set in cookie

token set in cookie

Access Token এবং Refresh Token কুকিতে সংরক্ষণ

অ্যাপ্লিকেশন বা ওয়েবসাইটে নিরাপদ লগইন ও API রিকুয়েস্ট পরিচালনার জন্য Access Token এবং Refresh Token ব্যবহারের প্রক্রিয়া অত্যন্ত গুরুত্বপূর্ণ। টোকেনগুলো সাধারণত নিরাপদ প্রমাণীকরণের (authentication) জন্য ব্যবহার করা হয়। নিচে কিভাবে Access Token এবং Refresh Token কুকি-তে সেট করা, রিফ্রেশ করা এবং সুরক্ষিত API রিকুয়েস্ট পাঠানো হয় তা বিস্তারিতভাবে দেখানো হয়েছে।

১. লগইন করার পর Access Token এবং Refresh Token কুকিতে সংরক্ষণ

Backend (Django Rest Framework):

লগইন সফল হলে Access Token এবং Refresh Token দুটি কুকিতে সংরক্ষণ করা উচিত। Access Token এর মেয়াদ স্বল্পমেয়াদি হয়ে থাকে, কিন্তু Refresh Token দীর্ঘমেয়াদি হয় এবং নতুন Access Token জেনারেট করতে ব্যবহৃত হয়।

from rest_framework_simplejwt.tokens import RefreshToken

# Login View (Adding custom claims to access token)
class LoginView(APIView):
    def post(self, request):
        email = request.data.get('email')
        password = request.data.get('password')
        
        user = authenticate(email=email, password=password)
        
        if user is not None:
            # Generate Refresh Token and Access Token
            refresh = RefreshToken.for_user(user)
            access = refresh.access_token

            # Add extra data to the Access Token
            access['email'] = user.email  # Example: adding email to token
            access['username'] = user.username  # Example: adding username to token
            access['user_id'] = user.id  # Example: adding user ID to token

            # Set tokens in cookies
            response = Response({
                'message': 'Login successful',
                'access_token': str(access),
                'refresh_token': str(refresh),
            })
            
            response.set_cookie(
                key='access_token',
                value=str(access),
                httponly=True,
                secure=True,
                samesite='Strict'
            )
            
            response.set_cookie(
                key='refresh_token',
                value=str(refresh),
                httponly=True,
                secure=True,
                samesite='Strict'
            )
            
            return response
        
        return Response({'error': 'Invalid credentials'}, status=status.HTTP_401_UNAUTHORIZED)

React.js ডেভেলপমেন্টে কুকি ব্যবহারের মাধ্যমে JWT (JSON Web Token) হ্যান্ডেল করা বেশ সহজ, তবে কিছু বিষয় মনে রাখতে হবে। নিচে ধাপে ধাপে ব্যাখ্যা করা হলো কিভাবে টোকেনগুলো সেভ করতে হবে এবং সিকিউর ভাবে রিকোয়েস্ট পাঠাতে হবে।

১.১ লগইন রিকোয়েস্ট

প্রথমে, ইউজার লগইন করার সময় তোমার React অ্যাপ্লিকেশন লগইন রিকোয়েস্ট পাঠাবে। নিচে একটি উদাহরণ দেওয়া হলো:

import axios from 'axios';

const loginUser = async (email, password) => {
    try {
        const response = await axios.post('https://yourapiurl.com/api/login/', {
            email,
            password
        });

        // টোকেন কুকিতে সেভ হবে, কুকি অটোমেটিক্যালি ব্রাউজারের দ্বারা হ্যান্ডেল হবে
        console.log('Login successful:', response.data);
    } catch (error) {
        console.error('Login failed:', error.response.data);
    }
};

২. লগআউট করা এবং Refresh Token ব্ল্যাকলিস্ট করা

লগআউটের সময় Refresh Token ব্ল্যাকলিস্ট করতে হবে যাতে পুরানো টোকেন ব্যবহারের মাধ্যমে আবার লগইন করা না যায়। কুকি মুছে ফেলা হবে এবং টোকেন রিফ্রেশ বন্ধ করা হবে।

class LogoutView(APIView):
    def post(self, request):
        try:
            # Refresh Token কুকি থেকে সংগ্রহ করা হচ্ছে
            refresh_token = request.COOKIES.get("refresh_token")

            if not refresh_token:
                return Response({"error": "Refresh token not found in cookies."}, status=status.HTTP_400_BAD_REQUEST)

            # Refresh Token ব্ল্যাকলিস্ট করা হচ্ছে
            token = RefreshToken(refresh_token)
            token.blacklist()

            # কুকি মুছে ফেলা হচ্ছে
            response = Response(status=status.HTTP_205_RESET_CONTENT)
            response.delete_cookie("access_token")
            response.delete_cookie("refresh_token")
            return response

        except Exception as e:
            return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST)

React কোড: Logout.js

import React from 'react';
import axios from 'axios';

const Logout = () => {
    const handleLogout = async () => {
        try {
            // API রিকুয়েস্টের জন্য URL
            const response = await axios.post('https://your-api-url/logout/', {}, {
                withCredentials: true // কুকি পাঠানোর জন্য
            });
            if (response.status === 205) {
                console.log("Logout successful");
                // এখানে লগআউট সফল হলে আপনি যা করতে চান তা করুন
            }
        } catch (error) {
            console.error("Logout failed:", error.response.data);
            // ত্রুটি পরিচালনা করুন
        }
    };

    return (
        <div>
            <h2>Logout Page</h2>
            <button onClick={handleLogout}>Logout</button>
        </div>
    );
};

export default Logout;

৩. Refresh Token থেকে Access Token রিফ্রেশ করা

যখন Access Token এর মেয়াদ শেষ হয়ে যায়, তখন Refresh Token ব্যবহার করে নতুন Access Token তৈরি করা হয়। নিচে দেখানো হয়েছে কীভাবে কুকি থেকে Refresh Token সংগ্রহ করে নতুন Access Token তৈরি করা যায়:

class RefreshTokenView(APIView):
    def post(self, request):
        try:
            # কুকি থেকে Refresh Token সংগ্রহ করা হচ্ছে
            refresh_token = request.COOKIES.get('refresh_token')
            if not refresh_token:
                return Response({"error": "Refresh token not found."}, status=status.HTTP_400_BAD_REQUEST)

            # Refresh Token থেকে নতুন Access Token তৈরি করা হচ্ছে
            token = RefreshToken(refresh_token)
            new_access_token = str(token.access_token)

            # নতুন Access Token কুকিতে সেট করা হচ্ছে
            response = Response({
                'access': new_access_token
            })
            response.set_cookie(
                key='access_token',
                value=new_access_token,
                httponly=True,
                secure=True,
                samesite='Strict'
            )
            return response

        except Exception as e:
            return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST)


from django.urls import path
from .views import RefreshTokenView

urlpatterns = [
    path('auth/refresh-token/', RefreshTokenView.as_view(), name='refresh_token'),
]

৪. ক্লায়েন্ট সাইড থেকে Access Token সহ API রিকুয়েস্ট পাঠানো

ফ্রন্টএন্ড থেকে Access Token ব্যবহার করে নিরাপদ রিকুয়েস্ট পাঠানো হয়। টোকেনটি Authorization হেডারে Bearer ফরম্যাটে পাঠানো হয়। নিচের কোডে কীভাবে এটি করা যায় তা দেখানো হয়েছে:

Example Code (React এবং axios ব্যবহার করে):

import axios from 'axios';

// কুকি থেকে Access Token সংগ্রহের ফাংশন
function getCookie(name) {
    let cookieValue = null;
    if (document.cookie && document.cookie !== '') {
        const cookies = document.cookie.split(';');
        for (let i = 0; i < cookies.length; i++) {
            const cookie = cookies[i].trim();
            if (cookie.substring(0, name.length + 1) === (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
}
export const refreshAccessToken = async () => {
    try {
        // Refresh Token পাঠিয়ে নতুন Access Token তৈরি করা
        const response = await axios.post('https://your-api-url/auth/refresh-token/', {}, {
            withCredentials: true, // কুকি পাঠানোর জন্য
        });

        return response.data.access; // নতুন Access Token রিটার্ন করা
    } catch (error) {
        console.error('Unable to refresh access token:', error);
        throw error;  // ত্রুটি হলে হ্যান্ডেল করা
    }
};



// Access Token সহ API রিকুয়েস্ট পাঠানোর ফাংশন
export const sendRequest = async (url) => {
    try {
        // API রিকুয়েস্ট পাঠানোর জন্য
        const response = await axios.get(url, {
            withCredentials: true, // কুকি পাঠানোর জন্য
        });

        return response.data;  // সার্ভার থেকে ডাটা রিটার্ন করা
    } catch (error) {
        // যদি Access Token এর মেয়াদ শেষ হয়ে যায় তাহলে রিফ্রেশ টোকেন ব্যবহার করে টোকেন রিফ্রেশ করা
        if (error.response.status === 401) {
            // নতুন Access Token জেনারেট করা
            await refreshAccessToken();

            // পুনরায় রিকুয়েস্ট পাঠানো নতুন টোকেন সহ
            const retryResponse = await axios.get(url, {
                withCredentials: true,
            });

            return retryResponse.data;
        }
        throw error; // অন্য ত্রুটি থাকলে হ্যান্ডেল করা
    }
};


Django কিভাবে 401 Unauthorized রেসপন্স পাঠায়?

আপনার API তে যদি টোকেন ব্যবহার করা হয় এবং সেই API এর উপরে permission_classes বা authentication_classes ডেকোরেটর (decorator) থাকে, তাহলে Django স্বয়ংক্রিয়ভাবে টোকেন চেক করবে। টোকেন যদি বৈধ না হয়, কিংবা তার মেয়াদ শেষ হয়ে যায়, তাহলে Django 401 Unauthorized রেসপন্স দিয়ে দিবে।

উদাহরণস্বরূপ, নিচের কোডটি একটি প্রোটেক্টেড API ভিউ দেখাচ্ছে:

from rest_framework.views import APIView
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response

class ProtectedView(APIView):
    permission_classes = [IsAuthenticated]  # এটি টোকেন যাচাই করবে

    def get(self, request):
        return Response({"message": "এটি একটি রোটেকটেড ভিউ, টোকেন রযোজন।"})

লগইন করার সময় টোকেন কোথায় সেভ হচ্ছে?

কোডে JWT Tokens (যেমন Access Token এবং Refresh Token) তৈরি করা হচ্ছে। এই টোকেনগুলো মূলত ডাটাবেসে সেভ হচ্ছে না। এগুলো ইউজারের কুকি (cookies) বা ক্লায়েন্ট সাইডে রাখা হচ্ছে।

response.set_cookie(
    key='refresh_token',
    value=tokens['refresh'],  # এই টোকেনটি ক্লায়েন্টের কুকিতে রাখা হচ্ছে
    httponly=True,  # JavaScript থেকে অ্যাক্সেস করা যাবে না
    secure=True,  # শুধুমাত্র HTTPS এর মাধ্যমে ট্রান্সমিট করা যাবে
    samesite='Strict',  # Cross-site অনুরোধ রোধ করবে
    max_age=settings.SIMPLE_JWT['REFRESH_TOKEN_LIFETIME'].total_seconds()  # কুকির মেয়াদ নির্ধারণ
)

এখানে, Refresh Token ইউজারের কুকিতে সেট করা হচ্ছে। যখন ইউজার আবার রিকোয়েস্ট পাঠায়, তখন তাদের ব্রাউজার সেই কুকি থেকে টোকেনটি অটোমেটিক্যালি পাঠায়।

তাহলে মূল কথা হলো: লগইনের সময় টোকেন ডাটাবেসে সেভ হয় না, এটি কুকি হিসেবে ক্লায়েন্ট সাইডে রাখা হয়।

Refresh Token কোথা থেকে আসছে?

তুমি যে অংশে refresh_token = request.data["refresh"] এই কোডটি ব্যবহার করছো, এটি নির্ভর করছে তুমি refresh_token কীভাবে ক্লায়েন্ট থেকে সার্ভারে পাঠাচ্ছো। তুমি যদি refresh_token-কে কুকিতে সেট করে থাকো, তাহলে তুমি ক্লায়েন্ট থেকে কুকির মাধ্যমে এই টোকেনটি পাঠাবে।

তুমি যদি কুকি হিসেবে ব্যবহার করছো, তাহলে তুমি এভাবে সরাসরি request.COOKIES থেকে টোকেন নিতে পারো:

refresh_token = request.COOKIES.get('refresh_token')

অথবা তুমি সরাসরি ক্লায়েন্টের বডি থেকে request.data["refresh"] এর মাধ্যমে টোকেন নিতে পারো, যদি ক্লায়েন্ট রিকোয়েস্টের বডিতে টোকেন পাঠায়।

Refresh Token সম্পর্কে ভালোভাবে বোঝা

Refresh Token সম্পর্কে ভালোভাবে বোঝার জন্য প্রথমে JWT (JSON Web Token) এর সংক্ষিপ্ত ধারণা প্রয়োজন। JWT মূলত দুটি অংশে বিভক্ত হয়:

  1. Access Token – যা অল্প সময়ের জন্য কার্যকরী থাকে এবং ক্লায়েন্টকে সার্ভারে অনুরোধ পাঠানোর অনুমতি দেয়।
  2. Refresh Token – যা দীর্ঘ সময়ের জন্য কার্যকরী থাকে এবং Access Token এর মেয়াদ শেষ হয়ে গেলে নতুন Access Token জেনারেট করার জন্য ব্যবহৃত হয়।

Refresh Token কী?

Refresh Token ব্যবহার করে আমরা Access Token এর মেয়াদ শেষ হয়ে গেলে নতুন Access Token নিতে পারি। Access Token সাধারণত কম সময়ের জন্য ব্যবহৃত হয় (যেমন ৫-১০ মিনিট), তাই যখন Access Token এক্সপায়ার হয়ে যায়, তখন ক্লায়েন্ট নতুন করে লগইন না করেই Refresh Token ব্যবহার করে একটি নতুন Access Token নিতে পারে। Refresh Token সাধারণত বেশি সময়ের জন্য বৈধ থাকে (যেমন ৭ দিন বা ৩০ দিন)।

Refresh Token এর ব্যবহার:

আপনার দেওয়া কোডের মধ্যে Refresh Token এর ব্যবহার এবং এর সাথে সম্পর্কিত অন্যান্য বিষয়গুলো ব্যাখ্যা করা হলো:

1. Refresh Token তৈরি করা:

def get_tokens_for_user(user):
    refresh = RefreshToken.for_user(user)  # ইউজারের জন্য একটি নতুন Refresh Token জেনারেট করা
    refresh['email'] = user.email  # টোকেনের সাথে ইউজারের ইমেইল যুক্ত করা
    refresh['username'] = user.username  # টোকেনের সাথে ইউজারের ইউজারনেম যুক্ত করা

    return {
        'refresh': str(refresh),  # Refresh Token রিটার্ন করা
        'access': str(refresh.access_token),  # Access Token রিটার্ন করা
        'user': {
            'id': user.id,
            'email': user.email,
            'username': user.username,
        }
    }

এই ফাংশনটি ইউজারের জন্য Refresh Token এবং Access Token তৈরি করে। এরপর আমরা এই টোকেনগুলো রেসপন্সে ক্লায়েন্টের কাছে পাঠাই।

2. Refresh Token কুকিতে সংরক্ষণ করা:

response.set_cookie(
    key='refresh_token',
    value=tokens['refresh'],
    httponly=True,  # JavaScript থেকে এই কুকিতে অ্যাক্সেস করা যাবে না
    secure=True,  # শুধুমাত্র HTTPS এর মাধ্যমে কুকি পাঠানো যাবে
    samesite='Strict',  # Cross-site অনুরোধ প্রতিরোধ করা হবে, CSRF থেকে সুরক্ষা
    max_age=settings.SIMPLE_JWT['REFRESH_TOKEN_LIFETIME'].total_seconds()  # কুকির মেয়াদ কতক্ষণ থাকবে সেটি নির্ধারণ করা
)

1. httponly=True:

  • কাজ: এই প্যারামিটারটি কুকিকে HTTP-only হিসেবে চিহ্নিত করে।
  • ব্যাখ্যা: যখন একটি কুকিকে httponly হিসেবে সেট করা হয়, তখন এটি ব্রাউজারের JavaScript কোড দ্বারা অ্যাক্সেস করা যায় না। অর্থাৎ, কোনো cross-site scripting (XSS) আক্রমণ ঘটলেও, অ্যাটাকার আপনার কুকির তথ্য পাবে না, যেহেতু JavaScript এই কুকি পড়তে পারবে না।
  • উদাহরণ: সাইটে যদি XSS আক্রমণ ঘটে এবং জাভাস্ক্রিপ্ট ব্যবহারের মাধ্যমে আপনার কুকির অ্যাক্সেস নেওয়ার চেষ্টা করে, এটি সফল হবে না।

2. secure=True:

  • কাজ: এই প্যারামিটারটি নিশ্চিত করে যে কুকি কেবলমাত্র HTTPS কানেকশনের মাধ্যমে পাঠানো হবে।
  • ব্যাখ্যা: secure=True মানে কুকিটি শুধু তখনই ব্রাউজারের মাধ্যমে পাঠানো হবে যখন কানেকশনটি HTTPS (Hypertext Transfer Protocol Secure) এর মাধ্যমে তৈরি হয়। এটি কুকি স্নিফিং বা man-in-the-middle (MITM) আক্রমণের বিরুদ্ধে সুরক্ষা প্রদান করে।
  • উদাহরণ: যদি আপনার সাইট HTTP ব্যবহার করে (যা নিরাপদ নয়), তাহলে কুকি পাঠানো হবে না। শুধু HTTPS কানেকশন হলে কুকি ব্রাউজারে পাঠানো যাবে।

3. samesite='Strict':

  • কাজ: এটি CSRF (Cross-Site Request Forgery) আক্রমণ প্রতিরোধে সাহায্য করে।
  • ব্যাখ্যা: samesite='Strict' মানে হলো, কুকিটি কেবলমাত্র তখনই পাঠানো হবে যখন ক্লায়েন্ট একই ডোমেইন থেকে রিকোয়েস্ট করবে। যদি অন্য কোনো ওয়েবসাইট থেকে রিকোয়েস্ট আসে (যেমন: ফর্ম সাবমিশন), তখন কুকিটি পাঠানো হবে না। এটি Cross-Site Request Forgery (CSRF) আক্রমণের বিরুদ্ধে প্রতিরোধ গড়ে তোলে।
  • উদাহরণ: ধরুন, কেউ আপনার ইউজারের কুকি ব্যবহার করে অন্য সাইট থেকে রিকোয়েস্ট পাঠানোর চেষ্টা করে, তাহলে কুকি সেই রিকোয়েস্টে যাবে না কারণ এটি “SameSite” পলিসির কারণে আটকে যাবে।

4. max_age=settings.SIMPLE_JWT['REFRESH_TOKEN_LIFETIME'].total_seconds():

উদাহরণ: ধরুন, REFRESH_TOKEN_LIFETIME ১ দিন সেট করা আছে। তাহলে কুকিটি ২৪ ঘণ্টার জন্য বৈধ থাকবে। এরপর, এটি স্বয়ংক্রিয়ভাবে expire হয়ে যাবে এবং ব্যবহারকারীদের নতুন টোকেন জেনারেট করতে হবে।

কাজ: এটি কুকির লাইফটাইম (মেয়াদ) নির্ধারণ করে।

ব্যাখ্যা: max_age এর মান হলো কুকিটি কত সময় পর্যন্ত বৈধ থাকবে। এখানে এটি SIMPLE_JWT সেটিং থেকে REFRESH_TOKEN_LIFETIME নেয়া হয়েছে, যা সেকেন্ডে নির্ধারণ করা হয়। এই সেটিং অনুযায়ী, কুকি নির্দিষ্ট সময় পরে এক্সপায়ার হয়ে যাবে।

3. Refresh Token এর মাধ্যমে Access Token নবায়ন:

কখনো কখনো Access Token এর মেয়াদ শেষ হয়ে যেতে পারে। এমন অবস্থায় ইউজারকে পুনরায় লগইন না করেই আমরা Refresh Token দিয়ে নতুন Access Token নিতে পারি। এর জন্য আমরা একটি API রিকোয়েস্ট পাঠাই যেখানে Refresh Token সরবরাহ করি এবং এর বিনিময়ে সার্ভার থেকে নতুন Access Token পাই।

4. Refresh Token ব্ল্যাকলিস্ট করা (Logout এর সময়):

যখন ইউজার লগআউট করে, তখন আমরা Refresh Token কে blacklist করি যাতে সেই টোকেনটি আর ব্যবহার করা না যায়। নিচে এর উদাহরণ দেওয়া হলো:

class LogoutView(APIView):
    def post(self, request):
        try:
            refresh_token = request.data["refresh"]  # রিকোয়েস্ট থেকে Refresh Token নেয়া
            token = RefreshToken(refresh_token)
            token.blacklist()  # Refresh Token ব্ল্যাকলিস্ট করা
            return Response(status=status.HTTP_205_RESET_CONTENT)  # সফল হলে রেসপন্স দেয়া
        except Exception:
            return Response(status=status.HTTP_400_BAD_REQUEST)  # ব্যর্থ হলে এরর রেসপন্স

এখানে, Refresh Token কে ব্ল্যাকলিস্ট করা হচ্ছে, যাতে ইউজার লগআউট করার পরে সেই টোকেনটি আর ব্যবহার করা না যায়। এটি সিকিউরিটির জন্য গুরুত্বপূর্ণ কারণ, একটি টোকেন ব্যবহার করে বারবার অ্যাক্সেস নেয়া সম্ভব নয়।

Refresh Token ব্ল্যাকলিস্ট কীভাবে সেভ হচ্ছে?

Refresh Token ব্ল্যাকলিস্ট কীভাবে সেভ হচ্ছে?

JWT (JSON Web Token) এর বৈশিষ্ট্য অনুযায়ী, এটি ডাটাবেসে সেভ হয় না। তবে, তুমি যদি টোকেন ব্ল্যাকলিস্ট করতে চাও (যেমন লগআউট করার পর টোকেনটি আর ব্যবহার করতে না দেওয়া), তাহলে এর জন্য ডাটাবেস ব্যবহার করতে হবে।

Django Simple JWT প্যাকেজে Token Blacklisting নামের একটি ফিচার আছে, যা Refresh Token ব্ল্যাকলিস্ট করার সুযোগ দেয়। এটি টোকেনের অবস্থা (active, blacklisted) ডাটাবেসে সেভ করে রাখে।

Token Blacklisting কিভাবে কাজ করে?

Token Blacklisting কিভাবে কাজ করে?

যখন তুমি একটি টোকেন ব্ল্যাকলিস্ট করছো, তখন সেটি ডাটাবেসে সংরক্ষণ করা হয়। Django Simple JWT প্যাকেজের BlacklistedToken মডেলটি ব্যবহার করে ব্ল্যাকলিস্ট হওয়া টোকেনগুলোর রেকর্ড ডাটাবেসে রাখা হয়।

উদাহরণ:

from rest_framework_simplejwt.tokens import RefreshToken

def logout(request):
    try:
        refresh_token = request.data["refresh"]  # রিফ্রেশ টোকেন নেয়া
        token = RefreshToken(refresh_token)  # রিফ্রেশ টোকেনকে ভেরিফাই করা
        token.blacklist()  # রিফ্রেশ টোকেনকে ব্ল্যাকলিস্ট করা
        return Response(status=status.HTTP_205_RESET_CONTENT)  # সফল হলে রেসপন্স দেয়া
    except Exception:
        return Response(status=status.HTTP_400_BAD_REQUEST)  # ব্যর্থ হলে এরর রেসপন্স

এখানে token.blacklist() ফাংশনটি refresh token-কে ব্ল্যাকলিস্ট করছে, এবং এটি ডাটাবেসে সংরক্ষণ করছে।

How can we help?