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, les renvoyez à un appareil client, puis les utilisez pour vous authentifier via la méthode signInWithCustomToken()
.
Pour y parvenir, vous devez créer un point de terminaison de serveur qui accepte les informations de connexion, telles qu'un nom d'utilisateur et un mot de passe, et, si les informations d'identification sont valides, renvoie un JWT personnalisé. Le 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 lors de l'accès à d'autres services Firebase, tels que Firebase Realtime Database et Cloud Storage. De plus, le contenu du JWT sera disponible dans l'objet auth
de vos règles de sécurité de base de données en temps réel et dans l'objet request.auth
de vos règles de sécurité du stockage cloud .
Vous pouvez créer un jeton personnalisé avec le SDK Firebase Admin 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 que tu commences
Les jetons personnalisés sont des JWT signés dans lesquels 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 qui doit être utilisé par le SDK d'administration Firebase pour signer des jetons personnalisés :
- Utilisation d'un fichier JSON de compte de service : cette méthode peut être utilisée dans n'importe quel environnement, mais vous nécessite de regrouper un fichier JSON de compte de service avec votre code. Des précautions particulières doivent être prises pour garantir que le fichier JSON du compte de service n'est pas exposé à des parties externes.
- Laisser le SDK Admin découvrir un compte de service : cette méthode peut être utilisée dans des environnements gérés par Google tels que Google Cloud Functions et App Engine. Vous devrez peut-être configurer certaines autorisations supplémentaires via la console Google Cloud.
- Utilisation d'un ID de compte de service : lorsqu'elle est utilisée dans un environnement géré par Google, cette méthode signera 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 via la console Google Cloud.
Utiliser un fichier JSON de compte de service
Les fichiers JSON des comptes de service contiennent toutes les informations correspondant aux comptes de service (y compris la clé privée RSA). Ils peuvent être téléchargés depuis la console Firebase. Suivez les instructions de configuration du SDK Admin pour plus d’informations sur la façon d’initialiser le SDK Admin avec un fichier JSON de compte de service.
Cette méthode d’initialisation convient à un large éventail de déploiements du SDK Admin. Il permet également au SDK Admin de créer et de signer des jetons personnalisés localement, sans effectuer d'appels d'API à distance. Le principal inconvénient de cette approche est qu'elle vous oblige à regrouper un fichier JSON de compte de service avec votre code. Notez également que la clé privée dans un fichier JSON de compte de service est une information sensible et qu'un soin particulier doit être pris pour la garder confidentielle. 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 fourni pour votre application par Google App Engine.
Si votre code est déployé dans un autre environnement géré (par exemple, Google Cloud Functions, Google Compute Engine), le SDK d'administration Firebase peut découvrir 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 les jetons à distance.
Pour utiliser ces méthodes de signature, initialisez le SDK avec les informations d'identification par défaut de l'application Google et ne spécifiez pas de chaîne d'ID de compte de service :
Noeud.js
initializeApp();
Java
FirebaseApp.initializeApp();
Python
default_app = firebase_admin.initialize_app()
Aller
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, téléchargez un fichier JSON de compte de service et définissez la variable d'environnement GOOGLE_APPLICATION_CREDENTIALS
pour qu'elle pointe vers lui.
Si le SDK d'administration 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 ultérieures de signature de jetons. L'ID du compte de service découvert 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écouverts 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 admin de la console Google Cloud pour accorder aux comptes de service par défaut les autorisations nécessaires. Consultez la section de dépannage ci-dessous pour plus de détails.
Utiliser un identifiant de compte de service
Pour maintenir 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 seront utilisées pour signer les jetons lors de l'exécution dans un environnement géré par Google. Cela peut rendre les stratégies IAM plus simples et plus sécurisées, et éviter d'avoir à inclure le fichier JSON du compte de service dans votre code.
L'ID du compte de service peut être trouvé dans la 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
. 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 :
Noeud.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)
Aller
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 identifiants de compte de service ne sont pas des informations sensibles et leur exposition est donc sans conséquence. Cependant, pour signer des jetons personnalisés avec le compte de service spécifié, le SDK d'administration Firebase doit appeler un service distant. De plus, vous devez également vous assurer que le compte de service que le SDK Admin utilise pour effectuer cet appel (généralement {project-name}@appspot.gserviceaccount.com
) dispose de l' autorisation iam.serviceAccounts.signBlob
. Consultez la section de dépannage ci-dessous pour plus de détails.
Créer des jetons personnalisés à l'aide du SDK d'administration Firebase
Le SDK Firebase Admin dispose d'une méthode intégrée pour créer des jetons personnalisés. Au minimum, vous devez 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 après une heure.
Noeud.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)
Aller
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 éventuellement spécifier des revendications supplémentaires à inclure dans le jeton personnalisé. Par exemple, ci-dessous, un champ premiumAccount
a été ajouté au token personnalisé, qui sera disponible dans les objets auth
/ request.auth
de vos règles de sécurité :
Noeud.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)
Aller
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
Connectez-vous à l'aide de jetons personnalisés sur les clients
Après avoir créé un jeton personnalisé, vous devez l'envoyer à votre application client. L'application client s'authentifie avec le jeton personnalisé en appelant signInWithCustomToken()
:
iOS+
Objectif c
[[FIRAuth auth] signInWithCustomToken:customToken
completion:^(FIRAuthDataResult * _Nullable authResult,
NSError * _Nullable error) {
// ...
}];
Rapide
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);
}
}
});
Unité
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 avec espace de noms Web
firebase.auth().signInWithCustomToken(token)
.then((userCredential) => {
// Signed in
var user = userCredential.user;
// ...
})
.catch((error) => {
var errorCode = error.code;
var errorMessage = error.message;
// ...
});
API modulaire 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, votre utilisateur sera désormais connecté à votre application client avec le compte spécifié par l' uid
inclus dans le jeton personnalisé. Si ce compte n'existait pas auparavant, un enregistrement pour cet utilisateur sera créé.
De la même manière qu'avec d'autres méthodes de connexion (telles que signInWithEmailAndPassword()
et signInWithCredential()
), l'objet auth
dans vos règles de sécurité de base de données en temps réel et l'objet request.auth
dans vos règles de sécurité Cloud Storage seront renseignés avec l' uid
de l'utilisateur. . Dans ce cas, l' 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éez des jetons personnalisés à l'aide d'une bibliothèque JWT tierce
Si votre backend est dans une langue qui ne dispose pas de SDK d'administration Firebase officiel, vous pouvez toujours créer manuellement des jetons personnalisés. Tout d’abord, recherchez une bibliothèque JWT tierce pour votre langue. Ensuite, utilisez cette bibliothèque JWT pour créer un JWT qui inclut les revendications suivantes :
Réclamations de jetons personnalisés | ||
---|---|---|
alg | Algorithme | "RS256" |
iss | Émetteur | Adresse e-mail du compte de service de votre projet |
sub | Sujet | Adresse e-mail du compte de service de votre projet |
aud | Public | "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit" |
iat | Publié à l'époque | L'heure actuelle, en secondes depuis l'époque UNIX |
exp | Date d'expiration | L'heure, en secondes depuis l'époque UNIX, à laquelle le jeton expire. Cela peut être au maximum 3 600 secondes plus tard que le iat .Remarque : cela contrôle uniquement l'heure à laquelle le jeton personnalisé lui-même expire. Mais une fois que vous avez connecté un utilisateur à l'aide signInWithCustomToken() , il restera connecté à l'appareil jusqu'à ce que sa session soit invalidée ou que l'utilisateur se déconnecte. |
uid | L'identifiant unique de l'utilisateur connecté doit être une chaîne comprenant entre 1 et 128 caractères inclus. 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 sur la manière de créer des jetons personnalisés dans diverses langues non prises en charge par le SDK d'administration Firebase :
PHP
Utilisation 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");
}
Rubis
Utilisation 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
Après avoir créé le jeton personnalisé, envoyez-le à votre application client pour qu'il puisse s'authentifier 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 et comment les résoudre.
API IAM non activée
Si vous spécifiez un ID de compte de service pour signer les jetons, vous pouvez obtenir une erreur similaire à la suivante :
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 Firebase Admin utilise l' API IAM pour signer les jetons. Cette erreur indique que l'API IAM n'est actuellement pas activée pour votre projet Firebase. Ouvrez le lien dans le message d'erreur dans un navigateur Web et 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 sous lequel le SDK d'administration Firebase est exécuté ne dispose pas de l'autorisation iam.serviceAccounts.signBlob
, vous pouvez recevoir un message d'erreur semblable au suivant :
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 jeton de compte de service » au compte de service en question, généralement {project-name}@appspot.gserviceaccount.com
:
- Ouvrez la page IAM et administrateur dans la console Google Cloud.
- Sélectionnez votre projet et cliquez sur "Continuer".
- Cliquez sur l'icône d'édition correspondant au compte de service que vous souhaitez mettre à jour.
- Cliquez sur "Ajouter un autre rôle".
- Tapez « Service Account Token Creator » dans le filtre de recherche et sélectionnez-le dans les résultats.
- Cliquez sur "Enregistrer" pour confirmer l'attribution du rôle.
Reportez-vous à la documentation IAM pour plus de détails sur ce processus, 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 similaire au suivant, le SDK d'administration 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 comptez 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. Sinon, veillez à spécifier le fichier JSON du compte de service ou l'ID du compte de service lors de l'initialisation du SDK.