Firebase vous donne un contrôle total sur l'authentification en vous permettant d'authentifier les utilisateurs ou les appareils à l'aide de jetons Web JSON (JWT) sécurisés. Vous générez ces jetons sur votre serveur, vous les transmettez à un appareil client, puis vous les utilisez pour vous authentifier via la méthode signInWithCustomToken().
Pour ce faire, vous devez créer un point de terminaison de serveur qui accepte les identifiants de connexion (par exemple, un nom d'utilisateur et un mot de passe) et, si les identifiants sont valides, renvoie un jeton JWT personnalisé. Le jeton JWT personnalisé renvoyé par votre serveur peut ensuite ê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 pour accéder à 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 que Firebase ne prend pas en charge de manière native.
Avant de commencer
Les jetons personnalisés sont des jetons JWT signés dont la clé privée utilisée pour la signature appartient à un compte de service Google. Il existe plusieurs façons de spécifier le compte de service Google que le SDK Admin Firebase doit utiliser pour signer des jetons personnalisés :
- Laisser le SDK Admin découvrir un compte de service : cette méthode peut être utilisée dans les environnements gérés par Google, tels que Google Cloud Functions et App Engine. Vous devrez peut-être configurer des autorisations supplémentaires via la console Google Cloud.
- Utiliser un ID de compte de service : cette méthode signe les jetons à l'aide des clés gérées par Google du compte de service spécifié. Vous n'avez donc pas à gérer vous-même les clés privées. Il utilise un service Web distant, et vous devrez peut-être configurer des autorisations supplémentaires pour ce compte de service via la console Google Cloud.
- Utiliser un fichier de clé de compte de service : n'utilisez cette méthode que si votre code s'exécute en dehors d'un environnement géré par Google et que vous ne pouvez pas utiliser l'une des méthodes ci-dessus. Vous devez créer, stocker et protéger une clé privée de longue durée.
Nous vous recommandons les deux premières méthodes, car elles ne nécessitent pas de télécharger ni de gérer des clés de compte de service.
Permettre au SDK Admin de 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étecter 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 Functions ou Google Compute Engine), le SDK Admin Firebase peut détecter automatiquement une chaîne d'ID de compte de service à partir du serveur de métadonnées local. L'ID de compte de service découvert est ensuite utilisé conjointement avec le service IAM 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 localement sans télécharger de clé de compte de service, connectez-vous avec vos propres identifiants utilisateur à l'aide de la Google Cloud CLI :
gcloud auth application-default login
Comme il n'y a pas de serveur de métadonnées sur votre ordinateur local, le SDK Admin ne peut pas y détecter automatiquement un ID de compte de service. Spécifiez plutôt un ID de compte de service lorsque vous initialisez le SDK, et assurez-vous que votre compte utilisateur dispose du rôle Créateur de jetons du compte de service sur ce compte de service.
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 jeton 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 explicitement spécifiés, 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 les autorisations nécessaires aux comptes de service par défaut. Pour en savoir plus, consultez la section "Dépannage" ci-dessous.
Utiliser un ID de compte de service
Pour assurer la cohérence entre les différentes parties de votre application, vous pouvez spécifier un ID de compte de service dont les clés gérées par Google seront utilisées pour signer les jetons. Cela peut simplifier les stratégies IAM et les rendre plus sécurisées, et évite d'avoir à télécharger, stocker ou déployer une clé de compte de service.
L'ID du compte de service est disponible dans la console Google Cloud.
Les ID de compte de service sont des adresses e-mail au format suivant : <client-id>@<project-id>.iam.gserviceaccount.com. Ils identifient de manière unique les comptes de service dans les projets Firebase et 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. Par conséquent, leur exposition n'a aucune conséquence. Toutefois, pour signer des jetons personnalisés avec le compte de service spécifié, le SDK Admin Firebase doit appeler un service distant.
De plus, vous devez vous assurer que l'identité utilisée par le SDK Admin pour effectuer cet appel (généralement {project-name}@appspot.gserviceaccount.com en production ou votre propre compte utilisateur lors du développement local) dispose de l'autorisation iam.serviceAccounts.signBlob.
Pour en savoir plus, consultez la section "Dépannage" ci-dessous.
Utiliser un fichier de clé de compte de service
Si votre code s'exécute dans un environnement extérieur à Google Cloud où aucune des méthodes ci-dessus n'est disponible, vous pouvez initialiser le SDK Admin avec un fichier de clé de compte de service. Pour en savoir plus, suivez les instructions de configuration du SDK Admin. Avec cette méthode, le SDK Admin signe les jetons personnalisés localement à l'aide de la clé privée du fichier, sans effectuer d'appels d'API distants.
Les clés de compte de service sont des identifiants de longue durée. En cas de fuite d'une clé, toute personne qui la possède peut agir en tant que compte de service jusqu'à ce que la clé soit supprimée. Ne validez jamais de fichiers de clés dans le contrôle des versions, et envisagez d'utiliser la fédération d'identité de charge de travail au lieu de clés lorsque votre environnement le permet. Notez également que certaines organisations utilisent des contraintes liées aux règles d'administration pour empêcher la création de clés de compte de service. Dans ce cas, vous devez utiliser l'une des autres méthodes.
Créer des jetons personnalisés à l'aide du SDK Admin Firebase
Le SDK Admin Firebase dispose d'une méthode intégrée pour créer des jetons personnalisés. Vous devez au minimum fournir un uid, qui peut être n'importe quelle chaîne, mais qui doit identifier de manière unique l'utilisateur ou l'appareil que 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 des revendications supplémentaires à inclure dans le jeton personnalisé. Par exemple, ci-dessous, un champ premiumAccount a été ajouté au jeton personnalisé, qui sera disponible dans les objets auth / request.auth de 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. 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 connecté à votre application cliente avec le compte spécifié par l'élément uid inclus dans le jeton personnalisé. Si ce compte n'existait pas auparavant, un enregistrement sera créé pour cet utilisateur.
Comme pour les autres méthodes de connexion (telles que signInWithEmailAndPassword() et signInWithCredential()), l'objet auth de votre Realtime Database Security Rules et l'objet request.auth de votre Cloud Storage Security Rules seront renseignés avec l'uid de l'utilisateur. Dans ce cas, le uid sera celui 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, elles peuvent être référencées à partir de l'objet auth.token (Firebase Realtime Database) ou 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 un langage qui ne dispose pas d'un SDK Admin Firebase officiel, vous pouvez toujours créer manuellement des jetons personnalisés. Tout d'abord, recherchez une bibliothèque JWT tierce pour votre langage. Utilisez ensuite cette bibliothèque JWT pour créer un jeton JWT incluant les revendications suivantes :
| Revendications de jetons personnalisées | ||
|---|---|---|
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 |
Audience | "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émentation pour créer des jetons personnalisés dans différents langages non compatibles avec le SDK Admin Firebase. Ces exemples signent les jetons localement avec une clé privée de compte de service. Les consignes de sécurité pour les fichiers de clé de compte de service s'appliquent donc. Pour éviter d'avoir à gérer une clé privée, vous pouvez demander à Google de signer la charge utile du jeton à distance en appelant la méthode signJwt de l'API Service Account Credentials IAM. L'appelant doit disposer du rôle Créateur de jetons du compte de service sur le compte de service.
PHP
Utiliser php-jwt :
// Requires: composer require firebase/php-jwt
use Firebase\JWT\JWT;
// Placeholder values. In production, load your service account's email
// address and private key from a secure secret store. Never hard-code
// private keys in source code.
$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"
# Placeholder values. In production, load your service account's email
# address and private key from a secure secret store. Never hard-code
# private keys in source code.
$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
Après avoir créé le jeton personnalisé, envoyez-le à votre application cliente pour l'utiliser pour l'authentification auprès de Firebase. Consultez les exemples de code ci-dessus pour savoir comment procéder.
Dépannage
Cette section décrit certains problèmes courants que les développeurs peuvent rencontrer lors de la création de jetons personnalisés, ainsi que la manière de les résoudre.
API IAM non activée
Si vous spécifiez un ID de compte de service pour signer des jetons, 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 l'API IAM pour signer les jetons. Cette erreur indique que l'API IAM n'est pas activée pour votre projet Firebase. Ouvrez le lien du message d'erreur dans un navigateur Web, puis cliquez sur le bouton "Activer l'API" pour l'activer pour votre projet.
Le compte de service ne dispose pas des autorisations requises
Si le compte de service sur lequel le SDK Admin Firebase est exécuté ne dispose pas de l'autorisation iam.serviceAccounts.signBlob, un message d'erreur semblable au suivant peut s'afficher :
Permission iam.serviceAccounts.signBlob is required to perform this operation
on service account projects/-/serviceAccounts/{your-service-account-id}.
Pour résoudre ce problème, attribuez le rôle IAM Créateur de jetons du compte de service au compte de service approprié. Le compte de service par défaut utilisé dépend de l'environnement et de la version de Cloud Functions :
- Cloud Functions (1re génération) : utilise le compte de service App Engine par défaut.
- Cloud Functions (2nd gen) : utilise le compte de service Compute Engine par défaut.
Dans la console Google Cloud, accédez à IAM et administration.
Cliquez sur l'icône de modification correspondant au compte de service que vous souhaitez mettre à jour.
Cliquez sur Ajouter un autre rôle.
Saisissez
"Service Account Token Creator"dans le filtre de recherche, puis sélectionnez-le dans les résultats.Cliquez sur Enregistrer pour confirmer l'attribution du rôle.
Pour en savoir plus sur ce processus, consultez la documentation IAM ou découvrez comment mettre à jour les rôles à l'aide des 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, cela signifie que 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 utilisez le SDK pour détecter 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. Sinon, veillez à spécifier un ID de compte de service lors de l'initialisation du SDK ou, dans les environnements non Google, initialisez le SDK avec un fichier de clé de compte de service.