Pular para o conteúdo principal

Adicionar autenticação ao seu aplicativo web PHP

Este guia mostrará como integrar o Logto em seu aplicativo web PHP.

dica
  • O exemplo usa Laravel, mas os conceitos são os mesmos para outros frameworks.

Pré-requisitos

  • Uma conta na Logto Cloud ou um Logto auto-hospedado (Confira o guia de Introdução para criar uma se você não tiver).
  • Um aplicativo web tradicional Logto criado.

Instalação

composer require logto/sdk

Integração

Inicializar LogtoClient

Primeiro, crie uma configuração Logto:

index.php
use Logto\Sdk\LogtoClient;
use Logto\Sdk\LogtoConfig;

$client = new LogtoClient(
new LogtoConfig(
endpoint: "https://you-logto-endpoint.app",
appId: "replace-with-your-app-id",
appSecret: "replace-with-your-app-secret",
),
);
dica

Você pode encontrar e copiar o "App Secret" na página de detalhes do aplicativo no Admin Console:

App Secret

Por padrão, o SDK usa a sessão PHP embutida para armazenar os dados do Logto. Se você quiser usar outro armazenamento, pode passar um objeto de armazenamento personalizado como segundo parâmetro:

index.php
$client = new LogtoClient(
new LogtoConfig(
// ...
),
new YourCustomStorage(),
);

Veja Storage para mais detalhes.

Configurar URIs de redirecionamento

Antes de mergulharmos nos detalhes, aqui está uma visão geral rápida da experiência do usuário final. O processo de login pode ser simplificado da seguinte forma:

  1. Seu aplicativo invoca o método de login.
  2. O usuário é redirecionado para a página de login do Logto. Para aplicativos nativos, o navegador do sistema é aberto.
  3. O usuário faz login e é redirecionado de volta para o seu aplicativo (configurado como o URI de redirecionamento).
Sobre o login baseado em redirecionamento
  1. Este processo de autenticação segue o protocolo OpenID Connect (OIDC), e o Logto aplica medidas de segurança rigorosas para proteger o login do usuário.
  2. Se você tiver vários aplicativos, pode usar o mesmo provedor de identidade (Logto). Uma vez que o usuário faz login em um aplicativo, o Logto completará automaticamente o processo de login quando o usuário acessar outro aplicativo.

Para saber mais sobre a lógica e os benefícios do login baseado em redirecionamento, veja Experiência de login do Logto explicada.


nota

Nos trechos de código a seguir, assumimos que seu aplicativo está sendo executado em http://localhost:3000/.

Configurar URIs de redirecionamento

Vá para a página de detalhes do aplicativo no Logto Console. Adicione um URI de redirecionamento http://localhost:3000/callback.

URI de redirecionamento no Logto Console

Assim como no login, os usuários devem ser redirecionados para o Logto para sair da sessão compartilhada. Uma vez concluído, seria ótimo redirecionar o usuário de volta para o seu site. Por exemplo, adicione http://localhost:3000/ como a seção de URI de redirecionamento pós logout.

Em seguida, clique em "Salvar" para salvar as alterações.

Manipular callback

Após o usuário fazer login, o Logto redirecionará o usuário para a URL de callback que você definiu no Logto Console. Neste exemplo, usamos /callback como a URL de callback:

Route::get('/callback', function () {
try {
$client->handleSignInCallback(); // Lida com várias coisas
} catch (\Throwable $exception) {
return $exception; // Altere isso para a sua lógica de tratamento de erros
}
return redirect('/'); // Redireciona o usuário para a página inicial após um login bem-sucedido
});

Implementar rota de login

No seu aplicativo web, adicione uma rota para lidar adequadamente com a solicitação de login dos usuários. Por exemplo:

Route::get('/sign-in', function () {
return redirect($client->signIn('http://localhost:3000/callback'));
});

Substitua http://localhost:3000/callback pela URL de callback que você definiu no seu Logto Console para este aplicativo.

Se você quiser mostrar a página de cadastro na primeira tela, pode definir interactionMode como signUp:

Route::get('/sign-in', function () {
return redirect($client->signIn('http://localhost:3000/callback', InteractionMode::signUp));
});

Agora, sempre que seus usuários visitarem http://localhost:3000/sign-in, isso iniciará uma nova tentativa de login e redirecionará o usuário para a página de login do Logto.

Nota Criar uma rota de login não é a única maneira de iniciar uma tentativa de login. Você sempre pode usar o método signIn para obter a URL de login e redirecionar o usuário para ela.

Implementar rota de logout

Após o usuário fazer uma solicitação de logout, o Logto limpará todas as informações de autenticação do usuário na sessão.

Para limpar a sessão PHP e a sessão Logto, uma rota de logout pode ser implementada da seguinte forma:

Route::get('/sign-out', function () {
return redirect(
// Redireciona o usuário para a página inicial após um logout bem-sucedido
$client->signOut('http://localhost:3000/')
);
});

postLogoutRedirectUri é opcional, e se não for fornecido, o usuário será redirecionado para uma página padrão do Logto após um sign-out bem-sucedido (sem redirecionar de volta para o seu aplicativo).

Nota O nome postLogoutRedirectUri é da especificação OpenID Connect RP-Initiated Logout. Embora o Logto use "sign-out" em vez de "logout", o conceito é o mesmo.

Manipular status de autenticação

No Logto SDK, podemos usar $client->isAuthenticated() para verificar o status de autenticação. Se o usuário estiver autenticado, o valor será verdadeiro; caso contrário, o valor será falso.

Também precisamos implementar uma página inicial para demonstração:

  • Se o usuário não estiver autenticado, mostrar um botão de login;
  • Se o usuário estiver autenticado, mostrar um botão de logout.
Route::get('/', function () {
if ($client->isAuthenticated() === false) {
return "Não autenticado <a href='/sign-in'>Entrar</a>";
}

return "<a href='/sign-out'>Sair</a>";
});

Ponto de verificação: Teste seu aplicativo

Agora, você pode testar seu aplicativo:

  1. Execute seu aplicativo, você verá o botão de login.
  2. Clique no botão de login, o SDK iniciará o processo de login e redirecionará você para a página de login do Logto.
  3. Após fazer login, você será redirecionado de volta para o seu aplicativo e verá o botão de logout.
  4. Clique no botão de logout para limpar o armazenamento local e sair.

Obter informações do usuário

Exibir informações do usuário

Para exibir as informações do usuário, você pode usar o método getIdTokenClaims ou o método fetchUserInfo para obter informações do usuário. Enquanto getIdTokenClaims retorna as informações do usuário contidas no Token de ID (ID token), fetchUserInfo busca as informações do usuário do endpoint userinfo.

index.php
Route::get('/userinfo', function () {
if ($client->isAuthenticated() === false) {
return "Não autenticado <a href='/sign-in'>Entrar</a>";
}

return (
// Obter reivindicações do Token de ID local
json_decode($client->getIdTokenClaims())
. "<br>"
// Buscar informações do usuário do endpoint userinfo do Logto
json_decode($client->fetchUserInfo())
);
});

Nossos modelos de dados são baseados em JsonModel, que é seguro para aceitar chaves indefinidas ao codificar ou decodificar JSON.

Note que um campo (reivindicação) com valor null não significa que o campo está definido. A razão pode ser que o escopo relacionado não foi solicitado, ou o usuário não possui o campo.

Por exemplo, se não solicitarmos o escopo email ao fazer login, o campo email será null. No entanto, se solicitarmos o escopo email, o campo email será o endereço de email do usuário, se disponível.

Solicitar reivindicações adicionais

Você pode perceber que algumas informações do usuário estão faltando no objeto retornado de getIdTokenClaims. Isso ocorre porque OAuth 2.0 e OpenID Connect (OIDC) são projetados para seguir o princípio do menor privilégio (PoLP), e o Logto é construído com base nesses padrões.

Por padrão, reivindicações limitadas são retornadas. Se você precisar de mais informações, pode solicitar escopos adicionais para acessar mais reivindicações.

info

Uma "reivindicação (Claim)" é uma afirmação feita sobre um sujeito; um "escopo (Scope)" é um grupo de reivindicações. No caso atual, uma reivindicação é uma informação sobre o usuário.

Aqui está um exemplo não normativo da relação escopo - reivindicação:

dica

A reivindicação "sub" significa "sujeito (Subject)", que é o identificador único do usuário (ou seja, ID do usuário).

O Logto SDK sempre solicitará três escopos: openid, profile e offline_access.

Para solicitar escopos adicionais, você pode configurar a opção scopes ao inicializar o cliente Logto:

index.php
$client = new LogtoClient(
new LogtoConfig(
// ...outras configurações
scopes: ["email", "phone"], // Atualize conforme suas necessidades
),
);

Alternativamente, você pode usar o enum UserScope para adicionar escopos:

index.php
use Logto\Sdk\Constants\UserScope;

$client = new LogtoClient(
new LogtoConfig(
// ...outras configurações
scopes: [UserScope::email, UserScope::phone], // Atualize conforme suas necessidades
),
);

Então, as reivindicações adicionais estarão disponíveis no valor de retorno de getIdTokenClaims ou fetchUserInfo.

Reivindicações que precisam de solicitações de rede

Para evitar o inchaço do Token de ID (ID token), algumas reivindicações requerem solicitações de rede para serem buscadas. Por exemplo, a reivindicação custom_data não está incluída no objeto do usuário, mesmo que seja solicitada nos escopos. Para acessar essas reivindicações, você pode usar o método fetchUserInfo:

index.php
$client->fetchUserInfo()->custom_data;
Este método buscará as informações do usuário solicitando ao endpoint userinfo. Para saber mais sobre os escopos e reivindicações disponíveis, veja a seção Escopos e reivindicações.

Escopos e reivindicações

Logto usa as convenções de escopos e reivindicações do OIDC para definir os escopos e reivindicações para recuperar informações do usuário do Token de ID e do endpoint userinfo do OIDC. Tanto "escopo" quanto "reivindicação" são termos das especificações OAuth 2.0 e OpenID Connect (OIDC).

Aqui está a lista de Escopos (Scopes) suportados e as Reivindicações (Claims) correspondentes:

openid

Nome da ReivindicaçãoTipoDescriçãoPrecisa de userinfo?
substringO identificador único do usuárioNão

profile

Nome da ReivindicaçãoTipoDescriçãoPrecisa de userinfo?
namestringO nome completo do usuárioNão
usernamestringO nome de usuário do usuárioNão
picturestringURL da foto de perfil do Usuário Final. Este URL DEVE referir-se a um arquivo de imagem (por exemplo, um arquivo de imagem PNG, JPEG ou GIF), em vez de uma página da Web contendo uma imagem. Note que este URL DEVE referenciar especificamente uma foto de perfil do Usuário Final adequada para exibição ao descrever o Usuário Final, em vez de uma foto arbitrária tirada pelo Usuário Final.Não
created_atnumberHora em que o Usuário Final foi criado. O tempo é representado como o número de milissegundos desde a época Unix (1970-01-01T00:00:00Z).Não
updated_atnumberHora em que as informações do Usuário Final foram atualizadas pela última vez. O tempo é representado como o número de milissegundos desde a época Unix (1970-01-01T00:00:00Z).Não

Outras reivindicações padrão incluem family_name, given_name, middle_name, nickname, preferred_username, profile, website, gender, birthdate, zoneinfo e locale também serão incluídas no escopo profile sem a necessidade de solicitar o endpoint userinfo. Uma diferença em relação às reivindicações acima é que essas reivindicações só serão retornadas quando seus valores não estiverem vazios, enquanto as reivindicações acima retornarão null se os valores estiverem vazios.

nota

Ao contrário das reivindicações padrão, as reivindicações created_at e updated_at estão usando milissegundos em vez de segundos.

email

Nome da ReivindicaçãoTipoDescriçãoPrecisa de userinfo?
emailstringO endereço de email do usuárioNão
email_verifiedbooleanSe o endereço de email foi verificadoNão

phone

Nome da ReivindicaçãoTipoDescriçãoPrecisa de userinfo?
phone_numberstringO número de telefone do usuárioNão
phone_number_verifiedbooleanSe o número de telefone foi verificadoNão

address

Por favor, consulte o OpenID Connect Core 1.0 para os detalhes da reivindicação de endereço.

custom_data

Nome da ReivindicaçãoTipoDescriçãoPrecisa de userinfo?
custom_dataobjectOs dados personalizados do usuárioSim

identities

Nome da ReivindicaçãoTipoDescriçãoPrecisa de userinfo?
identitiesobjectAs identidades vinculadas do usuárioSim
sso_identitiesarrayAs identidades SSO vinculadas do usuárioSim

urn:logto:scope:organizations

Nome da ReivindicaçãoTipoDescriçãoPrecisa de userinfo?
organizationsstring[]Os IDs das organizações às quais o usuário pertenceNão
organization_dataobject[]Os dados das organizações às quais o usuário pertenceSim

urn:logto:scope:organization_roles

Nome da ReivindicaçãoTipoDescriçãoPrecisa de userinfo?
organization_rolesstring[]Os papéis da organização aos quais o usuário pertence com o formato <organization_id>:<role_name>Não

Considerando o desempenho e o tamanho dos dados, se "Precisa de userinfo?" for "Sim", isso significa que a reivindicação não aparecerá no Token de ID, mas será retornada na resposta do endpoint userinfo.

Recursos de API e organizações

Recomendamos ler 🔐 Controle de Acesso Baseado em Papel (RBAC) primeiro para entender os conceitos básicos do RBAC do Logto e como configurar corretamente os recursos de API.

Configurar cliente Logto

Depois de configurar os recursos de API, você pode adicioná-los ao configurar o Logto em seu aplicativo:

index.php
$client = new LogtoClient(
new LogtoConfig(
// ...other configs
resources: ["https://shopping.your-app.com/api", "https://store.your-app.com/api"], // Adicionar recursos de API
),
);

Cada recurso de API tem suas próprias permissões (escopos).

Por exemplo, o recurso https://shopping.your-app.com/api tem as permissões shopping:read e shopping:write, e o recurso https://store.your-app.com/api tem as permissões store:read e store:write.

Para solicitar essas permissões, você pode adicioná-las ao configurar o Logto em seu aplicativo:

index.php
$client = new LogtoClient(
new LogtoConfig(
// ...other configs
scopes: ["shopping:read", "shopping:write", "store:read", "store:write"], // Adicionar escopos
resources: ["https://shopping.your-app.com/api", "https://store.your-app.com/api"], // Adicionar recursos de API
),
);

Você pode notar que os escopos são definidos separadamente dos recursos de API. Isso ocorre porque Resource Indicators for OAuth 2.0 especifica que os escopos finais para a solicitação serão o produto cartesiano de todos os escopos em todos os serviços de destino.

Assim, no caso acima, os escopos podem ser simplificados a partir da definição no Logto, ambos os recursos de API podem ter escopos read e write sem o prefixo. Então, na configuração do Logto:

index.php
$client = new LogtoClient(
new LogtoConfig(
// ...other configs
scopes: ["read", "write"], // Adicionar escopos
resources: ["https://shopping.your-app.com/api", "https://store.your-app.com/api"], // Adicionar recursos de API
),
);

Para cada recurso de API, ele solicitará os escopos read e write.

nota

Não há problema em solicitar escopos que não estão definidos nos recursos de API. Por exemplo, você pode solicitar o escopo email mesmo que os recursos de API não tenham o escopo email disponível. Escopos indisponíveis serão ignorados com segurança.

Após o login bem-sucedido, o Logto emitirá os escopos apropriados para os recursos de API de acordo com os papéis do usuário.

Buscar token de acesso para o recurso de API

Para buscar o token de acesso para um recurso de API específico, você pode usar o método GetAccessToken:

index.php
$accessToken = $client->getAccessToken("https://shopping.your-app.com/api");

Este método retornará um token de acesso JWT que pode ser usado para acessar o recurso de API quando o usuário tiver as permissões relacionadas. Se o token de acesso em cache atual tiver expirado, este método tentará automaticamente usar um token de atualização para obter um novo token de acesso.

Buscar tokens de organização

Se organização é um conceito novo para você, por favor, leia 🏢 Organizações (Multi-tenancy) para começar.

Você precisa adicionar o escopo core.UserScopeOrganizations ao configurar o cliente Logto:

use Logto\Sdk\Constants\UserScope;

$client = new LogtoClient(
new LogtoConfig(
// ...other configs
scopes: [UserScope::organizations], // Adicionar escopos
),
);

Uma vez que o usuário esteja autenticado, você pode buscar o token de organização para o usuário:

index.php
# Substitua o parâmetro por um ID de organização válido.
# IDs de organização válidos para o usuário podem ser encontrados na reivindicação do token de ID `organizations`.
$organizationToken = $client->getOrganizationToken(organization_id);
# ou
$claims = $client->getOrganizationTokenClaims(organization_id);

Leituras adicionais

Fluxos do usuário final: fluxos de autenticação, fluxos de conta e fluxos de organização Configurar conectores Proteger sua API