কাস্টম ব্যাকএন্ড থেকে App Check টোকেন যাচাই করা

আপনার অ্যাপের জন্য Google-এর নয় এমন কাস্টম ব্যাকএন্ড রিসোর্স, যেমন আপনার নিজের সেল্ফ-হোস্টেড ব্যাকএন্ড সুরক্ষিত করতে আপনি App Check ব্যবহার করতে পারেন। এটি করতে, আপনাকে নিম্নলিখিত দুটি কাজই করতে হবে:

  • আপনার ব্যাকএন্ডে প্রতিটি অনুরোধের সাথে App Check টোকেন পাঠাতে আপনার অ্যাপ ক্লায়েন্টকে পরিবর্তন করুন, যেমনটি iOS+, Android, ওয়েব, Flutter, Unity, অথবা C++-এর জন্য পৃষ্ঠায় বর্ণনা করা হয়েছে।
  • এই পৃষ্ঠায় যেমন বর্ণনা করা হয়েছে, সেই অনুযায়ী প্রতিটি অনুরোধের সাথে একটি সঠিক App Check টোকেন প্রয়োজন হওয়ার জন্য আপনার ব্যাকএন্ড পরিবর্তন করুন।

টোকেন যাচাইকরণ

আপনার ব্যাকএন্ডে App Check টোকেন যাচাই করতে, আপনার API এন্ডপয়েন্টে লজিক যোগ করুন যা নিম্নলিখিত কাজ করে:

  • প্রতিটি অনুরোধে App Check টোকেন আছে কিনা তা চেক করুন।

  • অ্যাডমিন SDK ব্যবহার করে App Check টোকেন যাচাই করুন।

    যাচাইকরণ সফল হলে, Admin SDK ডিকোড করা App Check টোকেন ফিরিয়ে দেয়। যাচাইকরণ সম্পূর্ণ হলে বোঝা যায় যে টোকেনটি আপনার Firebase প্রোজেক্টের কোনও অ্যাপ থেকে এসেছে।

যেকোনও একটি চেক সম্পূর্ণ না হলে অনুরোধ বাতিল করুন। যেমন:

Node.js

আপনি যদি আগে থেকে Node.js অ্যাডমিন SDK ইনস্টল না করে থাকেন, তাহলে তা করুন।

তারপর, উদাহরণ হিসেবে Express.js মিডলওয়্যার ব্যবহার করে:

import express from "express";
import { initializeApp } from "firebase-admin/app";
import { getAppCheck } from "firebase-admin/app-check";

const expressApp = express();
const firebaseApp = initializeApp();

const appCheckVerification = async (req, res, next) => {
    const appCheckToken = req.header("X-Firebase-AppCheck");

    if (!appCheckToken) {
        res.status(401);
        return next("Unauthorized");
    }

    try {
        const appCheckClaims = await getAppCheck().verifyToken(appCheckToken);

        // If verifyToken() succeeds, continue with the next middleware
        // function in the stack.
        return next();
    } catch (err) {
        res.status(401);
        return next("Unauthorized");
    }
}

expressApp.get("/yourApiEndpoint", [appCheckVerification], (req, res) => {
    // Handle request.
});

Python

আপনি যদি আগে থেকেই Python Admin SDK ইনস্টল না করে থাকেন, তাহলে তা করুন।

তারপর, আপনার API এন্ডপয়েন্ট হ্যান্ডলারে, app_check.verify_token() কল করুন এবং ব্যর্থ হলে অনুরোধ বাতিল করুন। নিম্নলিখিত উদাহরণে, @before_request-এর মাধ্যমে সাজানো একটি ফাংশন সব অনুরোধের জন্য এই টাস্কটি সম্পাদন করে:

import firebase_admin
from firebase_admin import app_check
import flask
import jwt

firebase_app = firebase_admin.initialize_app()
flask_app = flask.Flask(__name__)

@flask_app.before_request
def verify_app_check() -> None:
    app_check_token = flask.request.headers.get("X-Firebase-AppCheck", default="")
    try:
        app_check_claims = app_check.verify_token(app_check_token)
        # If verify_token() succeeds, okay to continue to route handler.
    except (ValueError, jwt.exceptions.DecodeError):
        flask.abort(401)

@flask_app.route("/yourApiEndpoint")
def your_api_endpoint(request: flask.Request):
    # Handle request.
    ...

Go

আপনি যদি আগে থেকে Go-এর জন্য Admin SDK ইনস্টল না করে থাকেন, তাহলে তা করুন।

তারপর, আপনার API এন্ডপয়েন্ট হ্যান্ডলারে, appcheck.Client.VerifyToken() কল করুন এবং এটি ব্যর্থ হলে অনুরোধটি বাতিল করুন। নিম্নলিখিত উদাহরণে, একটি র‍্যাপার ফাংশন এন্ডপয়েন্ট হ্যান্ডলারগুলিতে এই লজিক যোগ করে:

package main

import (
    "context"
    "log"
    "net/http"

    firebaseAdmin "firebase.google.com/go/v4"
    "firebase.google.com/go/v4/appcheck"
)

var (
    appCheck *appcheck.Client
)

func main() {
    app, err := firebaseAdmin.NewApp(context.Background(), nil)
    if err != nil {
        log.Fatalf("error initializing app: %v\n", err)
    }

    appCheck, err = app.AppCheck(context.Background())
    if err != nil {
        log.Fatalf("error initializing app: %v\n", err)
    }

    http.HandleFunc("/yourApiEndpoint", requireAppCheck(yourApiEndpointHandler))
    log.Fatal(http.ListenAndServe(":8080", nil))
}

func requireAppCheck(handler func(http.ResponseWriter, *http.Request)) func(http.ResponseWriter, *http.Request) {
    wrappedHandler := func(w http.ResponseWriter, r *http.Request) {
        appCheckToken, ok := r.Header[http.CanonicalHeaderKey("X-Firebase-AppCheck")]
        if !ok {
            w.WriteHeader(http.StatusUnauthorized)
            w.Write([]byte("Unauthorized."))
            return
        }

        _, err := appCheck.VerifyToken(appCheckToken[0])
        if err != nil {
            w.WriteHeader(http.StatusUnauthorized)
            w.Write([]byte("Unauthorized."))
            return
        }

        // If VerifyToken() succeeds, continue with the provided handler.
        handler(w, r)
    }
    return wrappedHandler
}

func yourApiEndpointHandler(w http.ResponseWriter, r *http.Request) {
    // Handle request.
}

জাভা

আপনি যদি আগে থেকে Java-র জন্য Admin SDK (ভার্সন 9.12.0 বা তার পরবর্তী) ইনস্টল না করে থাকেন, তাহলে তা করুন।

তারপর, আপনার API এন্ডপয়েন্ট হ্যান্ডলার বা অনুরোধ ফিল্টারে, FirebaseAppCheck.getInstance().verifyToken() (বা verifyTokenAsync()) কল করুন এবং এটি কাজ না করলে অনুরোধ বাতিল করুন। ইনকামিং অনুরোধের টোকেন যাচাই করতে নিম্নলিখিত উদাহরণে Spring Boot ফিল্টার ব্যবহার করা হয়েছে:

import com.google.firebase.FirebaseApp;
import com.google.firebase.appcheck.FirebaseAppCheck;
import com.google.firebase.appcheck.FirebaseAppCheckException;
import com.google.firebase.appcheck.VerifyAppCheckTokenResponse;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.filter.OncePerRequestFilter;

@SpringBootApplication
@RestController
public class Application {

    public static void main(String[] args) {
        FirebaseApp.initializeApp();
        SpringApplication.run(Application.class, args);
    }

    @GetMapping("/yourApiEndpoint")
    public String yourApiEndpoint() {
        // Handle request.
        return "Success";
    }

    @Component
    public static class AppCheckFilter extends OncePerRequestFilter {
        @Override
        protected void doFilterInternal(
                HttpServletRequest request,
                HttpServletResponse response,
                FilterChain filterChain) throws ServletException, IOException {
            String appCheckToken = request.getHeader("X-Firebase-AppCheck");
            if (appCheckToken == null || appCheckToken.isEmpty()) {
                response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
                return;
            }

            try {
                VerifyAppCheckTokenResponse appCheckResponse =
                    FirebaseAppCheck.getInstance().verifyToken(appCheckToken);
                // If verifyToken() succeeds, continue with the filter chain.
                filterChain.doFilter(request, response);
            } catch (FirebaseAppCheckException e) {
                response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
            }
        }
    }
}

অন্যান্য

আপনার ব্যাকএন্ড অন্য ভাষায় লেখা হলে, আপনি সাধারণ-উদ্দেশ্যের JWT লাইব্রেরি ব্যবহার করতে পারেন, যেমন jwt.io-তে পাওয়া যায়, App Check টোকেন যাচাই করতে।

আপনার টোকেন যাচাইকরণ লজিককে অবশ্যই নিম্নলিখিত ধাপগুলি সম্পূর্ণ করতে হবে:

  1. App Check JWKS এন্ডপয়েন্ট থেকে Firebase App Check পাবলিক JSON Web Key (JWK) সেট পান: https://firebaseappcheck.googleapis.com/v1/jwks
  2. App Check টোকেনের স্বাক্ষর বৈধ কিনা তা নিশ্চিত করতে যাচাই করুন।
  3. টোকেনের হেডার RS256 অ্যালগরিদম ব্যবহার করছে কিনা তা নিশ্চিত করুন।
  4. টোকেনের হেডারে JWT টাইপ আছে কিনা তা নিশ্চিত করুন।
  5. আপনার প্রোজেক্টের অধীনে Firebase App Check-এর মাধ্যমে টোকেন ইস্যু করা হয়েছে কিনা তা নিশ্চিত করুন।
  6. টোকেনের মেয়াদ শেষ হয়ে যায়নি তা নিশ্চিত করুন।
  7. টোকেনের দর্শক আপনার প্রোজেক্টের সাথে মিলছে কিনা তা নিশ্চিত করুন।
  8. ঐচ্ছিক: টোকেনের সাবজেক্ট আপনার অ্যাপের অ্যাপ আইডির সাথে ম্যাচ করছে কিনা তা চেক করুন।

JWT লাইব্রেরির ক্ষমতা আলাদা হতে পারে; আপনার বেছে নেওয়া লাইব্রেরি যে ধাপগুলি সম্পূর্ণ করেনি সেগুলি ম্যানুয়ালি সম্পূর্ণ করতে ভুলবেন না।

নিম্নলিখিত উদাহরণে Ruby-তে প্রয়োজনীয় ধাপগুলি দেখানো হয়েছে, যেখানে jwt জেমকে Rack মিডলওয়্যার লেয়ার হিসেবে ব্যবহার করা হয়েছে।

require 'json'
require 'jwt'
require 'net/http'
require 'uri'

class AppCheckVerification
def initialize(app, options = {})
    @app = app
    @project_number = options[:project_number]
end

def call(env)
    app_id = verify(env['HTTP_X_FIREBASE_APPCHECK'])
    return [401, { 'Content-Type' => 'text/plain' }, ['Unauthenticated']] unless app_id
    env['firebase.app'] = app_id
    @app.call(env)
end

def verify(token)
    return unless token

    # 1. Obtain the Firebase App Check Public Keys
    # Note: It is not recommended to hard code these keys as they rotate,
    # but you should cache them for up to 6 hours.
    uri = URI('https://firebaseappcheck.googleapis.com/v1/jwks')
    jwks = JSON(Net::HTTP.get(uri))

    # 2. Verify the signature on the App Check token
    payload, header = JWT.decode(token, nil, true, jwks: jwks, algorithms: 'RS256')

    # 3. Ensure the token's header uses the algorithm RS256
    return unless header['alg'] == 'RS256'

    # 4. Ensure the token's header has type JWT
    return unless header['typ'] == 'JWT'

    # 5. Ensure the token is issued by App Check
    return unless payload['iss'] == "https://firebaseappcheck.googleapis.com/#{@project_number}"

    # 6. Ensure the token is not expired
    return unless payload['exp'] > Time.new.to_i

    # 7. Ensure the token's audience matches your project
    return unless payload['aud'].include? "projects/#{@project_number}"

    # 8. The token's subject will be the app ID, you may optionally filter against
    # an allow list
    payload['sub']
rescue
end
end

class Application
def call(env)
    [200, { 'Content-Type' => 'text/plain' }, ["Hello app #{env['firebase.app']}"]]
end
end

use AppCheckVerification, project_number: 1234567890
run Application.new

রিপ্লে সুরক্ষা (বিটা)

রিপ্লে অ্যাটাক থেকে এন্ডপয়েন্ট সুরক্ষিত রাখতে, আপনি App Check টোকেন যাচাই করার পরে ব্যবহার করতে পারেন যাতে এটি শুধুমাত্র একবারই ব্যবহার করা যায়।

রিপ্লে সুরক্ষা ব্যবহার করলে টোকেন যাচাইকরণ কলে নেটওয়ার্ক রাউন্ড ট্রিপ যোগ হয়, তাই এটি ব্যবহার করে এমন যেকোনও এন্ডপয়েন্টে লেটেন্সি যোগ হয়। এই কারণে, আমরা সাজেস্ট করি যে আপনি শুধুমাত্র বিশেষ সংবেদনশীল এন্ডপয়েন্টে রিপ্লে সুরক্ষা চালু করুন।

রিপ্লে সুরক্ষা ব্যবহার করতে, নিম্নলিখিত ধাপগুলি অনুসরণ করুন:

  1. Cloud কনসোলে, টোকেন যাচাই করার জন্য ব্যবহৃত পরিষেবা অ্যাকাউন্টে "Firebase App Check টোকেন যাচাইকারী" ভূমিকা প্রদান করুন।

    • আপনি যদি Firebase কনসোল থেকে ডাউনলোড করা Admin SDK পরিষেবা অ্যাকাউন্ট ক্রেডেনশিয়ালের মাধ্যমে Admin SDK শুরু করেন, তাহলে প্রয়োজনীয় ভূমিকা আগে থেকেই মঞ্জুর করা আছে।
    • আপনি যদি ডিফল্ট অ্যাডমিন SDK কনফিগারেশন সহ ১ম জেনারেশন Cloud Functions ব্যবহার করেন, তাহলে App Engine ডিফল্ট পরিষেবা অ্যাকাউন্ট-কে ভূমিকাটি দিন। পরিষেবা অ্যাকাউন্টের অনুমতি পরিবর্তন করা দেখুন।
    • আপনি যদি ডিফল্ট অ্যাডমিন SDK কনফিগারেশন সহ ২য় জেনারেশন Cloud Functions ব্যবহার করেন, তাহলে ডিফল্ট কম্পিউট পরিষেবা অ্যাকাউন্ট-কে ভূমিকাটি অ্যাসাইন করুন।
  2. টোকেন ব্যবহার করতে, টোকেন যাচাইকরণ কলে { consume: true } (Node.js), consume=True (Python) বা VerifyAppCheckTokenOptions-এর সাথে setConsume(true) (Java) পাস করুন অথবা VerifyOneTimeToken() (Go) কল করুন এবং তারপর ফলাফল অবজেক্ট পরীক্ষা করুন। টোকেনটি আগে থেকেই ব্যবহার করা হয়ে থাকলে, অনুরোধটি বাতিল করুন বা সংশোধনমূলক অ্যাকশন নিন, যেমন কলারকে অন্যান্য চেক পাস করতে হবে:

    Node.js

    const appCheckClaims = await getAppCheck().verifyToken(appCheckToken, { consume: true });
    
    if (appCheckClaims.alreadyConsumed) {
        res.status(401);
        return next('Unauthorized');
    }
    
    // If verifyToken() succeeds and alreadyConsumed is not set, okay to continue.
    

    এটি টোকেন যাচাই করে এবং তারপরে এটি ব্যবহার করা হয়েছে বলে ফ্ল্যাগ করে। একই টোকেনে verifyToken(appCheckToken, { consume: true })-এর ভবিষ্যৎ ইনভোকেশন alreadyConsumed থেকে true-এ সেট করা হয়েছে। (মনে রাখবেন যে verifyToken() ব্যবহার করা টোকেন বাতিল করে না বা consume সেট করা না থাকলে এটি ব্যবহার করা হয়েছে কিনা তা চেক করে না।)

    Python

    app_check_claims = app_check.verify_token(app_check_token, consume=True)
    
    if app_check_claims.get("already_consumed"):
        return "App Check token already consumed", 401
    
    # If verify_token() succeeds and already_consumed is not set, okay to continue.
    

    এটি টোকেন যাচাই করে এবং তারপরে এটি ব্যবহার করা হয়েছে বলে ফ্ল্যাগ করে। একই টোকেনে app_check.verify_token(app_check_token, consume=True)-এর ভবিষ্যতের ইনভোকেশন already_consumed থেকে True-এ সেট করা হয়েছে। (মনে রাখবেন যে app_check.verify_token() ব্যবহার করা টোকেন বাতিল করে না বা এমনকি consume সেট করা না থাকলে এটি ব্যবহার করা হয়েছে কিনা তাও চেক করে না।)

    Go

    decodedToken, err := appCheck.VerifyOneTimeToken(r.Context(), appCheckToken[0])
    if err != nil || (decodedToken.AlreadyConsumed != nil && *decodedToken.AlreadyConsumed) {
        w.WriteHeader(http.StatusUnauthorized)
        w.Write([]byte("Unauthorized."))
        return
    }
    
    // If VerifyOneTimeToken() succeeds and AlreadyConsumed is not true, okay to
    // continue with the provided handler.
    

    এটি টোকেন যাচাই করে এবং তারপরে এটি ব্যবহার করা হয়েছে বলে ফ্ল্যাগ করে। একই টোকেন সেটে appCheck.VerifyOneTimeToken()-এর ভবিষ্যৎ ইনভোকেশন AlreadyConsumed থেকে true-এ সেট করা হয়েছে। (মনে রাখবেন যে appCheck.VerifyToken() ব্যবহার করা টোকেন বাতিল করে না বা এমনকি এটি ব্যবহার করা হয়েছে কিনা তাও চেক করে না।)

    জাভা

    VerifyAppCheckTokenOptions options = VerifyAppCheckTokenOptions.builder()
        .setConsume(true)
        .build();
    VerifyAppCheckTokenResponse appCheckResponse =
        FirebaseAppCheck.getInstance().verifyToken(appCheckToken, options);
    
    if (appCheckResponse.isAlreadyConsumed().orElse(false)) {
        response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
        return;
    }
    
    // If verifyToken() succeeds and isAlreadyConsumed() is not true, continue
    // with the filter chain.
    

    এটি টোকেন যাচাই করে এবং তারপরে এটি ব্যবহার করা হয়েছে বলে ফ্ল্যাগ করে। একই টোকেনে setConsume(true)-এর সাথে verifyToken(appCheckToken, options)-এর ভবিষ্যতের ইনভোকেশন isAlreadyConsumed() থেকে Optional.of(true) রিটার্ন করে। (মনে রাখবেন যে verifyToken() ব্যবহার করা টোকেন বাতিল করে না বা setConsume(true) সেট করা না থাকলে এটি ব্যবহার করা হয়েছে কিনা তাও চেক করে না।)

আপনি কোনও নির্দিষ্ট এন্ডপয়েন্টের জন্য এই ফিচার চালু করলে, এন্ডপয়েন্টের সাথে ব্যবহার করার জন্য কনজিউমেবল সীমিত-ব্যবহারের টোকেন পেতে আপনাকে অবশ্যই অ্যাপ ক্লায়েন্ট কোড আপডেট করতে হবে। Apple প্ল্যাটফর্ম, Android এবং ওয়েব-এর জন্য ক্লায়েন্ট-সাইড ডকুমেন্ট দেখুন।