Catch up on everything announced at Firebase Summit, and learn how Firebase can help you accelerate app development and run your app with confidence. Learn More

Administrar sesiones de usuario

Organiza tus páginas con colecciones Guarda y categoriza el contenido según tus preferencias.

Las sesiones de autenticación de Firebase son duraderas. Cada vez que un usuario inicia sesión, las credenciales de usuario se envían al backend de autenticación de Firebase y se intercambian por un token de ID de Firebase (un JWT) y un token de actualización. Los tokens de Firebase ID son de corta duración y duran una hora; el token de actualización se puede usar para recuperar nuevos tokens de ID. Los tokens de actualización caducan solo cuando ocurre uno de los siguientes:

  • El usuario es eliminado
  • El usuario está deshabilitado
  • Se detecta un cambio de cuenta importante para el usuario. Esto incluye eventos como actualizaciones de contraseñas o direcciones de correo electrónico.

El SDK de Firebase Admin brinda la capacidad de revocar tokens de actualización para un usuario específico. Además, también está disponible una API para verificar la revocación del token de ID. Con estas capacidades, tiene más control sobre las sesiones de los usuarios. El SDK brinda la capacidad de agregar restricciones para evitar que las sesiones se utilicen en circunstancias sospechosas, así como un mecanismo para la recuperación de posibles robos de tokens.

Revocar tokens de actualización

Puede revocar el token de actualización existente de un usuario cuando un usuario informa sobre la pérdida o el robo de un dispositivo. De manera similar, si descubre una vulnerabilidad general o sospecha una fuga a gran escala de tokens activos, puede usar la API listUsers para buscar a todos los usuarios y revocar sus tokens para el proyecto especificado.

Los restablecimientos de contraseña también revocan los tokens existentes de un usuario; sin embargo, el backend de Firebase Authentication maneja la revocación automáticamente en ese caso. En la revocación, se cierra la sesión del usuario y se le solicita que vuelva a autenticarse.

Aquí hay una implementación de ejemplo que usa Admin SDK para revocar el token de actualización de un usuario determinado. Para inicializar Admin SDK, siga las instrucciones en la página de configuración .

Nodo.js

// Revoke all refresh tokens for a specified user for whatever reason.
// Retrieve the timestamp of the revocation, in seconds since the epoch.
getAuth()
  .revokeRefreshTokens(uid)
  .then(() => {
    return getAuth().getUser(uid);
  })
  .then((userRecord) => {
    return new Date(userRecord.tokensValidAfterTime).getTime() / 1000;
  })
  .then((timestamp) => {
    console.log(`Tokens revoked at: ${timestamp}`);
  });

Java

FirebaseAuth.getInstance().revokeRefreshTokens(uid);
UserRecord user = FirebaseAuth.getInstance().getUser(uid);
// Convert to seconds as the auth_time in the token claims is in seconds too.
long revocationSecond = user.getTokensValidAfterTimestamp() / 1000;
System.out.println("Tokens revoked at: " + revocationSecond);

Pitón

# Revoke tokens on the backend.
auth.revoke_refresh_tokens(uid)
user = auth.get_user(uid)
# Convert to seconds as the auth_time in the token claims is in seconds.
revocation_second = user.tokens_valid_after_timestamp / 1000
print('Tokens revoked at: {0}'.format(revocation_second))

Vamos

client, err := app.Auth(ctx)
if err != nil {
	log.Fatalf("error getting Auth client: %v\n", err)
}
if err := client.RevokeRefreshTokens(ctx, uid); err != nil {
	log.Fatalf("error revoking tokens for user: %v, %v\n", uid, err)
}
// accessing the user's TokenValidAfter
u, err := client.GetUser(ctx, uid)
if err != nil {
	log.Fatalf("error getting user %s: %v\n", uid, err)
}
timestamp := u.TokensValidAfterMillis / 1000
log.Printf("the refresh tokens were revoked at: %d (UTC seconds) ", timestamp)

C#

await FirebaseAuth.DefaultInstance.RevokeRefreshTokensAsync(uid);
var user = await FirebaseAuth.DefaultInstance.GetUserAsync(uid);
Console.WriteLine("Tokens revoked at: " + user.TokensValidAfterTimestamp);

Detectar revocación de token de ID

Debido a que los tokens de ID de Firebase son JWT sin estado, puede determinar que un token se revocó solo solicitando el estado del token desde el backend de autenticación de Firebase. Por esta razón, realizar esta verificación en su servidor es una operación costosa que requiere un viaje de ida y vuelta adicional a la red. Puede evitar realizar esta solicitud de red configurando las reglas de seguridad de Firebase que verifican la revocación en lugar de usar el SDK de administrador para realizar la verificación.

Detectar la revocación del token de ID en las reglas de seguridad de Firebase

Para poder detectar la revocación del token de ID mediante las reglas de seguridad, primero debemos almacenar algunos metadatos específicos del usuario.

Actualice los metadatos específicos del usuario en Firebase Realtime Database.

Guarde la marca de tiempo de revocación del token de actualización. Esto es necesario para realizar un seguimiento de la revocación del token de ID a través de las reglas de seguridad de Firebase. Esto permite controles eficientes dentro de la base de datos. En los ejemplos de código a continuación, use el uid y el tiempo de revocación obtenidos en la sección anterior .

Nodo.js

const metadataRef = getDatabase().ref('metadata/' + uid);
metadataRef.set({ revokeTime: utcRevocationTimeSecs }).then(() => {
  console.log('Database updated successfully.');
});

Java

DatabaseReference ref = FirebaseDatabase.getInstance().getReference("metadata/" + uid);
Map<String, Object> userData = new HashMap<>();
userData.put("revokeTime", revocationSecond);
ref.setValueAsync(userData);

Pitón

metadata_ref = firebase_admin.db.reference("metadata/" + uid)
metadata_ref.set({'revokeTime': revocation_second})

Agregar una verificación a las reglas de seguridad de Firebase

Para hacer cumplir esta verificación, configure una regla sin acceso de escritura del cliente para almacenar el tiempo de revocación por usuario. Esto se puede actualizar con la marca de tiempo UTC de la última hora de revocación como se muestra en los ejemplos anteriores:

{
  "rules": {
    "metadata": {
      "$user_id": {
        // this could be false as it is only accessed from backend or rules.
        ".read": "$user_id === auth.uid",
        ".write": "false",
      }
    }
  }
}

Cualquier dato que requiera acceso autenticado debe tener configurada la siguiente regla. Esta lógica solo permite que los usuarios autenticados con tokens de identificación no revocados accedan a los datos protegidos:

{
  "rules": {
    "users": {
      "$user_id": {
        ".read": "auth != null && $user_id === auth.uid && (
            !root.child('metadata').child(auth.uid).child('revokeTime').exists()
          || auth.token.auth_time > root.child('metadata').child(auth.uid).child('revokeTime').val()
        )",
        ".write": "auth != null && $user_id === auth.uid && (
            !root.child('metadata').child(auth.uid).child('revokeTime').exists()
          || auth.token.auth_time > root.child('metadata').child(auth.uid).child('revokeTime').val()
        )",
      }
    }
  }
}

Detectar la revocación del token de ID en el SDK.

En su servidor, implemente la siguiente lógica para la revocación del token de actualización y la validación del token de ID:

Cuando se debe verificar el token de ID de un usuario, se debe pasar el indicador booleano checkRevoked adicional a verifyIdToken . Si se revoca el token del usuario, se debe cerrar la sesión del usuario en el cliente o se le debe pedir que vuelva a autenticarse mediante las API de reautenticación proporcionadas por los SDK del cliente de autenticación de Firebase.

Para inicializar el SDK de administrador para su plataforma, siga las instrucciones en la página de configuración . Los ejemplos de cómo recuperar el token de ID se encuentran en la sección verifyIdToken .

Nodo.js

// Verify the ID token while checking if the token is revoked by passing
// checkRevoked true.
let checkRevoked = true;
getAuth()
  .verifyIdToken(idToken, checkRevoked)
  .then((payload) => {
    // Token is valid.
  })
  .catch((error) => {
    if (error.code == 'auth/id-token-revoked') {
      // Token has been revoked. Inform the user to reauthenticate or signOut() the user.
    } else {
      // Token is invalid.
    }
  });

Java

try {
  // Verify the ID token while checking if the token is revoked by passing checkRevoked
  // as true.
  boolean checkRevoked = true;
  FirebaseToken decodedToken = FirebaseAuth.getInstance()
      .verifyIdToken(idToken, checkRevoked);
  // Token is valid and not revoked.
  String uid = decodedToken.getUid();
} catch (FirebaseAuthException e) {
  if (e.getAuthErrorCode() == AuthErrorCode.REVOKED_ID_TOKEN) {
    // Token has been revoked. Inform the user to re-authenticate or signOut() the user.
  } else {
    // Token is invalid.
  }
}

Pitón

try:
    # Verify the ID token while checking if the token is revoked by
    # passing check_revoked=True.
    decoded_token = auth.verify_id_token(id_token, check_revoked=True)
    # Token is valid and not revoked.
    uid = decoded_token['uid']
except auth.RevokedIdTokenError:
    # Token revoked, inform the user to reauthenticate or signOut().
    pass
except auth.UserDisabledError:
    # Token belongs to a disabled user record.
    pass
except auth.InvalidIdTokenError:
    # Token is invalid
    pass

Vamos

client, err := app.Auth(ctx)
if err != nil {
	log.Fatalf("error getting Auth client: %v\n", err)
}
token, err := client.VerifyIDTokenAndCheckRevoked(ctx, idToken)
if err != nil {
	if err.Error() == "ID token has been revoked" {
		// Token is revoked. Inform the user to reauthenticate or signOut() the user.
	} else {
		// Token is invalid
	}
}
log.Printf("Verified ID token: %v\n", token)

C#

try
{
    // Verify the ID token while checking if the token is revoked by passing checkRevoked
    // as true.
    bool checkRevoked = true;
    var decodedToken = await FirebaseAuth.DefaultInstance.VerifyIdTokenAsync(
        idToken, checkRevoked);
    // Token is valid and not revoked.
    string uid = decodedToken.Uid;
}
catch (FirebaseAuthException ex)
{
    if (ex.AuthErrorCode == AuthErrorCode.RevokedIdToken)
    {
        // Token has been revoked. Inform the user to re-authenticate or signOut() the user.
    }
    else
    {
        // Token is invalid.
    }
}

Responder a la revocación del token en el cliente

Si el token se revoca mediante Admin SDK, se informa al cliente de la revocación y se espera que el usuario vuelva a autenticarse o se desconecte:

function onIdTokenRevocation() {
  // For an email/password user. Prompt the user for the password again.
  let password = prompt('Please provide your password for reauthentication');
  let credential = firebase.auth.EmailAuthProvider.credential(
      firebase.auth().currentUser.email, password);
  firebase.auth().currentUser.reauthenticateWithCredential(credential)
    .then(result => {
      // User successfully reauthenticated. New ID tokens should be valid.
    })
    .catch(error => {
      // An error occurred.
    });
}

Seguridad avanzada: aplicar restricciones de dirección IP

Un mecanismo de seguridad común para detectar el robo de tokens es realizar un seguimiento de los orígenes de las direcciones IP de las solicitudes. Por ejemplo, si las solicitudes siempre provienen de la misma dirección IP (servidor que realiza la llamada), se pueden aplicar sesiones de una sola dirección IP. O bien, puede revocar el token de un usuario si detecta que la dirección IP del usuario cambió repentinamente de geolocalización o si recibe una solicitud de un origen sospechoso.

Para realizar comprobaciones de seguridad basadas en la dirección IP, para cada solicitud autenticada, inspeccione el token de ID y verifique si la dirección IP de la solicitud coincide con las direcciones IP confiables anteriores o está dentro de un rango confiable antes de permitir el acceso a datos restringidos. Por ejemplo:

app.post('/getRestrictedData', (req, res) => {
  // Get the ID token passed.
  const idToken = req.body.idToken;
  // Verify the ID token, check if revoked and decode its payload.
  admin.auth().verifyIdToken(idToken, true).then((claims) => {
    // Get the user's previous IP addresses, previously saved.
    return getPreviousUserIpAddresses(claims.sub);
  }).then(previousIpAddresses => {
    // Get the request IP address.
    const requestIpAddress = req.connection.remoteAddress;
    // Check if the request IP address origin is suspicious relative to previous
    // IP addresses. The current request timestamp and the auth_time of the ID
    // token can provide additional signals of abuse especially if the IP address
    // suddenly changed. If there was a sudden location change in a
    // short period of time, then it will give stronger signals of possible abuse.
    if (!isValidIpAddress(previousIpAddresses, requestIpAddress)) {
      // Invalid IP address, take action quickly and revoke all user's refresh tokens.
      revokeUserTokens(claims.uid).then(() => {
        res.status(401).send({error: 'Unauthorized access. Please login again!'});
      }, error => {
        res.status(401).send({error: 'Unauthorized access. Please login again!'});
      });
    } else {
      // Access is valid. Try to return data.
      getData(claims).then(data => {
        res.end(JSON.stringify(data);
      }, error => {
        res.status(500).send({ error: 'Server error!' })
      });
    }
  });
});