Ir para o console

Criar tokens personalizados

Com o Firebase, você tem controle total sobre as autenticações ao permitir que usuários e dispositivos sejam autenticados usando JSON Web Tokens (JWTs) seguros. Você gera esses tokens no seu servidor, devolve-os a um dispositivo cliente e os usa para autenticação pelo método signInWithCustomToken().

Para isso, você precisa criar um ponto de extremidade do servidor que aceite credenciais de login, como nome de usuário e senha. Se elas forem válidas, o servidor retornará um JWT personalizado que poderá ser usado por um dispositivo de cliente para realizar a autenticação com o Firebase (iOS, Android e Web). Após a autenticação, essa identidade será usada quando outros serviços do Firebase forem acessados, como o Firebase Realtime Database e o Cloud Storage. Além disso, os conteúdos do JWT estarão disponíveis no objeto auth nas Regras de segurança do Firebase Realtime Database, e no objeto request.auth nas Regras de segurança do Cloud Storage.

É possível criar um token personalizado com o Admin SDK do Firebase ou, então, você pode usar uma biblioteca JWT de terceiros caso seu servidor esteja escrito em uma linguagem não compatível com o Firebase.

Antes de começar

Os tokens personalizados são JWTs assinados, em que a chave particular, usada para assinatura, é de uma conta de serviço do Google. Existem várias maneiras de especificar a conta de serviço do Google que deve ser usada pelo SDK Admin do Firebase para assinar tokens personalizados:

  • Com o uso de um arquivo JSON da conta de serviço - Esse método pode ser usado em qualquer ambiente, mas exige que você empacote um arquivo JSON da conta de serviço com seu código. É preciso tomar cuidado para garantir que o arquivo JSON da conta de serviço não seja exposto a partes externas.
  • Permitindo a descoberta de uma conta de serviço pelo SDK Admin - Esse método pode ser usado em ambientes gerenciados pelo Google, como o Google Cloud Functions e o Google App Engine. Talvez você precise configurar algumas permissões extras por meio do Console do Google Cloud Platform.
  • Usando um código de conta de serviço - Quando usado em um ambiente gerenciado pelo Google, esse método assinará tokens usando a chave da conta de serviço especificada. No entanto, ele usa um serviço da Web remoto e você pode precisar configurar permissões adicionais para essa conta de serviço por meio do console do Google Cloud Platform.

Como usar um arquivo JSON da conta de serviço

Os arquivos JSON da conta de serviço têm todas as informações correspondentes às contas de serviço, incluindo a chave particular RSA. É possível fazer o respectivo download no Console do Firebase. Para mais informações sobre como inicializar o SDK Admin com um arquivo JSON da conta de serviço, siga as Instruções de configuração do SDK Admin.

Esse método de inicialização é adequado para uma grande variedade de implantações do SDK Admin. Com ele, também é possível usar o SDK Admin para criar e assinar tokens personalizados localmente, sem fazer chamadas remotas de API. A principal desvantagem dessa abordagem é que você precisa empacotar um arquivo JSON da conta de serviço com seu código. Observe também que a chave particular em um arquivo JSON da conta de serviço é uma informação confidencial. É preciso tomar cuidado especial para mantê-la confidencial. Especificamente, evite adicionar arquivos JSON da conta de serviço ao controle de versão pública.

Como permitir a descoberta de uma conta de serviço pelo SDK Admin

Se o código for implantado em um ambiente gerenciado pelo Google, é possível que o SDK Admin tente descobrir automaticamente um meio de assinar tokens personalizados:

  • Se o código for implantado no ambiente padrão do Google App Engine para Java, Python ou Go, o SDK Admin poderá usar o serviço App Identity presente nesse ambiente para assinar tokens personalizados. O serviço de identidade do aplicativo assina dados usando uma conta de serviço fornecida para seu aplicativo pelo Google App Engine.

  • Se seu código for implantado em outro ambiente gerenciado (por exemplo, Google Cloud Functions, Google Compute Engine), o SDK Admin do Firebase poderá detectar automaticamente uma string de código da conta de serviço no servidor de metadados local. O código da conta de serviço descoberto é então usado com o serviço IAM para assinar os tokens remotamente.

Para usar esses métodos de assinatura, inicialize o SDK com credenciais padrão do aplicativo do Google e não especifique uma string de código de conta de serviço:

Node.js

admin.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();

Para testar o mesmo código localmente, faça o download de um arquivo JSON da conta de serviço e defina a variável de ambiente GOOGLE_APPLICATION_CREDENTIALS e direcione para ela.

Se o SDK Admin do Firebase precisar descobrir uma string de código de conta de serviço, isso será feito quando seu código criar um token personalizado pela primeira vez. O resultado é armazenado em cache e reutilizado para operações subsequentes de assinatura de token. O código da conta de serviço de descoberta automática geralmente é uma das contas de serviço padrão fornecidas pelo Google Cloud Platform:

Como ocorre com os códigos de conta de serviço especificados explicitamente, os códigos da conta de serviço de descoberta automática precisam ter a permissão iam.serviceAccounts.signBlob para que a criação do token personalizado funcione. Talvez seja necessário usar a seção IAM e Admin do console do Google Cloud Platform para conceder as permissões necessárias às contas de serviço padrão. Para mais informações, consulte a seção de solução de problemas, abaixo.

Como usar um código de conta de serviço

Para manter a consistência entre várias partes do seu aplicativo, é possível especificar um código de conta de serviço cujas chaves serão usadas para assinar os tokens quando executados em um ambiente gerenciado pelo Google. Isso pode tornar as políticas do IAM mais simples e mais seguras e evitar a necessidade de incluir o arquivo JSON da conta de serviço no seu código.

O código da conta de serviço pode ser encontrado no Console do Google Cloud Platform ou no campo client_email de um arquivo JSON da conta de serviço transferida. Códigos de conta de serviço são endereços de e-mail com o seguinte formato: <client-id>@<project-id>.iam.gserviceaccount.com. Eles identificam de forma exclusiva as contas de serviço nos projetos do Firebase e do Google Cloud Platform.

Para criar tokens personalizados usando um código da conta de serviço separado, inicie o SDK como mostrado abaixo:

Node.js

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

Java

FirebaseOptions options = new 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",
});

Códigos da conta de serviço não são informações confidenciais e, portanto, sua exposição é irrelevante. No entanto, para assinar tokens personalizados com a conta de serviço especificada, o SDK Admin do Firebase precisa invocar um serviço remoto. Além disso, também é necessário verificar se a conta de serviço que o SDK Admin está usando para fazer essa chamada, normalmente {project-name}@appspot.gserviceaccount.com, tem a permissão iam.serviceAccounts.signBlob. Para mais informações, consulte a seção de solução de problemas, abaixo.

Criar tokens personalizados usando o SDK Admin do Firebase

O SDK Admin do Firebase tem um método incorporado para criação de tokens personalizados. É necessário fornecer no mínimo um uid, que pode ser qualquer string, mas que deve identificar exclusivamente o usuário ou o dispositivo que você está autenticando. Esses tokens expiram após uma hora.

Node.js

let uid = 'some-uid';

admin.auth().createCustomToken(uid)
  .then(function(customToken) {
    // Send token back to client
  })
  .catch(function(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

Você também pode especificar outras declarações a serem incluídas no token personalizado. No exemplo abaixo, um campo premiumAccount foi adicionado ao token personalizado, que ficará disponível nos objetos auth / request.auth nas suas Regras de segurança:

Node.js

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

admin.auth().createCustomToken(userId, additionalClaims)
  .then(function(customToken) {
    // Send token back to client
  })
  .catch(function(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

Fazer login usando tokens personalizados em clientes

Depois de criar um token personalizado, é necessário enviá-lo ao aplicativo cliente. O aplicativo cliente faz a autenticação com o token personalizado chamando o signInWithCustomToken():

No 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.FirebaseUser newUser = task.Result;
  Debug.LogFormat("User signed in successfully: {0} ({1})",
      newUser.DisplayName, newUser.UserId);
});

C++

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

Web

firebase.auth().signInWithCustomToken(token).catch(function(error) {
  // Handle Errors here.
  var errorCode = error.code;
  var errorMessage = error.message;
  // ...
});

Se a autenticação for bem-sucedida, o login do usuário será feito no app cliente com a conta especificada pelo uid incluso no token personalizado. Se essa conta ainda não tiver sido criada, um registro será adicionado para o usuário.

Como acontece com outros métodos de login, como signInWithEmailAndPassword() e signInWithCredential(), o objeto auth nas Regras de segurança do Firebase Realtime Database e o objeto request.auth nas Regras de segurança do Cloud Storage são preenchidos com o uid do usuário. Nesse caso, o uid será aquele que foi especificado ao criar o token personalizado.

Regras do Database

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

Regras do Storage

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

Se o token personalizado tiver declarações adicionais, elas poderão ser citadas fora do objeto auth.token (Firebase Realtime Database) ou request.auth.token (Cloud Storage) nas suas regras:

Regras do Database

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

Regras do Storage

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

Criar tokens personalizados usando uma biblioteca JWT de terceiros

Se seu back-end estiver em uma linguagem que não contenha o SDK Admin oficial do Firebase, ainda será possível criar tokens personalizados manualmente. Primeiro, encontre uma biblioteca JWT de terceiros para sua linguagem. Em seguida, use essa biblioteca JWT para criar um JWT que inclua as seguintes reivindicações:

Reivindicações de token personalizados
alg Algoritmo "RS256"
iss Emissor Endereço de e-mail da conta de serviço do seu projeto.
sub Assunto Endereço de e-mail da conta de serviço do seu projeto.
aud Público "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit"
iat Emitido em A hora exata, em segundos desde a época de UNIX.
exp Data de validade O tempo, em segundos desde a época de UNIX, em que o token expira. Pode ser no máximo 3.600 segundos depois do iat
Observação: ele controla apenas o horário em que o token personalizado expira. Mas quando você conecta um usuário por meio de signInWithCustomToken(), ele permanece conectado no dispositivo até que a seção seja invalidada ou que ele se desconecte.
uid É necessário que o identificador único do usuário que se conectou seja uma string entre 1 e 36 caracteres.
claims (opcional) Declarações personalizadas opcionais a serem incluídas nas variáveis auth / request.auth das regras de segurança.

Veja abaixo alguns exemplos sobre como criar tokens personalizados em várias linguagens não compatíveis com o SDK Admin do Firebase:

PHP

Ao usar 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

Ao usar 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

Depois de criar o token personalizado, envie-o para o aplicativo cliente para usar na autenticação com o Firebase. Consulte as amostras de códigos acima para saber como fazer isso.

Como resolver problemas

Nesta seção, descrevemos alguns problemas comuns que os desenvolvedores podem encontrar na criação de tokens personalizados e como resolvê-los.

API IAM não ativada

Se você estiver especificando um código de conta de serviço para assinar tokens, poderá receber um erro semelhante ao seguinte:

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.

O SDK Admin do Firebase usa a API IAM para assinar tokens. Esse erro indica que a API IAM não está ativada no momento para seu projeto do Firebase. Abra o link na mensagem de erro em um navegador da Web e clique no botão "Ativar API" para ativá-la no projeto.

A conta de serviço não tem as permissões necessárias

Se a conta de serviço em que o SDK Admin do Firebase está sendo executado não tiver a permissão iam.serviceAccounts.signBlob, você poderá receber uma mensagem de erro como a seguinte:

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

A maneira mais fácil de resolver isso é conceder o papel do IAM "Criador de token da conta de serviço" à conta de serviço em questão, geralmente {project-name}@appspot.gserviceaccount.com:

  1. Abra a página do IAM e Admin no Console do Google Cloud Platform.
  2. Selecione seu projeto e clique em "Continuar".
  3. Clique no ícone de edição correspondente à conta de serviço que você quer atualizar.
  4. Clique em "Adicionar novo papel".
  5. Digite "Criador de token de conta de serviço" no filtro de pesquisa e selecione-o nos resultados.
  6. Clique em "Salvar" para confirmar a concessão do papel.

Consulte a documentação do IAM para mais informações sobre esse processo ou para saber como atualizar papéis com as ferramentas de linha de comando gcloud.

Falha ao determinar a conta de serviço

Se você receber uma mensagem de erro semelhante à seguinte, o SDK Admin do Firebase não foi inicializado corretamente.

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

Se você estiver confiando no SDK para descobrir automaticamente um código de conta de serviço, verifique se o código foi implantado em um ambiente gerenciado do Google com um servidor de metadados. Caso contrário, não se esqueça de especificar o arquivo JSON da conta de serviço ou o código da conta de serviço na inicialização do SDK.