Ir para o console

Gerenciar sessões de usuários

As sessões do Firebase Authentication são longas. Sempre que um usuário faz login, as credenciais dele são enviadas para o back-end do Firebase Authentication e trocadas por um token de código do Firebase (um JWT) e um token de atualização. Os tokens de código do Firebase são de curta duração e duram uma hora. O token de atualização pode ser usado para recuperar novos tokens de código. Os tokens de atualização expiram somente em uma das seguintes situações:

  • O usuário é excluído.
  • O usuário é desativado.
  • Uma mudança importante na conta é detectada para o usuário. Isso inclui eventos como atualizações de senha ou de endereço de e-mail.

O Admin SDK do Firebase permite revogar os tokens de atualização de um usuário especificado. Além disso, também há uma API disponível para verificar a revogação do token de código. Com esses recursos, você tem mais controle sobre as sessões dos usuários. O SDK oferece a possibilidade de adicionar restrições para evitar que as sessões sejam usadas em circunstâncias suspeitas, bem como um mecanismo de recuperação para potenciais roubos de token.

Revogar tokens de atualização

Você pode revogar o token de atualização existente de um usuário quando ele informa que o dispositivo foi perdido ou roubado. Da mesma forma, se você descobrir uma vulnerabilidade geral ou suspeitar de um vazamento de tokens ativos, em grande escala, use a API listUsers (em inglês) para procurar todos os usuários e revogar os respectivos tokens no projeto especificado.

As redefinições de senha também revogam os tokens existentes de um usuário. No entanto, o back-end do Firebase Authentication processa a revogação automaticamente nesse caso. Após a revogação, o usuário é desconectado e solicitado a se autenticar novamente.

Veja um exemplo de implementação que usa o Admin SDK para revogar o token de atualização de um usuário. Para inicializar o Admin SDK, siga as instruções na página de configuração.

Node.js

// Revoke all refresh tokens for a specified user for whatever reason.
// Retrieve the timestamp of the revocation, in seconds since the epoch.
admin.auth().revokeRefreshTokens(uid)
  .then(() => {
    return admin.auth().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);

Python

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

Go

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)

Detectar a revogação do token de código

Como os tokens de código do Firebase são JWTs sem estado, você pode determinar se um token foi revogado por meio da solicitação do status do token no back-end do Firebase Authentication. Por isso, executar essa verificação no seu servidor tem um alto custo, porque exige uma operação de ida e volta adicional. Você pode evitar essa solicitação de rede configurando as regras do Firebase que verificam a revogação em vez de usar o Admin SDK para fazer a verificação.

Detectar uma revogação do token de código nas regras do Database

Para detectar a revogação do token de código usando as regras do banco de dados, primeiro precisamos armazenar alguns metadados específicos do usuário.

Atualizar metadados específicos do usuário no Firebase Realtime Database

Salve o carimbo de data/hora da revogação do token de atualização. Isso é necessário para rastrear a revogação do token de código por meio das regras do Firebase. Assim, é possível realizar verificações eficientes no banco de dados. Nas amostras de código abaixo, use o uid e o horário da revogação encontrados na seção anterior.

Node.js

const metadataRef = admin.database().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);

Python

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

Adicionar uma verificação nas regras do Database

Para realizar essa verificação, configure uma regra sem acesso de gravação ao cliente a fim de armazenar o horário da revogação por usuário. Ele pode ser atualizado com o carimbo de data/hora em UTC do último horário de revogação, como mostrado nos exemplos 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",
      }
    }
  }
}

Qualquer dado que exija acesso autenticado precisa ter a seguinte regra configurada. Esta lógica só permite que os usuários autenticados com tokens de código não revogados acessem os dados protegidos:

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

Detectar revogação do token de código no SDK

No servidor, implemente a seguinte lógica para revogar o token de atualização e validar o token de código:

Quando o token de código de um usuário precisar ser verificado, será necessário transmitir o sinalizador booleano checkRevoked adicional para verifyIdToken. Se o token do usuário for revogado, o usuário precisará ser desconectado do cliente. Ou então, ele será solicitado a se autenticar novamente com as APIs de reautenticação fornecidas pelos SDKs de cliente do Firebase Authentication.

Para inicializar o Admin SDK na sua plataforma, siga as instruções na página de configuração. Veja exemplos de recuperação do token de código na seção verifyIdToken.

Node.js

// Verify the ID token while checking if the token is revoked by passing
// checkRevoked true.
let checkRevoked = true;
admin.auth().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.getErrorCode().equals("id-token-revoked")) {
    // Token has been revoked. Inform the user to re-authenticate or signOut() the user.
  } else {
    // Token is invalid.
  }
}

Python

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.AuthError as exc:
    if exc.code == 'ID_TOKEN_REVOKED':
        # Token revoked, inform the user to reauthenticate or signOut().
        pass
    else:
        # Token is invalid
        pass

Go

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)

Responder à revogação do token no cliente

Caso o token seja revogado por meio do Admin SDK, o cliente será informado sobre a revogação e o usuário precisará se autenticar novamente ou ficará desconectado:

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

Segurança avançada: aplicar restrições de endereço IP

Um mecanismo de segurança comum para detectar o roubo do token é acompanhar as origens de endereço IP das solicitações. Por exemplo, se as solicitações forem sempre provenientes do mesmo endereço IP (servidor que faz a chamada), poderão ser aplicadas sessões de endereço IP único. Ou você poderá revogar o token de um usuário se detectar que a localização geográfica do endereço IP dele foi alterada de repente ou se receber uma solicitação de origem suspeita.

Para realizar verificações de segurança com base no endereço IP, inspecione o token de código de cada solicitação autenticada. Verifique se o endereço IP da solicitação corresponde aos endereços IP confiáveis anteriores ou está dentro de um intervalo confiável antes de permitir o acesso a dados restritos. Por exemplo:

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