Gerador de Link para Login Externo (Magic Link)
Login
> Externo v2 — Guia de Integração (Cliente)
Documento para o cliente implementar a geração do magic link em seu próprio sistema. > Versão do esquema: v2. Validade do link: 2 minutos.
Visão geral
O Login Externo permite que o seu sistema (intranet, portal etc.) gere um link de acesso que autentica automaticamente uma pessoa na plataforma, sem digitar senha.
Na v2, esse link é assinado criptograficamente (HMAC-SHA256) e tem validade curta (2 minutos). Você recebe da nossa equipe um par de chaves e monta a URL localmente — nenhuma chamada de rede é necessária para gerar o link.
O que você recebe da nossa equipe
Entregues uma única vez, por canal seguro:
| Valor | O que é | Onde usar |
|---|---|---|
external_login_id |
Chave pública (identificador da empresa) | Vai dentro do link. Não é segredo. |
external_login_secret |
Chave privada (segredo de assinatura) | Fica só no seu servidor. Nunca vai no link. |
url_empresa |
URL base da sua plataforma (ex.: https://sua-empresa.exemplo.com) |
Prefixo do link. Sem barra final. |
>
⚠️ O
external_login_secreté secreto. Guarde-o apenas no servidor (variável de ambiente ou configuração protegida). Nunca o coloque em código versionado público, no navegador, em logs ou na URL. Se suspeitar de vazamento, solicite a rotação do segredo à nossa equipe.
Formato do link
GET {url_empresa}/login_externo/acessar?v=2&d={payload}&s={assinatura}
v=2— versão do esquema (fixo).d— o payload em Base64 url-safe sem padding (JSON abaixo).s— a assinatura HMAC-SHA256 do valor ded, em hexadecimal.
Payload (JSON antes de codificar em d)
{
"emp": "<<external_login_id>>",
"login": "<<login_da_pessoa>>",
"ts": 1753800000
}
emp— a sua chave pública (external_login_id).login— o login da pessoa na plataforma. Se for CPF, envie apenas os números (sem pontos ou traço).ts— o timestamp Unix (segundos) do momento da geração. O servidor aceita uma janela de ±120 segundos.
Algoritmo (agnóstico de linguagem)
- Monte o objeto JSON com
emp,loginets(timestamp Unix em segundos). - Serialize esse JSON em texto (UTF-8).
- Codifique em Base64 url-safe: Base64 padrão, trocando
+por-e/por_, e removendo o=de padding do final. O resultado é o valord. - Calcule
s = HMAC-SHA256(mensagem = d, chave = external_login_secret)e represente em hexadecimal minúsculo. - Monte a URL:
{url_empresa}/login_externo/acessar?v=2&d={d}&s={s}.
>
🔑 Ponto crítico: a assinatura é calculada sobre a string
d(exatamente como ela vai na URL) — não sobre o JSON original. Isso significa que pequenas diferenças na serialização do JSON entre linguagens não quebram a validação, desde que você assine exatamente o mesmodque envia na URL.> >
desusam apenas caracteres seguros para URL (A–Z a–z 0–9 - _e hexadecimal), então não precisam de URL-encoding adicional.
Exemplos de código
Em todos: gere o link no momento do redirecionamento (ele expira em 2 minutos) e substitua os placeholders pelos valores recebidos.
PHP
<<?php
$externalLoginId = '<<external_login_id>>'; // chave PÚBLICA (vai na URL)
$externalLoginSecret = '<<external_login_secret>>'; // chave PRIVADA — nunca expor
$urlEmpresa = 'https://<<url-empresa>>'; // sem barra final
$loginPessoa = '<<login_da_pessoa>>'; // CPF: só números
$payload = array(
'emp' =>> $externalLoginId,
'login' =>> $loginPessoa,
'ts' =>> time(),
);
// Base64 url-safe sem padding
$d = rtrim(strtr(base64_encode(json_encode($payload)), '+/', '-_'), '=');
// HMAC-SHA256 (hex) sobre o valor de $d
$s = hash_hmac('sha256', $d, $externalLoginSecret);
echo $urlEmpresa . '/login_externo/acessar?v=2&d=' . $d . '&s=' . $s;
?>>
Java
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
public class MagicLinkV2 {
public static String gerar(String externalLoginId, String externalLoginSecret,
String urlEmpresa, String loginPessoa) throws Exception {
long ts = System.currentTimeMillis() / 1000L;
// Monte o JSON (use uma lib JSON se o login puder conter caracteres especiais)
String json = "{\"emp\":\"" + externalLoginId + "\",\"login\":\"" + loginPessoa + "\",\"ts\":" + ts + "}";
// Base64 url-safe sem padding
String d = Base64.getUrlEncoder().withoutPadding()
.encodeToString(json.getBytes(StandardCharsets.UTF_8));
// HMAC-SHA256 (hex) sobre o valor de d
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(externalLoginSecret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
byte[] raw = mac.doFinal(d.getBytes(StandardCharsets.UTF_8));
StringBuilder hex = new StringBuilder(raw.length * 2);
for (byte b : raw) hex.append(String.format("%02x", b));
String s = hex.toString();
return urlEmpresa + "/login_externo/acessar?v=2&d=" + d + "&s=" + s;
}
}
Node.js
const crypto = require('crypto');
function gerarLink(externalLoginId, externalLoginSecret, urlEmpresa, loginPessoa) {
const payload = {
emp: externalLoginId,
login: loginPessoa,
ts: Math.floor(Date.now() / 1000),
};
// Base64 url-safe sem padding (Node 16+ suporta 'base64url')
const d = Buffer.from(JSON.stringify(payload)).toString('base64url');
// HMAC-SHA256 (hex) sobre o valor de d
const s = crypto.createHmac('sha256', externalLoginSecret).update(d).digest('hex');
return `${urlEmpresa}/login_externo/acessar?v=2&d=${d}&s=${s}`;
}
>
Em versões antigas do Node (sem
'base64url'), gere assim: >Buffer.from(JSON.stringify(payload)).toString('base64').replace(/\+/g,'-').replace(/\//g,'_').replace(/=+$/,'')
Python
import base64, hmac, hashlib, json, time
def gerar_link(external_login_id, external_login_secret, url_empresa, login_pessoa):
payload = {"emp": external_login_id, "login": login_pessoa, "ts": int(time.time())}
# JSON compacto (sem espaços) em UTF-8
raw = json.dumps(payload, separators=(",", ":")).encode("utf-8")
# Base64 url-safe sem padding
d = base64.urlsafe_b64encode(raw).rstrip(b"=").decode("ascii")
# HMAC-SHA256 (hex) sobre o valor de d
s = hmac.new(external_login_secret.encode("utf-8"),
d.encode("utf-8"), hashlib.sha256).hexdigest()
return f"{url_empresa}/login_externo/acessar?v=2&d={d}&s={s}"
Regras operacionais
- Gere o link só na hora do redirecionamento. Ele expira em 2 minutos — não pré-gere nem faça cache de links.
- Mantenha o servidor com o relógio sincronizado (NTP). O
tsé validado numa janela de ±120s; relógio muito defasado faz o link ser rejeitado. - CPF como login: envie apenas os números (sem pontos/traço).
- Nunca exponha o
external_login_secret— só no servidor. Ele nunca aparece na URL. - Cada pessoa que vai acessar gera seu próprio link (com o
logindela no payload).
Testando
- Gere um link com o código acima para uma pessoa ativa e existente na plataforma.
- Abra o link no navegador em até 2 minutos — o acesso deve ser efetuado automaticamente.
- Nossa equipe também consegue validar o processo de geração pelo painel administrativo (diagnóstico item a item) — envie um link de exemplo caso precise de suporte.
Erros comuns
| Sintoma | Causa provável |
|---|---|
| Link rejeitado logo após gerar | ts em milissegundos em vez de segundos; ou relógio do servidor defasado. |
| Sempre "acesso inválido" | Assinatura calculada sobre o JSON em vez do valor d; ou external_login_secret incorreto. |
| Funciona intermitentemente | Link pré-gerado/cacheado sendo aberto após os 2 minutos. |
| Caracteres estranhos na URL | d gerado em Base64 padrão (com +, /, =) em vez de url-safe sem padding. |
| Pessoa não encontrada | login divergente do cadastro (ex.: CPF com formatação, ou pessoa inativa). |
Dúvidas
Em caso de dúvida na integração, entre em contato com a nossa equipe. Para rotação do segredo (troca do external_login_secret), solicite formalmente — a rotação invalida os links assinados com o segredo anterior.