จัดการคุกกี้เซสชัน

Firebase Auth ให้การจัดการคุกกี้เซสชันฝั่งเซิร์ฟเวอร์สำหรับเว็บไซต์แบบดั้งเดิมที่ต้องอาศัยคุกกี้เซสชัน โซลูชันนี้มีข้อดีหลายประการเหนือโทเค็น ID ที่มีอายุสั้นฝั่งไคลเอ็นต์ ซึ่งอาจต้องใช้กลไกการเปลี่ยนเส้นทางในแต่ละครั้งเพื่ออัปเดตคุกกี้เซสชันเมื่อหมดอายุ:

  • ปรับปรุงความปลอดภัยผ่านโทเค็นเซสชันที่ใช้ JWT ซึ่งสามารถสร้างได้โดยใช้บัญชีบริการที่ได้รับอนุญาตเท่านั้น
  • คุกกี้เซสชันไร้สถานะที่มาพร้อมกับคุณประโยชน์ทั้งหมดของการใช้ JWT สำหรับการตรวจสอบสิทธิ์ คุกกี้เซสชันมีการอ้างสิทธิ์แบบเดียวกัน (รวมถึงการอ้างสิทธิ์แบบกำหนดเอง) เป็นโทเค็น ID ทำให้การตรวจสอบสิทธิ์แบบเดียวกันสามารถบังคับใช้กับคุกกี้เซสชันได้
  • ความสามารถในการสร้างคุกกี้เซสชันโดยมีเวลาหมดอายุที่กำหนดเองตั้งแต่ 5 นาทีถึง 2 สัปดาห์
  • ความยืดหยุ่นในการบังคับใช้นโยบายคุกกี้ตามความต้องการของแอปพลิเคชัน: โดเมน เส้นทาง ปลอดภัย httpOnly ฯลฯ
  • ความสามารถในการเพิกถอนคุกกี้เซสชันเมื่อสงสัยว่ามีการขโมยโทเค็นโดยใช้ API การเพิกถอนโทเค็นการรีเฟรชที่มีอยู่
  • ความสามารถในการตรวจจับการเพิกถอนเซสชันจากการเปลี่ยนแปลงบัญชีที่สำคัญ

เข้าสู่ระบบ

สมมติว่าแอปพลิเคชันใช้คุกกี้ฝั่งเซิร์ฟเวอร์ httpOnly ให้ลงชื่อเข้าใช้ผู้ใช้ในหน้าเข้าสู่ระบบโดยใช้ SDK ไคลเอ็นต์ โทเค็น Firebase ID จะถูกสร้างขึ้น จากนั้นโทเค็น ID จะถูกส่งผ่าน HTTP POST ไปยังจุดสิ้นสุดการเข้าสู่ระบบเซสชัน โดยที่คุกกี้เซสชันจะถูกสร้างขึ้นโดยใช้ Admin 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');
});

หากต้องการสร้างคุกกี้เซสชันเพื่อแลกกับโทเค็น ID ที่ให้มา จำเป็นต้องมีจุดสิ้นสุด HTTP ส่งโทเค็นไปยังปลายทาง โดยตั้งค่าระยะเวลาเซสชันที่กำหนดเองโดยใช้ Firebase Admin SDK ควรใช้มาตรการที่เหมาะสมเพื่อป้องกันการโจมตีด้วยการปลอมแปลงคำขอข้ามไซต์ (CSRF)

โหนด 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();
  }
}

หลาม

@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"}`))
}

ค#

// 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 ก่อนที่จะออกคุกกี้เซสชัน เพื่อลดหน้าต่างการโจมตีในกรณีที่โทเค็น ID ถูกขโมย:

โหนด 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();

หลาม

# 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"}`))
}

ค#

// 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");

หลังจากลงชื่อเข้าใช้แล้ว ส่วนที่มีการป้องกันการเข้าถึงทั้งหมดของเว็บไซต์ควรตรวจสอบคุกกี้เซสชันและยืนยันก่อนที่จะแสดงเนื้อหาที่ถูกจำกัดตามกฎความปลอดภัยบางประการ

โหนด 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();
  }
}

หลาม

@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)
}

ค#

// 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");
    }
}

ตรวจสอบคุกกี้เซสชันโดยใช้ Admin SDK VerifySessionCookie API นี่คือการดำเนินการที่มีต้นทุนการผลิตต่ำ ใบรับรองสาธารณะจะถูกสอบถามและแคชไว้ตั้งแต่แรกจนกว่าจะหมดอายุ การตรวจสอบคุกกี้เซสชันสามารถทำได้ด้วยใบรับรองสาธารณะที่แคชไว้โดยไม่ต้องร้องขอเครือข่ายเพิ่มเติม

หากคุกกี้ไม่ถูกต้อง ตรวจสอบให้แน่ใจว่าได้ล้างคุกกี้แล้ว และขอให้ผู้ใช้ลงชื่อเข้าใช้อีกครั้ง มีตัวเลือกเพิ่มเติมเพื่อตรวจสอบการเพิกถอนเซสชัน โปรดทราบว่าการดำเนินการนี้จะเพิ่มคำขอเครือข่ายเพิ่มเติมทุกครั้งที่มีการตรวจสอบคุกกี้เซสชัน

ด้วยเหตุผลด้านความปลอดภัย คุกกี้เซสชัน Firebase ไม่สามารถใช้กับบริการ Firebase อื่นๆ ได้ เนื่องจากระยะเวลาการใช้งานที่กำหนดเอง ซึ่งสามารถกำหนดระยะเวลาสูงสุดได้ 2 สัปดาห์ แอปพลิเคชันทั้งหมดที่ใช้คุกกี้ฝั่งเซิร์ฟเวอร์คาดว่าจะบังคับใช้การตรวจสอบสิทธิ์หลังจากตรวจสอบฝั่งเซิร์ฟเวอร์คุกกี้เหล่านี้

โหนด 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();
}

หลาม

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)
}

ค#

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 ควรส่งผลให้คุกกี้เซสชันถูกล้าง โปรดทราบว่าแม้ว่าคุกกี้จะถูกล้างออกไปแล้ว แต่คุกกี้จะยังคงทำงานอยู่จนกว่าจะหมดอายุตามธรรมชาติ

โหนด 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();
}

หลาม

@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)
}

ค#

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

การเรียก API การเพิกถอนจะเพิกถอนเซสชันและเพิกถอนเซสชันอื่น ๆ ของผู้ใช้ทั้งหมดด้วย โดยบังคับให้เข้าสู่ระบบใหม่ สำหรับแอปพลิเคชันที่มีความละเอียดอ่อน แนะนำให้ใช้ระยะเวลาเซสชันที่สั้นลง

โหนด 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();
  }
}

หลาม

@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)
}

ค#

// 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 บุคคลที่สาม

หากแบ็กเอนด์ของคุณเป็นภาษาที่ Firebase Admin SDK ไม่รองรับ คุณยังคงตรวจสอบคุกกี้เซสชันได้ ขั้นแรก ค้นหาไลบรารี 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 ของโทเค็น ID ที่ใช้สร้างคุกกี้เซสชัน

สุดท้าย ตรวจสอบให้แน่ใจว่าคุกกี้เซสชันได้รับการลงนามโดยคีย์ส่วนตัวที่สอดคล้องกับการอ้างสิทธิ์ลูกของโทเค็น รับรหัสสาธารณะจาก https://www.googleapis.com/identitytoolkit/v3/relyingparty/publicKeys และใช้ไลบรารี JWT เพื่อตรวจสอบลายเซ็น ใช้ค่าของอายุสูงสุดในส่วนหัว Cache-Control ของการตอบกลับจากจุดสิ้นสุดนั้นเพื่อกำหนดเวลาที่จะรีเฟรชคีย์สาธารณะ

หากการตรวจสอบข้างต้นทั้งหมดสำเร็จ คุณสามารถใช้หัวเรื่อง ( sub ) ของคุกกี้เซสชันเป็น uid ของผู้ใช้หรืออุปกรณ์ที่เกี่ยวข้องได้