Crear fichas personalizadas

Firebase le brinda control total sobre la autenticación al permitirle autenticar usuarios o dispositivos mediante tokens web JSON (JWT) seguros. Usted genera estos tokens en su servidor, los pasa de regreso a un dispositivo cliente y luego los usa para autenticarse a través del método signInWithCustomToken() .

Para lograr esto, debe crear un punto final de servidor que acepte credenciales de inicio de sesión (como un nombre de usuario y contraseña) y, si las credenciales son válidas, devuelva un JWT personalizado. El JWT personalizado devuelto desde su servidor puede ser utilizado por un dispositivo cliente para autenticarse con Firebase ( iOS+ , Android , web ). Una vez autenticada, esta identidad se utilizará al acceder a otros servicios de Firebase, como Firebase Realtime Database y Cloud Storage. Además, el contenido del JWT estará disponible en el objeto auth en sus Reglas de seguridad de bases de datos en tiempo real y en el objeto request.auth en sus Reglas de seguridad de almacenamiento en la nube .

Puede crear un token personalizado con el SDK de administrador de Firebase o puede usar una biblioteca JWT de terceros si su servidor está escrito en un lenguaje que Firebase no admite de forma nativa.

Antes de que empieces

Los tokens personalizados son JWT firmados en los que la clave privada utilizada para firmar pertenece a una cuenta de servicio de Google. Hay varias formas de especificar la cuenta de servicio de Google que debe usar el SDK de administrador de Firebase para firmar tokens personalizados:

  • Uso de un archivo JSON de cuenta de servicio : este método se puede utilizar en cualquier entorno, pero requiere que empaquete un archivo JSON de cuenta de servicio junto con su código. Se debe tener especial cuidado para garantizar que el archivo JSON de la cuenta de servicio no esté expuesto a terceros.
  • Permitir que el SDK de administración descubra una cuenta de servicio : este método se puede utilizar en entornos administrados por Google, como Google Cloud Functions y App Engine. Es posible que tengas que configurar algunos permisos adicionales a través de la consola de Google Cloud.
  • Uso de un ID de cuenta de servicio : cuando se utiliza en un entorno administrado por Google, este método firmará tokens utilizando la clave de la cuenta de servicio especificada. Sin embargo, utiliza un servicio web remoto y es posible que tengas que configurar permisos adicionales para esta cuenta de servicio a través de la consola de Google Cloud.

Usando un archivo JSON de cuenta de servicio

Los archivos JSON de cuentas de servicio contienen toda la información correspondiente a las cuentas de servicio (incluida la clave privada RSA). Se pueden descargar desde la consola Firebase. Siga las instrucciones de configuración del Admin SDK para obtener más información sobre cómo inicializar el Admin SDK con un archivo JSON de cuenta de servicio.

Este método de inicialización es adecuado para una amplia gama de implementaciones de Admin SDK. También permite que Admin SDK cree y firme tokens personalizados localmente, sin realizar ninguna llamada API remota. El principal inconveniente de este enfoque es que requiere empaquetar un archivo JSON de cuenta de servicio junto con su código. Tenga en cuenta también que la clave privada en un archivo JSON de cuenta de servicio es información confidencial y se debe tener especial cuidado para mantenerla confidencial. Específicamente, absténgase de agregar archivos JSON de cuentas de servicio al control de versiones públicas.

Permitir que Admin SDK descubra una cuenta de servicio

Si su código se implementa en un entorno administrado por Google, el SDK de administrador puede intentar descubrir automáticamente un medio para firmar tokens personalizados:

  • Si su código se implementa en el entorno estándar de App Engine para Java, Python o Go, el SDK de administrador puede usar el servicio App Identity presente en ese entorno para firmar tokens personalizados. El servicio App Identity firma datos utilizando una cuenta de servicio proporcionada para su aplicación por Google App Engine.

  • Si su código se implementa en algún otro entorno administrado (por ejemplo, Google Cloud Functions, Google Compute Engine), el SDK de Firebase Admin puede descubrir automáticamente una cadena de ID de cuenta de servicio desde el servidor de metadatos local. Luego, el ID de la cuenta de servicio descubierta se utiliza junto con el servicio IAM para firmar tokens de forma remota.

Para utilizar estos métodos de firma, inicialice el SDK con las credenciales predeterminadas de la aplicación de Google y no especifique una cadena de ID de cuenta de servicio:

Nodo.js

initializeApp();

Java

FirebaseApp.initializeApp();

Pitón

default_app = firebase_admin.initialize_app()

Ir

app, err := firebase.NewApp(context.Background(), nil)
if err != nil {
	log.Fatalf("error initializing app: %v\n", err)
}

C#

FirebaseApp.Create();

Para probar el mismo código localmente, descargue un archivo JSON de cuenta de servicio y configure la variable de entorno GOOGLE_APPLICATION_CREDENTIALS para que apunte a él.

Si el SDK de Firebase Admin tiene que descubrir una cadena de ID de cuenta de servicio, lo hace cuando su código crea un token personalizado por primera vez. El resultado se almacena en caché y se reutiliza para operaciones posteriores de firma de tokens. El ID de la cuenta de servicio descubierta automáticamente suele ser una de las cuentas de servicio predeterminadas proporcionadas por Google Cloud:

Al igual que con los ID de cuentas de servicio especificados explícitamente, los ID de cuentas de servicio descubiertos automáticamente deben tener el permiso iam.serviceAccounts.signBlob para que funcione la creación de tokens personalizados. Es posible que deba utilizar la sección de administración e IAM de la consola de Google Cloud para otorgar los permisos necesarios a las cuentas de servicio predeterminadas. Consulte la sección de solución de problemas a continuación para obtener más detalles.

Usando una ID de cuenta de servicio

Para mantener la coherencia entre varias partes de su aplicación, puede especificar un ID de cuenta de servicio cuyas claves se usarán para firmar tokens cuando se ejecute en un entorno administrado por Google. Esto puede hacer que las políticas de IAM sean más simples y seguras, y evitar tener que incluir el archivo JSON de la cuenta de servicio en su código.

El ID de la cuenta de servicio se puede encontrar en la consola de Google Cloud o en el campo client_email de un archivo JSON de la cuenta de servicio descargado. Los ID de cuentas de servicio son direcciones de correo electrónico que tienen el siguiente formato: <client-id>@<project-id>.iam.gserviceaccount.com . Identifican de forma única cuentas de servicio en proyectos de Firebase y Google Cloud.

Para crear tokens personalizados utilizando un ID de cuenta de servicio independiente, inicialice el SDK como se muestra a continuación:

Nodo.js

initializeApp({
  serviceAccountId: 'my-client-id@my-project-id.iam.gserviceaccount.com',
});

Java

FirebaseOptions options = FirebaseOptions.builder()
    .setCredentials(GoogleCredentials.getApplicationDefault())
    .setServiceAccountId("my-client-id@my-project-id.iam.gserviceaccount.com")
    .build();
FirebaseApp.initializeApp(options);

Pitón

options = {
    'serviceAccountId': 'my-client-id@my-project-id.iam.gserviceaccount.com',
}
firebase_admin.initialize_app(options=options)

Ir

conf := &firebase.Config{
	ServiceAccountID: "my-client-id@my-project-id.iam.gserviceaccount.com",
}
app, err := firebase.NewApp(context.Background(), conf)
if err != nil {
	log.Fatalf("error initializing app: %v\n", err)
}

C#

FirebaseApp.Create(new AppOptions()
{
    Credential = GoogleCredential.GetApplicationDefault(),
    ServiceAccountId = "my-client-id@my-project-id.iam.gserviceaccount.com",
});

Los ID de cuentas de servicio no son información confidencial y, por lo tanto, su exposición no tiene consecuencias. Sin embargo, para firmar tokens personalizados con la cuenta de servicio especificada, el SDK de Firebase Admin debe invocar un servicio remoto. Además, también debe asegurarse de que la cuenta de servicio que utiliza el SDK de administrador para realizar esta llamada (generalmente {project-name}@appspot.gserviceaccount.com ) tenga el permiso iam.serviceAccounts.signBlob . Consulte la sección de solución de problemas a continuación para obtener más detalles.

Cree tokens personalizados con el SDK de administrador de Firebase

El SDK de Firebase Admin tiene un método integrado para crear tokens personalizados. Como mínimo, debe proporcionar un uid , que puede ser cualquier cadena pero debe identificar de forma única al usuario o dispositivo que está autenticando. Estos tokens caducan después de una hora.

Nodo.js

const uid = 'some-uid';

getAuth()
  .createCustomToken(uid)
  .then((customToken) => {
    // Send token back to client
  })
  .catch((error) => {
    console.log('Error creating custom token:', error);
  });

Java

String uid = "some-uid";

String customToken = FirebaseAuth.getInstance().createCustomToken(uid);
// Send token back to client

Pitón

uid = 'some-uid'

custom_token = auth.create_custom_token(uid)

Ir

client, err := app.Auth(context.Background())
if err != nil {
	log.Fatalf("error getting Auth client: %v\n", err)
}

token, err := client.CustomToken(ctx, "some-uid")
if err != nil {
	log.Fatalf("error minting custom token: %v\n", err)
}

log.Printf("Got custom token: %v\n", token)

C#

var uid = "some-uid";

string customToken = await FirebaseAuth.DefaultInstance.CreateCustomTokenAsync(uid);
// Send token back to client

Opcionalmente, también puede especificar reclamaciones adicionales que se incluirán en el token personalizado. Por ejemplo, a continuación, se agregó un campo premiumAccount al token personalizado, que estará disponible en los objetos auth / request.auth en sus Reglas de seguridad:

Nodo.js

const userId = 'some-uid';
const additionalClaims = {
  premiumAccount: true,
};

getAuth()
  .createCustomToken(userId, additionalClaims)
  .then((customToken) => {
    // Send token back to client
  })
  .catch((error) => {
    console.log('Error creating custom token:', error);
  });

Java

String uid = "some-uid";
Map<String, Object> additionalClaims = new HashMap<String, Object>();
additionalClaims.put("premiumAccount", true);

String customToken = FirebaseAuth.getInstance()
    .createCustomToken(uid, additionalClaims);
// Send token back to client

Pitón

uid = 'some-uid'
additional_claims = {
    'premiumAccount': True
}

custom_token = auth.create_custom_token(uid, additional_claims)

Ir

client, err := app.Auth(context.Background())
if err != nil {
	log.Fatalf("error getting Auth client: %v\n", err)
}

claims := map[string]interface{}{
	"premiumAccount": true,
}

token, err := client.CustomTokenWithClaims(ctx, "some-uid", claims)
if err != nil {
	log.Fatalf("error minting custom token: %v\n", err)
}

log.Printf("Got custom token: %v\n", token)

C#

var uid = "some-uid";
var additionalClaims = new Dictionary<string, object>()
{
    { "premiumAccount", true },
};

string customToken = await FirebaseAuth.DefaultInstance
    .CreateCustomTokenAsync(uid, additionalClaims);
// Send token back to client

Nombres de tokens personalizados reservados

Iniciar sesión usando tokens personalizados en los clientes

Después de crear un token personalizado, debes enviarlo a tu aplicación cliente. La aplicación cliente se autentica con el token personalizado llamando signInWithCustomToken() :

iOS+

C objetivo
[[FIRAuth auth] signInWithCustomToken:customToken
                           completion:^(FIRAuthDataResult * _Nullable authResult,
                                        NSError * _Nullable error) {
  // ...
}];
Rápido
Auth.auth().signIn(withCustomToken: customToken ?? "") { user, error in
  // ...
}

Androide

mAuth.signInWithCustomToken(mCustomToken)
        .addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
            @Override
            public void onComplete(@NonNull Task<AuthResult> task) {
                if (task.isSuccessful()) {
                    // Sign in success, update UI with the signed-in user's information
                    Log.d(TAG, "signInWithCustomToken:success");
                    FirebaseUser user = mAuth.getCurrentUser();
                    updateUI(user);
                } else {
                    // If sign in fails, display a message to the user.
                    Log.w(TAG, "signInWithCustomToken:failure", task.getException());
                    Toast.makeText(CustomAuthActivity.this, "Authentication failed.",
                            Toast.LENGTH_SHORT).show();
                    updateUI(null);
                }
            }
        });

Unidad

auth.SignInWithCustomTokenAsync(custom_token).ContinueWith(task => {
  if (task.IsCanceled) {
    Debug.LogError("SignInWithCustomTokenAsync was canceled.");
    return;
  }
  if (task.IsFaulted) {
    Debug.LogError("SignInWithCustomTokenAsync encountered an error: " + task.Exception);
    return;
  }

  Firebase.Auth.AuthResult result = task.Result;
  Debug.LogFormat("User signed in successfully: {0} ({1})",
      result.User.DisplayName, result.User.UserId);
});

C++

firebase::Future<firebase::auth::AuthResult> result =
    auth->SignInWithCustomToken(custom_token);

API con espacio de nombres web

firebase.auth().signInWithCustomToken(token)
  .then((userCredential) => {
    // Signed in
    var user = userCredential.user;
    // ...
  })
  .catch((error) => {
    var errorCode = error.code;
    var errorMessage = error.message;
    // ...
  });

API modular web

import { getAuth, signInWithCustomToken } from "firebase/auth";

const auth = getAuth();
signInWithCustomToken(auth, token)
  .then((userCredential) => {
    // Signed in
    const user = userCredential.user;
    // ...
  })
  .catch((error) => {
    const errorCode = error.code;
    const errorMessage = error.message;
    // ...
  });

Si la autenticación se realiza correctamente, su usuario iniciará sesión en su aplicación cliente con la cuenta especificada por el uid incluido en el token personalizado. Si esa cuenta no existía anteriormente, se creará un registro para ese usuario.

De la misma manera que con otros métodos de inicio de sesión (como signInWithEmailAndPassword() y signInWithCredential() ), el objeto de auth en sus Reglas de seguridad de bases de datos en tiempo real y el objeto request.auth en sus Reglas de seguridad de Cloud Storage se completarán con el uid del usuario. . En este caso, el uid será el que especificaste al generar el token personalizado.

Reglas de base de datos

{
  "rules": {
    "adminContent": {
      ".read": "auth.uid === 'some-uid'"
    }
  }
}

Reglas de almacenamiento

service firebase.storage {
  match /b/<your-firebase-storage-bucket>/o {
    match /adminContent/{filename} {
      allow read, write: if request.auth != null && request.auth.uid == "some-uid";
    }
  }
}

Si el token personalizado contiene reclamos adicionales, se puede hacer referencia a ellos desde el objeto auth.token (Firebase Realtime Database) o request.auth.token (Cloud Storage) en sus reglas:

Reglas de base de datos

{
  "rules": {
    "premiumContent": {
      ".read": "auth.token.premiumAccount === true"
    }
  }
}

Reglas de almacenamiento

service firebase.storage {
  match /b/<your-firebase-storage-bucket>/o {
    match /premiumContent/{filename} {
      allow read, write: if request.auth.token.premiumAccount == true;
    }
  }
}

Cree tokens personalizados utilizando una biblioteca JWT de terceros

Si su backend está en un idioma que no tiene un SDK de administrador de Firebase oficial, aún puede crear tokens personalizados manualmente. Primero, busque una biblioteca JWT de terceros para su idioma. Luego, use esa biblioteca JWT para crear un JWT que incluya las siguientes afirmaciones:

Reclamaciones de tokens personalizados
alg Algoritmo "RS256"
iss Editor La dirección de correo electrónico de la cuenta de servicio de su proyecto
sub Sujeto La dirección de correo electrónico de la cuenta de servicio de su proyecto
aud Audiencia "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit"
iat Emitido en el momento La hora actual, en segundos desde la época UNIX.
exp Tiempo de expiración El tiempo, en segundos desde la época UNIX, en el que caduca el token. Puede ser un máximo de 3600 segundos más tarde que el iat .
Nota: esto solo controla el momento en que caduca el token personalizado . Pero una vez que inicia sesión con signInWithCustomToken() , este permanecerá conectado en el dispositivo hasta que se invalide su sesión o el usuario cierre sesión.
uid El identificador único del usuario que ha iniciado sesión debe ser una cadena de entre 1 y 128 caracteres, inclusive. uid más cortos ofrecen un mejor rendimiento.
claims (opcional) Reclamaciones personalizadas opcionales para incluir en las variables auth / request.auth de las reglas de seguridad

A continuación, se muestran algunos ejemplos de implementaciones de cómo crear tokens personalizados en una variedad de idiomas que el SDK de Firebase Admin no admite:

PHP

Usando php-jwt :

// Requires: composer require firebase/php-jwt
use Firebase\JWT\JWT;

// Get your service account's email address and private key from the JSON key file
$service_account_email = "abc-123@a-b-c-123.iam.gserviceaccount.com";
$private_key = "-----BEGIN PRIVATE KEY-----...";

function create_custom_token($uid, $is_premium_account) {
  global $service_account_email, $private_key;

  $now_seconds = time();
  $payload = array(
    "iss" => $service_account_email,
    "sub" => $service_account_email,
    "aud" => "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit",
    "iat" => $now_seconds,
    "exp" => $now_seconds+(60*60),  // Maximum expiration time is one hour
    "uid" => $uid,
    "claims" => array(
      "premium_account" => $is_premium_account
    )
  );
  return JWT::encode($payload, $private_key, "RS256");
}

Rubí

Usando ruby-jwt :

require "jwt"

# Get your service account's email address and private key from the JSON key file
$service_account_email = "service-account@my-project-abc123.iam.gserviceaccount.com"
$private_key = OpenSSL::PKey::RSA.new "-----BEGIN PRIVATE KEY-----\n..."

def create_custom_token(uid, is_premium_account)
  now_seconds = Time.now.to_i
  payload = {:iss => $service_account_email,
             :sub => $service_account_email,
             :aud => "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit",
             :iat => now_seconds,
             :exp => now_seconds+(60*60), # Maximum expiration time is one hour
             :uid => uid,
             :claims => {:premium_account => is_premium_account}}
  JWT.encode payload, $private_key, "RS256"
end

Después de crear el token personalizado, envíelo a su aplicación cliente para usarlo para autenticarse con Firebase. Consulte los ejemplos de código anteriores para saber cómo hacer esto.

Solución de problemas

Esta sección describe algunos problemas comunes que los desarrolladores pueden encontrar al crear tokens personalizados y cómo resolverlos.

API de IAM no habilitada

Si especifica un ID de cuenta de servicio para firmar tokens, puede recibir un error similar al siguiente:

Identity and Access Management (IAM) API has not been used in project
1234567890 before or it is disabled. Enable it by visiting
https://console.developers.google.com/apis/api/iam.googleapis.com/overview?project=1234567890
then retry. If you enabled this API recently, wait a few minutes for the action
to propagate to our systems and retry.

El SDK de Firebase Admin usa la API de IAM para firmar tokens. Este error indica que la API de IAM no está habilitada actualmente para su proyecto de Firebase. Abra el enlace en el mensaje de error en un navegador web y haga clic en el botón "Habilitar API" para habilitarlo para su proyecto.

La cuenta de servicio no tiene los permisos necesarios

Si la cuenta de servicio que ejecuta el SDK de Firebase Admin no tiene el permiso iam.serviceAccounts.signBlob , es posible que reciba un mensaje de error como el siguiente:

Permission iam.serviceAccounts.signBlob is required to perform this operation
on service account projects/-/serviceAccounts/{your-service-account-id}.

La forma más sencilla de resolver esto es otorgar la función de IAM "Creador de tokens de cuenta de servicio" a la cuenta de servicio en cuestión, generalmente {project-name}@appspot.gserviceaccount.com :

  1. Abra la página de administración e IAM en la consola de Google Cloud.
  2. Seleccione su proyecto y haga clic en "Continuar".
  3. Haga clic en el ícono de edición correspondiente a la cuenta de servicio que desea actualizar.
  4. Haga clic en "Agregar otro rol".
  5. Escriba "Creador de tokens de cuenta de servicio" en el filtro de búsqueda y selecciónelo entre los resultados.
  6. Haga clic en "Guardar" para confirmar la concesión del rol.

Consulta la documentación de IAM para obtener más detalles sobre este proceso o aprende cómo actualizar roles usando las herramientas de línea de comandos de gcloud.

No se pudo determinar la cuenta de servicio

Si recibe un mensaje de error similar al siguiente, el SDK de administración de Firebase no se ha inicializado correctamente.

Failed to determine service account ID. Initialize the SDK with service account
credentials or specify a service account ID with iam.serviceAccounts.signBlob
permission.

Si confía en el SDK para descubrir automáticamente un ID de cuenta de servicio, asegúrese de que el código esté implementado en un entorno administrado de Google con un servidor de metadatos. De lo contrario, asegúrese de especificar el archivo JSON de la cuenta de servicio o el ID de la cuenta de servicio en la inicialización del SDK.