Join us for Firebase Summit on November 10, 2021. Tune in to learn how Firebase can help you accelerate app development, release with confidence, and scale with ease. Register

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

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

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

เข้าสู่ระบบ

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

Java

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

ค#

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

Java

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

ค#

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

Java

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

ค#

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

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

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

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

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

Java

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

ค#

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

Java

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

ค#

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

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

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

Java

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

ค#

// 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 บัตรประจำตัวประชาชนโทเค็นใช้ในการสร้างเซสชันคุกกี้

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

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