Repository navigation
autenticacao
Home > Autenticação
Basic e JWT desde a 1.0 · Digest e OAuth2:
2.0 🚧· Delphi e Lazarus · Nível: intermediário
Em uma frase: fazer o servidor exigir login e o cliente enviá-lo sozinho.
Você vai precisar: um servidor com rotas (Rotas). Login sem HTTPS manda a senha legível pela rede: em produção, use HTTPS.
| Tipo | Como funciona | Use quando |
|---|---|---|
| Basic | usuário e senha em todo pedido | sistema interno, sob HTTPS, o mais simples |
| JWT | o cliente troca usuário e senha por um token e envia o token em cada pedido | o mais comum em APIs e apps |
Digest 2.0 🚧
|
a senha nunca viaja, só um resumo dela | equipamentos e clientes que já falam Digest |
OAuth2 2.0 🚧
|
um servidor de autorização emite tokens para vários sistemas | integração entre empresas, login de terceiros |
Em todos, o servidor tem um componente TRALServer... (paleta RAL - Server) e
o cliente o par TRALClient... (paleta RAL - Client). Ligue o do servidor em
Servidor.Authentication e o do cliente em Cliente.Authentication.
Uma rota que deve ficar pública mesmo assim: SkipAuth([amGET]) (ou
SkipAuthMethods). Ver Rotas.
Servidor, um usuário fixo:
RALServerBasicAuth1.UserName := 'admin';
RALServerBasicAuth1.Password := 'segredo';
Servidor.Authentication := RALServerBasicAuth1;Servidor, conferindo no banco (OnValidate):
procedure TForm1.BasicValidate(ARequest: TRALRequest; AResponse: TRALResponse;
var AResult: boolean);
var
vUser: TRALAuthBasic;
begin
vUser := ARequest.Authorization.AsAuthBasic;
AResult := ConfereSenha(vUser.UserName, vUser.Password); // a sua função
end;Cliente:
RALClientBasicAuth1.UserName := 'admin';
RALClientBasicAuth1.Password := 'segredo';
Cliente.Authentication := RALClientBasicAuth1;AuthDialog := True no servidor faz o navegador abrir a janelinha de login.
O cliente envia um JSON para a rota /gettoken (POST). O servidor confere e
devolve {"token":"..."}. Daí em diante o cliente manda o token em cada pedido e
pede outro sozinho quando ele vence.
Servidor:
RALServerJWTAuth1.SignSecretKey := 'uma-chave-longa-e-secreta'; // obrigatória
RALServerJWTAuth1.ExpirationSecs := 1800; // 30 min (padrão)
Servidor.Authentication := RALServerJWTAuth1;
// OnGetToken: quem pode receber um token
procedure TForm1.JWTGetToken(ARequest: TRALRequest; AResponse: TRALResponse;
AParams: TRALJWTParams; var AResult: boolean);
var
vJson: TRALJSONObject;
begin
vJson := TRALJSONObject(TRALJSON.ParseJSON(ARequest.RequestText)); // unit RALJson
try
AResult := (vJson <> nil) and
ConfereSenha(vJson.Get('usuario').AsString, vJson.Get('senha').AsString);
if AResult then
AParams.AddClaim('usuario', vJson.Get('usuario').AsString); // vai dentro do token
finally
vJson.Free;
end;
end;Cliente:
RALClientJWTAuth1.Payload.AddClaim('usuario', 'ana'); // o JSON enviado a /gettoken
RALClientJWTAuth1.AutoGetToken := True; // já é o padrão: pede e renova sozinho
Cliente.Authentication := RALClientJWTAuth1;Na rota, quem está logado: ARequest.Authorization.AsAuthBearer.Payload.GetClaim('usuario').
| Propriedade (servidor) | O que faz |
|---|---|
SignSecretKey |
a chave que assina o token. Vazia = erro 500. Longa e fora do código-fonte |
ExpirationSecs |
validade do token em segundos |
Algorithm |
tjaHSHA256 (padrão), 384, 512; RS256/ES256 com chave própria: 2.0 🚧
|
AuthRoute |
a rota do token (padrão /gettoken) |
UseCookie |
1.1: guarda o token num cookie raltoken (para páginas web) |
CookieSameSite |
1.2: o SameSite desse cookie |
OnValidate |
regra extra a cada pedido (ex.: usuário bloqueado depois de logado) |
OnRenewToken |
1.2: decidir a renovação e mudar as claims
|
Warning
Não ponha a senha em AddClaim no servidor: o conteúdo do token é legível por
qualquer um (só a assinatura é protegida).
Important
Em desenvolvimento (2.0). Ainda não está numa versão lançada e pode mudar.
// servidor: um usuário fixo, ou OnGetCredential para buscar no banco
RALServerDigest1.UserName := 'admin';
RALServerDigest1.Password := 'segredo';
Servidor.Authentication := RALServerDigest1;
// cliente
RALClientDigest1.UserName := 'admin';
RALClientDigest1.Password := 'segredo';
Cliente.Authentication := RALClientDigest1;| Propriedade (servidor) | Padrão | O que faz |
|---|---|---|
Algorithms |
SHA-256 e MD5 | algoritmos oferecidos (também SHA-512-256) |
Realm |
RAL |
o nome que o cliente vê |
NonceLifetime |
300 s | validade de cada desafio |
ReplayProtection |
ligado | recusa o mesmo pedido repetido |
OnGetCredential |
— | devolve a senha (ou o hash) de um usuário |
Important
Em desenvolvimento (2.0). Ainda não está numa versão lançada e pode mudar.
O TRALServerOAuth2 faz dois papéis, separáveis:
| Papel | Liga com | O que faz |
|---|---|---|
| emitir tokens (servidor de autorização) |
Issuing := True (padrão) |
cria /oauth/token, /oauth/authorize, /oauth/introspect, /oauth/revoke, /oauth/jwks e /.well-known/oauth-authorization-server
|
| conferir tokens (servidor de recursos) | Validation |
ovLocal (os próprios), ovJWKS (de outro emissor, por JWKSURL), ovIntrospection (pergunta ao emissor, IntrospectionURL) |
Fluxos (Grants): client_credentials (sistema com sistema), authorization_code
com PKCE obrigatório (usuário que faz login numa página), refresh_token. Senha
direta e fluxo implícito não existem, de propósito.
Servidor emitindo tokens para sistemas:
RALServerOAuth2_1.SignSecretKey := 'uma-chave-longa-e-secreta';
RALServerOAuth2_1.OnValidateClient := ValidaCliente; // confere ClientID e ClientSecret
Servidor.Authentication := RALServerOAuth2_1;Cliente:
RALClientOAuth2_1.TokenURL := '/oauth/token';
RALClientOAuth2_1.ClientID := 'erp';
RALClientOAuth2_1.ClientSecret := 'segredo-do-erp';
RALClientOAuth2_1.Scope := 'pedidos';
Cliente.Authentication := RALClientOAuth2_1;Escopo por rota (RouteScopes, uma linha rota=escopos) responde 403 a quem não
tem o escopo. Para login de usuário num app de desktop, TRALOAuth2Loopback recebe
o retorno do navegador em 127.0.0.1.
Removido na 2.0 🚧: nunca funcionou e não é o antecessor do OAuth2.
| O que acontece | Causa | O que fazer |
|---|---|---|
| 401 em tudo | o cliente não tem Authentication, ou a senha não bate |
confira o par cliente/servidor |
| 401 numa rota pública | a autenticação vale para todas as rotas |
SkipAuth([...]) na rota |
JWT: 500 SignSecretKey
|
chave vazia | preencha SignSecretKey
|
| JWT: o servidor não emite token |
OnGetToken não foi atribuído, ou deixou AResult = False
|
atribua e confira as credenciais ali |
| 403 depois de várias tentativas | proteção contra força bruta bloqueou o IP | espere ExpirationTime, ou libere o IP; ver Segurança
|
WWW-Authenticate: Bearer error="invalid_token" |
token vencido, assinatura errada ou recusado em OnValidate
|
a descrição do erro diz qual |
Começar
Servidor
- Rotas e parâmetros
- Rotas avançadas: tipos, grupos, versões 🚧
- Ler o pedido e responder
- Resposta em fluxo 🚧
- Plugins 🚧
- Autenticação
- Banco de dados (DBWare)
- Servir site e arquivos
- Swagger e Postman
- Servidor SOAP 🚧
- HTTPS
- Criptografia ponto a ponto
- Segurança para produção
- Colocar no ar
- Desempenho
MQTT 🚧
- O que é e quando usar 🚧
- Cliente MQTT 🚧
- Broker MQTT 🚧
- MQTT-SN 🚧
- Ponte REST 🚧
- Segurança 🚧
- Rede ruim 🚧
Cliente
Motores
Avançado
Ajuda
- Problemas comuns
- Atualizar o Indy
- Várias versões do Delphi
- Vídeos e guias
- Módulos de terceiros
- Licenças
- Contribuir
- Roadmap e changelog
🚧 = em desenvolvimento, pode mudar