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 emAddClaimno servidor: o conteúdo do token é legível por
qualquer um (só a assinatura é protegida).
2.0 🚧[!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 |
2.0 🚧[!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 |
Wiki: _Sidebar
Wiki: atualizacao
Wiki: broker mqtt
Wiki: cliente
Wiki: dbware
Wiki: https
Wiki: mqtt seguranca
Wiki: pedido resposta
Wiki: plugins
Wiki: problemas comuns
Wiki: recursos
Wiki: roadmap
Wiki: rotas
Wiki: seguranca
Wiki: stored procedures