Créer des jetons personnalisés

Firebase vous permet de contrôler entièrement l'authentification en vous permettant d'authentifier des utilisateurs ou des appareils à l'aide de jetons Web JSON (JWT) sécurisés. Vous devez générer ces jetons sur votre serveur, les renvoyer à un appareil client, puis les utiliser pour vous authentifier via la méthode signInWithCustomToken().

Pour ce faire, vous devez créer un point de terminaison de serveur qui accepte les connexions des identifiants, tels qu'un nom d'utilisateur et un mot de passe, et, si les identifiants sont valide, renvoie un jeton JWT personnalisé. Le jeton JWT personnalisé renvoyé par votre serveur être utilisé par un appareil client pour s'authentifier auprès de Firebase (iOS+, Android, Web). Une fois authentifiée, cette identité sera utilisée lors de l'accès à d'autres services Firebase, tels que Firebase Realtime Database et Cloud Storage. De plus, le contenu du jeton JWT sera disponible dans l'objet auth de votre Realtime Database Security Rules et dans l'objet request.auth de votre Cloud Storage Security Rules.

Vous pouvez créer un jeton personnalisé avec le SDK Admin Firebase ou utiliser une bibliothèque JWT tierce si votre serveur est écrit dans un langage non compatible avec Firebase en mode natif.

Avant de commencer

Les jetons personnalisés sont des jetons JWT signés auxquels appartient la clé privée utilisée pour la signature un compte de service Google. Il existe plusieurs façons de spécifier le service Google compte que le SDK Firebase Admin doit utiliser pour signer les jetons:

  • À l'aide d'un fichier JSON de compte de service : cette méthode peut être utilisée dans n'importe quel environnement, mais vous devez empaqueter un fichier JSON de compte de service avec votre code. Il convient de veiller à ce que le fichier JSON du compte de service n'est pas exposé aux parties externes.
  • Autoriser le SDK Admin à découvrir un compte de service -- Cette méthode peuvent être utilisées dans des environnements gérés par Google, tels que Google Cloud Functions et App Engine. Vous devrez peut-être configurer certains des autorisations supplémentaires via la console Google Cloud.
  • À l'aide d'un ID de compte de service : lorsque cette méthode est utilisée dans un environnement géré par Google, elle signe les jetons à l'aide de la clé du compte de service spécifié. Cependant, il utilise un service Web distant, et vous devrez peut-être configurer des autorisations supplémentaires pour ce compte de service Console Google Cloud.

Utiliser un fichier JSON de compte de service

Les fichiers JSON de compte de service contiennent toutes les informations correspondant aux comptes de service (y compris la clé privée RSA). Vous pouvez les télécharger depuis la console Firebase. Pour savoir comment initialiser le SDK Admin avec un fichier JSON de compte de service, suivez les instructions de configuration du SDK Admin.

Cette méthode d'initialisation est adaptée à un large éventail de SDK Admin. déploiements. Elle permet aussi au SDK Admin de créer et de signer des jetons personnalisés en local, sans effectuer d'appels d'API distants. Le principal inconvénient de cette vous devez empaqueter un fichier JSON de compte de service ainsi que votre code. Notez également que la clé privée associée à un compte de service JSON contient des informations sensibles. Vous devez donc veiller à les conserver restent confidentielles. Plus précisément, évitez d'ajouter des fichiers JSON de compte de service au contrôle de version public.

Laisser le SDK Admin découvrir un compte de service

Si votre code est déployé dans un environnement géré par Google, le SDK Admin peut tenter de découvrir automatiquement un moyen de signer des jetons personnalisés :

  • Si votre code est déployé dans l'environnement standard App Engine pour Java, Python ou Go, le SDK Admin peut utiliser le service App Identity présent dans cet environnement pour signer des jetons personnalisés. Le service App Identity signe les données à l'aide d'un compte de service provisionné pour votre application par Google App Engine.

  • Si votre code est déployé dans un autre environnement géré (par exemple, Google Cloud ou Google Compute Engine), le SDK Firebase Admin peut détecter automatiquement d'ID de compte de service de la chaîne serveur de métadonnées. L'ID de compte de service découvert est ensuite utilisé conjointement avec pour signer des jetons à distance.

Pour utiliser ces méthodes de signature, initialisez le SDK avec les identifiants par défaut de l'application Google et ne spécifiez pas de chaîne d'ID de compte de service :

Node.js

initializeApp();

Java

FirebaseApp.initializeApp();

Python

default_app = firebase_admin.initialize_app()

Go

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

C#

FirebaseApp.Create();

Pour tester le même code en local, téléchargez un fichier JSON de compte de service et définissez le paramètre la variable d'environnement GOOGLE_APPLICATION_CREDENTIALS pour qu'elle pointe vers elle.

Si le SDK Admin Firebase doit découvrir une chaîne d'ID de compte de service, il le fait lorsque votre code crée un jeton personnalisé pour la première fois. Le résultat est mis en cache et réutilisé pour les opérations de signature de jetons ultérieures. L'ID du compte de service détecté automatiquement est généralement l'un des comptes de service par défaut fournis par Google Cloud :

Tout comme pour les ID de compte de service spécifiés explicitement, les ID de compte de service détectés automatiquement doivent disposer de l'autorisation iam.serviceAccounts.signBlob pour que la création de jetons personnalisés fonctionne. Vous devrez peut-être utiliser la section IAM et administration de la console Google Cloud pour accorder aux comptes de service par défaut les autorisations nécessaires. Pour en savoir plus, consultez la section de dépannage ci-dessous.

Utiliser un ID de compte de service

Pour maintenir la cohérence entre les différentes parties de votre application, vous pouvez Spécifiez un ID de compte de service dont les clés seront utilisées pour signer les jetons lors de l'exécution dans un environnement géré par Google. Cela peut simplifier et renforcer la sécurité des stratégies IAM, incluez le fichier JSON du compte de service dans votre code.

L'ID du compte de service se trouve dans la section Console Google Cloud, ou dans le champ client_email d'un fichier JSON de compte de service téléchargé. Les ID de compte de service sont des adresses e-mail au format suivant : <client-id>@<project-id>.iam.gserviceaccount.com. Elles identifient de manière unique comptes de service dans Firebase et les projets Google Cloud.

Pour créer des jetons personnalisés à l'aide d'un ID de compte de service distinct, initialisez le SDK comme indiqué ci-dessous:

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

Python

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

Go

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

Les ID de compte de service ne sont pas des informations sensibles. Leur exposition est sans conséquence. Toutefois, pour signer des jetons personnalisés avec le compte de service spécifié, le SDK Firebase Admin doit appeler un service distant. De plus, vous devez également vous assurer que le compte de service utilisé par le SDK Admin pour effectuer cet appel (généralement {project-name}@appspot.gserviceaccount.com) dispose de l'autorisation iam.serviceAccounts.signBlob. Pour en savoir plus, consultez la section de dépannage ci-dessous.

Créer des jetons personnalisés à l'aide du SDK Admin Firebase

Le SDK Admin Firebase intègre une méthode pour créer des jetons personnalisés. À vous devez fournir un uid, qui peut être n'importe quelle chaîne, mais ne doit identifier de manière unique l'utilisateur ou l'appareil que vous vous authentifiez. Ces jetons expirent au bout d'une heure.

Node.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

Python

uid = 'some-uid'

custom_token = auth.create_custom_token(uid)

Go

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

Vous pouvez également spécifier d'autres revendications à inclure dans l'attribut à partir d'un jeton d'accès. Par exemple, ci-dessous, un champ premiumAccount a été ajouté au un jeton personnalisé, qui sera disponible dans les objets auth / request.auth dans vos règles de sécurité:

Node.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

Python

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

custom_token = auth.create_custom_token(uid, additional_claims)

Go

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

Noms de jetons personnalisés réservés

Se connecter à l'aide de jetons personnalisés sur les clients

Après avoir créé un jeton personnalisé, vous devez l'envoyer à votre application cliente. La l'application cliente s'authentifie avec le jeton personnalisé en appelant signInWithCustomToken():

iOS+

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

Android

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

Unity

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

Web

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

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 l'authentification réussit, l'utilisateur est désormais connecté à votre application cliente avec le compte spécifié par le uid inclus dans l'attribut personnalisé à partir d'un jeton d'accès. Si ce compte n'existait pas, un enregistrement pour cet utilisateur sera enregistré créé.

Comme pour les autres méthodes de connexion (telles que signInWithEmailAndPassword() et signInWithCredential()) l'objet auth. dans votre Realtime Database Security Rules et l'objet request.auth dans votre Cloud Storage Security Rules sera renseigné avec les uid de l'utilisateur. Dans ce cas, uid sera celui qui que vous avez spécifié lors de la génération du jeton personnalisé.

Règles de base de données

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

Règles de stockage

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 le jeton personnalisé contient des revendications supplémentaires, celles-ci peuvent être référencées depuis le auth.token (Firebase Realtime Database) ou le request.auth.token (Cloud Storage) dans vos règles:

Règles de base de données

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

Règles de stockage

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

Créer des jetons personnalisés à l'aide d'une bibliothèque JWT tierce

Si votre backend est dans une langue qui ne dispose pas d'un administrateur Firebase officiel SDK, vous pouvez toujours créer manuellement des jetons personnalisés. Commencez par trouver une bibliothèque JWT tierce pour votre langage. Ensuite, utilisez cette bibliothèque JWT pour générer un jeton JWT incluant les revendications suivantes:

Revendications de jetons personnalisés
alg Algorithme "RS256"
iss Émetteur Adresse e-mail du compte de service de votre projet
sub Objet Adresse e-mail du compte de service de votre projet
aud Public visé "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit"
iat Date/Heure d'émission Heure actuelle, en secondes depuis l'epoch UNIX
exp Date/Heure d'expiration Durée, en secondes depuis l'époque UNIX, au bout de laquelle le jeton expire. Cette valeur peut correspondre à un maximum de 3 600 secondes après l'heure iat.
Remarque : Cette valeur ne contrôle que l'heure d'expiration du jeton personnalisé lui-même. Cependant, une fois que l'utilisateur est connecté à l'aide de signInWithCustomToken(), il reste connecté à l'appareil jusqu'à ce que sa session soit invalide ou qu'il se déconnecte.
uid L'identifiant unique de l'utilisateur connecté doit être une chaîne de 1 à 128 caractères inclus. Les uid plus courts offrent de meilleures performances.
claims (facultatif) Revendications personnalisées facultatives à inclure dans les variables auth/request.auth des règles de sécurité

Voici quelques exemples d'implémentations pour créer des jetons personnalisés dans différents langages non compatibles avec le SDK Admin Firebase :

PHP

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

Ruby

Utiliser 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

Une fois le jeton personnalisé créé, envoyez-le à votre application cliente pour l'utiliser pour s'authentifier avec Firebase. Pour savoir comment procéder, consultez les exemples de code ci-dessus.

Dépannage

Cette section décrit certains problèmes courants que les développeurs peuvent rencontrer la création de jetons personnalisés et la façon de les résoudre.

API IAM non activée

Si vous spécifiez un ID de compte de service pour les jetons de signature, vous pouvez obtenir une erreur semblable à celle-ci:

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.

Le SDK Admin Firebase utilise API IAM pour signer des jetons. Cette erreur indique que l'API IAM n'est actuellement pas activée pour votre projet Firebase. Ouvrez le lien figurant dans le message d'erreur dans un navigateur Web, puis cliquez sur "Activer l'API" pour l'activer pour votre projet.

Le compte de service ne dispose pas des autorisations requises

Si le compte de service, le SDK Admin Firebase s'exécute, il ne possède pas Autorisation iam.serviceAccounts.signBlob, vous pouvez recevoir un message d'erreur du type les éléments suivants:

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

Le moyen le plus simple de résoudre ce problème consiste à accorder le rôle IAM "Créateur de jetons du compte de service" au compte de service en question, généralement {project-name}@appspot.gserviceaccount.com :

  1. Ouvrez la page IAM et administration dans la console Google Cloud.
  2. Sélectionnez votre projet et cliquez sur "Continuer".
  3. Cliquez sur l'icône de modification correspondant au compte de service que vous souhaitez mettre à jour.
  4. Cliquez sur "Ajouter un autre rôle".
  5. Saisissez "Créateur de jetons du compte de service" dans le filtre de recherche, puis sélectionnez à partir des résultats.
  6. Cliquez sur "Enregistrer". pour confirmer l'attribution du rôle.

Consultez la documentation IAM. pour en savoir plus sur ce processus, ou découvrez comment mettre à jour les rôles à l'aide de la les outils de ligne de commande gcloud.

Échec de la détermination du compte de service

Si vous recevez un message d'erreur semblable à celui-ci, le SDK Admin Firebase n'a pas été correctement initialisé.

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 vous vous appuyez sur le SDK pour découvrir automatiquement un ID de compte de service, assurez-vous que le code est déployé dans un environnement Google géré avec un serveur de métadonnées. Dans le cas contraire, veillez à spécifier le fichier JSON du compte de service ou l'ID du compte de service lors de l'initialisation du SDK.