إدارة ملفات تعريف الارتباط للجلسة

توفّر مصادقة Firebase إدارة ملفات تعريف الارتباط للجلسة من جانب الخادم للعمليات مواقع الويب التي تعتمد على ملفات تعريف ارتباط الجلسة. ويتميز هذا الحل بالعديد من المزايا رموز تعريف قصيرة الأجل من جهة العميل، والتي قد تتطلب آلية إعادة توجيه لكل منها الوقت المُستغرق لتعديل ملفّ تعريف ارتباط الجلسة عند انتهاء الصلاحية:

  • تحسين الأمان من خلال الرموز المميّزة للجلسة المستندة إلى JWT والتي لا يمكن إنشاؤها إلّا باستخدام حسابات الخدمة المعتمَدة.
  • ملفات تعريف ارتباط الجلسة بلا حالة والتي لها مزايا استخدام JWT للمصادقة يمتلك ملف تعريف الارتباط للجلسة المطالبات نفسها (بما في ذلك المطالبات المخصّصة) التي يتضمنها الرمز المميّز لرقم التعريف، ما يجعل عمليات فحص الأذونات نفسها قابلة للتنفيذ على ملفات تعريف الارتباط للجلسة.
  • إمكانية إنشاء ملفّات تعريف ارتباط للجلسة بأوقات انتهاء صلاحية مخصّصة تتراوح بين 5 دقائق وأسبوعَين
  • المرونة في فرض سياسات ملفات تعريف الارتباط استنادًا إلى متطلبات التطبيق: النطاق، والمسار، والأمان، وhttpOnly، وما إلى ذلك
  • إمكانية إبطال ملفات تعريف الارتباط للجلسة عند الاشتباه في سرقة الرمز المميّز باستخدام واجهة برمجة التطبيقات الحالية لإبطال الرموز المميّزة لإعادة التحميل
  • القدرة على اكتشاف إبطال الجلسة في التغييرات الرئيسية في الحساب.

تسجيل الدخول

إذا كان أحد التطبيقات يستخدم ملفات تعريف الارتباط من جهة الخادم على httpOnly، يجب تسجيل الدخول إلى حساب المستخدم. في صفحة تسجيل الدخول باستخدام حِزم تطوير البرامج (SDK) للعميل. يتم إنشاء رمز مميّز لرقم تعريف Firebase ثم يتم إرسال الرمز المميز للمعرّف من خلال HTTP POST إلى نقطة نهاية تسجيل الدخول إلى الجلسة، حيث يمكن أن SDK للمشرف، يتم إنشاء ملف تعريف ارتباط للجلسة. عند النجاح، ينبغي أن تكون الولاية محوها من مساحة التخزين من جانب العميل.

firebase.initializeApp({
  apiKey: 'AIza…',
  authDomain: '<PROJECT_ID>.firebasepp.com'
});

// As httpOnly cookies are to be used, do not persist any state client side.
firebase.auth().setPersistence(firebase.auth.Auth.Persistence.NONE);

// When the user signs in with email and password.
firebase.auth().signInWithEmailAndPassword('user@example.com', 'password').then(user => {
  // Get the user's ID token as it is needed to exchange for a session cookie.
  return user.getIdToken().then(idToken = > {
    // Session login endpoint is queried and the session cookie is set.
    // CSRF protection should be taken into account.
    // ...
    const csrfToken = getCookie('csrfToken')
    return postIdTokenToSessionLogin('/sessionLogin', idToken, csrfToken);
  });
}).then(() => {
  // A page redirect would suffice as the persistence is set to NONE.
  return firebase.auth().signOut();
}).then(() => {
  window.location.assign('/profile');
});

لإنشاء ملف تعريف ارتباط جلسة مقابل الرمز المميز للمعرّف المقدم، HTTP نقطة النهاية مطلوبة. إرسال الرمز المميّز إلى نقطة النهاية، مع ضبط جلسة مخصّصة المدة الزمنية باستخدام حزمة تطوير البرامج (SDK) لمشرف Firebase. يجب اتخاذ التدابير المناسبة لمنع هجمات تزوير الطلبات من مواقع إلكترونية متعددة (CSRF).

Node.js

app.post('/sessionLogin', (req, res) => {
  // Get the ID token passed and the CSRF token.
  const idToken = req.body.idToken.toString();
  const csrfToken = req.body.csrfToken.toString();
  // Guard against CSRF attacks.
  if (csrfToken !== req.cookies.csrfToken) {
    res.status(401).send('UNAUTHORIZED REQUEST!');
    return;
  }
  // Set session expiration to 5 days.
  const expiresIn = 60 * 60 * 24 * 5 * 1000;
  // Create the session cookie. This will also verify the ID token in the process.
  // The session cookie will have the same claims as the ID token.
  // To only allow session cookie setting on recent sign-in, auth_time in ID token
  // can be checked to ensure user was recently signed in before creating a session cookie.
  getAuth()
    .createSessionCookie(idToken, { expiresIn })
    .then(
      (sessionCookie) => {
        // Set cookie policy for session cookie.
        const options = { maxAge: expiresIn, httpOnly: true, secure: true };
        res.cookie('session', sessionCookie, options);
        res.end(JSON.stringify({ status: 'success' }));
      },
      (error) => {
        res.status(401).send('UNAUTHORIZED REQUEST!');
      }
    );
});

جافا

@POST
@Path("/sessionLogin")
@Consumes("application/json")
public Response createSessionCookie(LoginRequest request) {
  // Get the ID token sent by the client
  String idToken = request.getIdToken();
  // Set session expiration to 5 days.
  long expiresIn = TimeUnit.DAYS.toMillis(5);
  SessionCookieOptions options = SessionCookieOptions.builder()
      .setExpiresIn(expiresIn)
      .build();
  try {
    // Create the session cookie. This will also verify the ID token in the process.
    // The session cookie will have the same claims as the ID token.
    String sessionCookie = FirebaseAuth.getInstance().createSessionCookie(idToken, options);
    // Set cookie policy parameters as required.
    NewCookie cookie = new NewCookie("session", sessionCookie /* ... other parameters */);
    return Response.ok().cookie(cookie).build();
  } catch (FirebaseAuthException e) {
    return Response.status(Status.UNAUTHORIZED).entity("Failed to create a session cookie")
        .build();
  }
}

Python

@app.route('/sessionLogin', methods=['POST'])
def session_login():
    # Get the ID token sent by the client
    id_token = flask.request.json['idToken']
    # Set session expiration to 5 days.
    expires_in = datetime.timedelta(days=5)
    try:
        # Create the session cookie. This will also verify the ID token in the process.
        # The session cookie will have the same claims as the ID token.
        session_cookie = auth.create_session_cookie(id_token, expires_in=expires_in)
        response = flask.jsonify({'status': 'success'})
        # Set cookie policy for session cookie.
        expires = datetime.datetime.now() + expires_in
        response.set_cookie(
            'session', session_cookie, expires=expires, httponly=True, secure=True)
        return response
    except exceptions.FirebaseError:
        return flask.abort(401, 'Failed to create a session cookie')

انتقال

return func(w http.ResponseWriter, r *http.Request) {
	// Get the ID token sent by the client
	defer r.Body.Close()
	idToken, err := getIDTokenFromBody(r)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	// Set session expiration to 5 days.
	expiresIn := time.Hour * 24 * 5

	// Create the session cookie. This will also verify the ID token in the process.
	// The session cookie will have the same claims as the ID token.
	// To only allow session cookie setting on recent sign-in, auth_time in ID token
	// can be checked to ensure user was recently signed in before creating a session cookie.
	cookie, err := client.SessionCookie(r.Context(), idToken, expiresIn)
	if err != nil {
		http.Error(w, "Failed to create a session cookie", http.StatusInternalServerError)
		return
	}

	// Set cookie policy for session cookie.
	http.SetCookie(w, &http.Cookie{
		Name:     "session",
		Value:    cookie,
		MaxAge:   int(expiresIn.Seconds()),
		HttpOnly: true,
		Secure:   true,
	})
	w.Write([]byte(`{"status": "success"}`))
}

#C

// POST: /sessionLogin
[HttpPost]
public async Task<ActionResult> Login([FromBody] LoginRequest request)
{
    // Set session expiration to 5 days.
    var options = new SessionCookieOptions()
    {
        ExpiresIn = TimeSpan.FromDays(5),
    };

    try
    {
        // Create the session cookie. This will also verify the ID token in the process.
        // The session cookie will have the same claims as the ID token.
        var sessionCookie = await FirebaseAuth.DefaultInstance
            .CreateSessionCookieAsync(request.IdToken, options);

        // Set cookie policy parameters as required.
        var cookieOptions = new CookieOptions()
        {
            Expires = DateTimeOffset.UtcNow.Add(options.ExpiresIn),
            HttpOnly = true,
            Secure = true,
        };
        this.Response.Cookies.Append("session", sessionCookie, cookieOptions);
        return this.Ok();
    }
    catch (FirebaseAuthException)
    {
        return this.Unauthorized("Failed to create a session cookie");
    }
}

بالنسبة إلى التطبيقات الحسّاسة، يجب التحقّق من auth_time قبل إصدار ملف تعريف ارتباط الجلسة، مما يقلل من فترة الهجوم في حالة سرقة الرمز المميز للمعرف:

Node.js

getAuth()
  .verifyIdToken(idToken)
  .then((decodedIdToken) => {
    // Only process if the user just signed in in the last 5 minutes.
    if (new Date().getTime() / 1000 - decodedIdToken.auth_time < 5 * 60) {
      // Create session cookie and set it.
      return getAuth().createSessionCookie(idToken, { expiresIn });
    }
    // A user that was not recently signed in is trying to set a session cookie.
    // To guard against ID token theft, require re-authentication.
    res.status(401).send('Recent sign in required!');
  });

جافا

// To ensure that cookies are set only on recently signed in users, check auth_time in
// ID token before creating a cookie.
FirebaseToken decodedToken = FirebaseAuth.getInstance().verifyIdToken(idToken);
long authTimeMillis = TimeUnit.SECONDS.toMillis(
    (long) decodedToken.getClaims().get("auth_time"));

// Only process if the user signed in within the last 5 minutes.
if (System.currentTimeMillis() - authTimeMillis < TimeUnit.MINUTES.toMillis(5)) {
  long expiresIn = TimeUnit.DAYS.toMillis(5);
  SessionCookieOptions options = SessionCookieOptions.builder()
      .setExpiresIn(expiresIn)
      .build();
  String sessionCookie = FirebaseAuth.getInstance().createSessionCookie(idToken, options);
  // Set cookie policy parameters as required.
  NewCookie cookie = new NewCookie("session", sessionCookie);
  return Response.ok().cookie(cookie).build();
}
// User did not sign in recently. To guard against ID token theft, require
// re-authentication.
return Response.status(Status.UNAUTHORIZED).entity("Recent sign in required").build();

Python

# To ensure that cookies are set only on recently signed in users, check auth_time in
# ID token before creating a cookie.
try:
    decoded_claims = auth.verify_id_token(id_token)
    # Only process if the user signed in within the last 5 minutes.
    if time.time() - decoded_claims['auth_time'] < 5 * 60:
        expires_in = datetime.timedelta(days=5)
        expires = datetime.datetime.now() + expires_in
        session_cookie = auth.create_session_cookie(id_token, expires_in=expires_in)
        response = flask.jsonify({'status': 'success'})
        response.set_cookie(
            'session', session_cookie, expires=expires, httponly=True, secure=True)
        return response
    # User did not sign in recently. To guard against ID token theft, require
    # re-authentication.
    return flask.abort(401, 'Recent sign in required')
except auth.InvalidIdTokenError:
    return flask.abort(401, 'Invalid ID token')
except exceptions.FirebaseError:
    return flask.abort(401, 'Failed to create a session cookie')

انتقال

return func(w http.ResponseWriter, r *http.Request) {
	// Get the ID token sent by the client
	defer r.Body.Close()
	idToken, err := getIDTokenFromBody(r)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	decoded, err := client.VerifyIDToken(r.Context(), idToken)
	if err != nil {
		http.Error(w, "Invalid ID token", http.StatusUnauthorized)
		return
	}
	// Return error if the sign-in is older than 5 minutes.
	if time.Now().Unix()-decoded.Claims["auth_time"].(int64) > 5*60 {
		http.Error(w, "Recent sign-in required", http.StatusUnauthorized)
		return
	}

	expiresIn := time.Hour * 24 * 5
	cookie, err := client.SessionCookie(r.Context(), idToken, expiresIn)
	if err != nil {
		http.Error(w, "Failed to create a session cookie", http.StatusInternalServerError)
		return
	}
	http.SetCookie(w, &http.Cookie{
		Name:     "session",
		Value:    cookie,
		MaxAge:   int(expiresIn.Seconds()),
		HttpOnly: true,
		Secure:   true,
	})
	w.Write([]byte(`{"status": "success"}`))
}

#C

// To ensure that cookies are set only on recently signed in users, check auth_time in
// ID token before creating a cookie.
var decodedToken = await FirebaseAuth.DefaultInstance.VerifyIdTokenAsync(idToken);
var authTime = new DateTime(1970, 1, 1).AddSeconds(
    (long)decodedToken.Claims["auth_time"]);

// Only process if the user signed in within the last 5 minutes.
if (DateTime.UtcNow - authTime < TimeSpan.FromMinutes(5))
{
    var options = new SessionCookieOptions()
    {
        ExpiresIn = TimeSpan.FromDays(5),
    };
    var sessionCookie = await FirebaseAuth.DefaultInstance.CreateSessionCookieAsync(
        idToken, options);
    // Set cookie policy parameters as required.
    this.Response.Cookies.Append("session", sessionCookie);
    return this.Ok();
}

// User did not sign in recently. To guard against ID token theft, require
// re-authentication.
return this.Unauthorized("Recent sign in required");

بعد تسجيل الدخول، يجب أن تتحقق كل الأقسام المحمية من موقع الويب ملف تعريف ارتباط الجلسة والتحقق منه قبل عرض محتوى محظور استنادًا إلى بعض حماية البيانات.

Node.js

// Whenever a user is accessing restricted content that requires authentication.
app.post('/profile', (req, res) => {
  const sessionCookie = req.cookies.session || '';
  // Verify the session cookie. In this case an additional check is added to detect
  // if the user's Firebase session was revoked, user deleted/disabled, etc.
  getAuth()
    .verifySessionCookie(sessionCookie, true /** checkRevoked */)
    .then((decodedClaims) => {
      serveContentForUser('/profile', req, res, decodedClaims);
    })
    .catch((error) => {
      // Session cookie is unavailable or invalid. Force user to login.
      res.redirect('/login');
    });
});

جافا

@POST
@Path("/profile")
public Response verifySessionCookie(@CookieParam("session") Cookie cookie) {
  String sessionCookie = cookie.getValue();
  try {
    // Verify the session cookie. In this case an additional check is added to detect
    // if the user's Firebase session was revoked, user deleted/disabled, etc.
    final boolean checkRevoked = true;
    FirebaseToken decodedToken = FirebaseAuth.getInstance().verifySessionCookie(
        sessionCookie, checkRevoked);
    return serveContentForUser(decodedToken);
  } catch (FirebaseAuthException e) {
    // Session cookie is unavailable, invalid or revoked. Force user to login.
    return Response.temporaryRedirect(URI.create("/login")).build();
  }
}

Python

@app.route('/profile', methods=['POST'])
def access_restricted_content():
    session_cookie = flask.request.cookies.get('session')
    if not session_cookie:
        # Session cookie is unavailable. Force user to login.
        return flask.redirect('/login')

    # Verify the session cookie. In this case an additional check is added to detect
    # if the user's Firebase session was revoked, user deleted/disabled, etc.
    try:
        decoded_claims = auth.verify_session_cookie(session_cookie, check_revoked=True)
        return serve_content_for_user(decoded_claims)
    except auth.InvalidSessionCookieError:
        # Session cookie is invalid, expired or revoked. Force user to login.
        return flask.redirect('/login')

انتقال

return func(w http.ResponseWriter, r *http.Request) {
	// Get the ID token sent by the client
	cookie, err := r.Cookie("session")
	if err != nil {
		// Session cookie is unavailable. Force user to login.
		http.Redirect(w, r, "/login", http.StatusFound)
		return
	}

	// Verify the session cookie. In this case an additional check is added to detect
	// if the user's Firebase session was revoked, user deleted/disabled, etc.
	decoded, err := client.VerifySessionCookieAndCheckRevoked(r.Context(), cookie.Value)
	if err != nil {
		// Session cookie is invalid. Force user to login.
		http.Redirect(w, r, "/login", http.StatusFound)
		return
	}

	serveContentForUser(w, r, decoded)
}

#C

// POST: /profile
[HttpPost]
public async Task<ActionResult> Profile()
{
    var sessionCookie = this.Request.Cookies["session"];
    if (string.IsNullOrEmpty(sessionCookie))
    {
        // Session cookie is not available. Force user to login.
        return this.Redirect("/login");
    }

    try
    {
        // Verify the session cookie. In this case an additional check is added to detect
        // if the user's Firebase session was revoked, user deleted/disabled, etc.
        var checkRevoked = true;
        var decodedToken = await FirebaseAuth.DefaultInstance.VerifySessionCookieAsync(
            sessionCookie, checkRevoked);
        return ViewContentForUser(decodedToken);
    }
    catch (FirebaseAuthException)
    {
        // Session cookie is invalid or revoked. Force user to login.
        return this.Redirect("/login");
    }
}

تحقَّق من ملفات تعريف الارتباط للجلسة باستخدام واجهة برمجة التطبيقات verifySessionCookie في "SDK للمشرف". هذا هو عملية علوية منخفضة. يتم أولاً الاستعلام عن الشهادات العامة وحفظها في ذاكرة التخزين المؤقت. إلى أن تنتهي صلاحيتها. يمكن التحقّق من ملفات تعريف الارتباط للجلسة باستخدام ذاكرة التخزين المؤقت. شهادات عامة بدون الحاجة إلى أي طلبات إضافية للشبكة.

إذا كان ملف تعريف الارتباط غير صالح، تأكَّد من محوه واطلب من المستخدم تسجيل الدخول. مرة أخرى. يتوفر خيار إضافي للتحقق من إبطال الجلسة. ملاحظة بأن هذا يضيف طلب شبكة إضافيًا في كل مرة يتم فيها تشغيل ملف تعريف ارتباط جلسة التحقق منه.

لأسباب تتعلق بالأمان، لا يمكن استخدام ملفات تعريف ارتباط جلسة Firebase مع خدمات Firebase بسبب فترة صلاحيتها المخصصة، والتي يمكن ضبطها على الحد الأقصى أسبوعَين. يُتوقع من جميع التطبيقات التي تستخدم ملفات تعريف الارتباط من جانب الخادم فرض عمليات التحقق من الأذونات بعد التحقق من جهة خادم ملفات تعريف الارتباط هذه.

Node.js

getAuth()
  .verifySessionCookie(sessionCookie, true)
  .then((decodedClaims) => {
    // Check custom claims to confirm user is an admin.
    if (decodedClaims.admin === true) {
      return serveContentForAdmin('/admin', req, res, decodedClaims);
    }
    res.status(401).send('UNAUTHORIZED REQUEST!');
  })
  .catch((error) => {
    // Session cookie is unavailable or invalid. Force user to login.
    res.redirect('/login');
  });

جافا

try {
  final boolean checkRevoked = true;
  FirebaseToken decodedToken = FirebaseAuth.getInstance().verifySessionCookie(
      sessionCookie, checkRevoked);
  if (Boolean.TRUE.equals(decodedToken.getClaims().get("admin"))) {
    return serveContentForAdmin(decodedToken);
  }
  return Response.status(Status.UNAUTHORIZED).entity("Insufficient permissions").build();
} catch (FirebaseAuthException e) {
  // Session cookie is unavailable, invalid or revoked. Force user to login.
  return Response.temporaryRedirect(URI.create("/login")).build();
}

Python

try:
    decoded_claims = auth.verify_session_cookie(session_cookie, check_revoked=True)
    # Check custom claims to confirm user is an admin.
    if decoded_claims.get('admin') is True:
        return serve_content_for_admin(decoded_claims)

    return flask.abort(401, 'Insufficient permissions')
except auth.InvalidSessionCookieError:
    # Session cookie is invalid, expired or revoked. Force user to login.
    return flask.redirect('/login')

انتقال

return func(w http.ResponseWriter, r *http.Request) {
	cookie, err := r.Cookie("session")
	if err != nil {
		// Session cookie is unavailable. Force user to login.
		http.Redirect(w, r, "/login", http.StatusFound)
		return
	}

	decoded, err := client.VerifySessionCookieAndCheckRevoked(r.Context(), cookie.Value)
	if err != nil {
		// Session cookie is invalid. Force user to login.
		http.Redirect(w, r, "/login", http.StatusFound)
		return
	}

	// Check custom claims to confirm user is an admin.
	if decoded.Claims["admin"] != true {
		http.Error(w, "Insufficient permissions", http.StatusUnauthorized)
		return
	}

	serveContentForAdmin(w, r, decoded)
}

#C

try
{
    var checkRevoked = true;
    var decodedToken = await FirebaseAuth.DefaultInstance.VerifySessionCookieAsync(
        sessionCookie, checkRevoked);
    object isAdmin;
    if (decodedToken.Claims.TryGetValue("admin", out isAdmin) && (bool)isAdmin)
    {
        return ViewContentForAdmin(decodedToken);
    }

    return this.Unauthorized("Insufficient permissions");
}
catch (FirebaseAuthException)
{
    // Session cookie is invalid or revoked. Force user to login.
    return this.Redirect("/login");
}

تسجيل الخروج

عندما يسجِّل المستخدم خروجه من جهة العميل، يجب التعامل معه في جانب الخادم من خلال النهائية. يجب أن يؤدي طلب POST/GET إلى محو ملف تعريف الارتباط للجلسة. لاحظ أنه على الرغم من مسح ملف تعريف الارتباط، فإنه يظل نشطًا حتى يصبح طبيعيًا انتهاء الصلاحية.

Node.js

app.post('/sessionLogout', (req, res) => {
  res.clearCookie('session');
  res.redirect('/login');
});

جافا

@POST
@Path("/sessionLogout")
public Response clearSessionCookie(@CookieParam("session") Cookie cookie) {
  final int maxAge = 0;
  NewCookie newCookie = new NewCookie(cookie, null, maxAge, true);
  return Response.temporaryRedirect(URI.create("/login")).cookie(newCookie).build();
}

Python

@app.route('/sessionLogout', methods=['POST'])
def session_logout():
    response = flask.make_response(flask.redirect('/login'))
    response.set_cookie('session', expires=0)
    return response

انتقال

return func(w http.ResponseWriter, r *http.Request) {
	http.SetCookie(w, &http.Cookie{
		Name:   "session",
		Value:  "",
		MaxAge: 0,
	})
	http.Redirect(w, r, "/login", http.StatusFound)
}

#C

// POST: /sessionLogout
[HttpPost]
public ActionResult ClearSessionCookie()
{
    this.Response.Cookies.Delete("session");
    return this.Redirect("/login");
}

يؤدي استدعاء واجهة برمجة التطبيقات للإبطال إلى إبطال الجلسة وأيضًا إبطال جميع طلبات جلسات أخرى، مما يؤدي إلى فرض تسجيل دخول جديد. بالنسبة للتطبيقات الحساسة، يجب أن يكون يُنصح بمدة الجلسة.

Node.js

app.post('/sessionLogout', (req, res) => {
  const sessionCookie = req.cookies.session || '';
  res.clearCookie('session');
  getAuth()
    .verifySessionCookie(sessionCookie)
    .then((decodedClaims) => {
      return getAuth().revokeRefreshTokens(decodedClaims.sub);
    })
    .then(() => {
      res.redirect('/login');
    })
    .catch((error) => {
      res.redirect('/login');
    });
});

جافا

@POST
@Path("/sessionLogout")
public Response clearSessionCookieAndRevoke(@CookieParam("session") Cookie cookie) {
  String sessionCookie = cookie.getValue();
  try {
    FirebaseToken decodedToken = FirebaseAuth.getInstance().verifySessionCookie(sessionCookie);
    FirebaseAuth.getInstance().revokeRefreshTokens(decodedToken.getUid());
    final int maxAge = 0;
    NewCookie newCookie = new NewCookie(cookie, null, maxAge, true);
    return Response.temporaryRedirect(URI.create("/login")).cookie(newCookie).build();
  } catch (FirebaseAuthException e) {
    return Response.temporaryRedirect(URI.create("/login")).build();
  }
}

Python

@app.route('/sessionLogout', methods=['POST'])
def session_logout():
    session_cookie = flask.request.cookies.get('session')
    try:
        decoded_claims = auth.verify_session_cookie(session_cookie)
        auth.revoke_refresh_tokens(decoded_claims['sub'])
        response = flask.make_response(flask.redirect('/login'))
        response.set_cookie('session', expires=0)
        return response
    except auth.InvalidSessionCookieError:
        return flask.redirect('/login')

انتقال

return func(w http.ResponseWriter, r *http.Request) {
	cookie, err := r.Cookie("session")
	if err != nil {
		// Session cookie is unavailable. Force user to login.
		http.Redirect(w, r, "/login", http.StatusFound)
		return
	}

	decoded, err := client.VerifySessionCookie(r.Context(), cookie.Value)
	if err != nil {
		// Session cookie is invalid. Force user to login.
		http.Redirect(w, r, "/login", http.StatusFound)
		return
	}
	if err := client.RevokeRefreshTokens(r.Context(), decoded.UID); err != nil {
		http.Error(w, "Failed to revoke refresh token", http.StatusInternalServerError)
		return
	}

	http.SetCookie(w, &http.Cookie{
		Name:   "session",
		Value:  "",
		MaxAge: 0,
	})
	http.Redirect(w, r, "/login", http.StatusFound)
}

#C

// POST: /sessionLogout
[HttpPost]
public async Task<ActionResult> ClearSessionCookieAndRevoke()
{
    var sessionCookie = this.Request.Cookies["session"];
    try
    {
        var decodedToken = await FirebaseAuth.DefaultInstance
            .VerifySessionCookieAsync(sessionCookie);
        await FirebaseAuth.DefaultInstance.RevokeRefreshTokensAsync(decodedToken.Uid);
        this.Response.Cookies.Delete("session");
        return this.Redirect("/login");
    }
    catch (FirebaseAuthException)
    {
        return this.Redirect("/login");
    }
}

التحقق من ملفات تعريف ارتباط الجلسة باستخدام مكتبة JWT تابعة لجهة خارجية

إذا كانت الخلفية بلغة غير متوافقة مع حزمة تطوير البرامج (SDK) لمشرف Firebase، التحقق من ملفات تعريف ارتباط الجلسة. أولاً، ابحث عن مكتبة JWT تابعة لجهة خارجية لغتك. بعد ذلك، تأكَّد من صحة العنوان والحمولة والتوقيع. من ملف تعريف ارتباط الجلسة.

تحقَّق من أن عنوان ملف تعريف الارتباط للجلسة يتوافق مع القيود التالية:

مطالبات عناوين ملفات تعريف الارتباط لجلسة Firebase
alg خوارزمية "RS256"
kid رقم تعريف المفتاح يجب أن يتوافق مع أحد المفاتيح العامة المدرجة في https://www.googleapis.com/identitytoolkit/v3/relyingparty/publicKeys

تحقَّق من توافق حمولة ملف تعريف الارتباط للجلسة مع القيود التالية:

مطالبات حمولة ملفات تعريف الارتباط الخاصة بجلسة Firebase
exp وقت انتهاء الصلاحية يجب أن يكون في المستقبل. يتم قياس الوقت بالثواني منذ نظام UNIX. حقبة يتم تحديد تاريخ انتهاء الصلاحية استنادًا إلى المدة المخصّصة المقدّمة عند يتم إنشاء ملف تعريف الارتباط.
iat وقت الإصدار يجب أن يكون في الماضي. يتم قياس الوقت بالثواني منذ نظام UNIX. حقبة
aud الجمهور يجب أن يكون معرّف مشروع Firebase هو المعرّف الفريد لحسابك في Firebase. والذي يمكن العثور عليه في عنوان URL لوحدة التحكم الخاصة بالمشروع.
iss جهة الإصدار يجب أن يكون "https://session.firebase.google.com/<projectId>""، حيث يكون <projectId> هو رقم تعريف المشروع نفسه المستخدَم في aud أعلاه.
sub الموضوع يجب أن تكون سلسلة غير فارغة وأن تكون uid للمستخدم أو الخاص بك.
auth_time وقت المصادقة يجب أن يكون في الماضي. الوقت الذي تمت مصادقته فيه من المستخدم. تطابق هذه النتيجة auth_time للرمز المميز للرقم التعريفي المستخدَم لإنشاء ملف تعريف الارتباط للجلسة.

أخيرًا، تأكَّد من أنّ المفتاح الخاص قد وقّع ملف تعريف ارتباط الجلسة. مطابق لطلب الطفل الخاص بالرمز المميز. الحصول على المفتاح العام من https://www.googleapis.com/identitytoolkit/v3/relyingparty/publicKeys والاستخدام مكتبة JWT للتحقق من التوقيع. استخدم قيمة max-age في عنوان Cache-Control للاستجابة من نقطة النهاية هذه لتحديد متى يجب إعادة تحميل المفاتيح العامة.

إذا نجحت جميع عمليات إثبات الملكية المذكورة أعلاه، يمكنك استخدام موضوع (sub) ملف تعريف ارتباط الجلسة باعتباره المعرّف الفريد للمستخدم أو الجهاز المقابل.