Login
Para comentar, a pessoa precisa estar logada. Existem dois jeitos, e você pode ligar um ou os dois na aba Configurações do painel.
| Login do seu site | Conta Qorum | |
|---|---|---|
| Para quem | Sites que já têm usuários e login | Sites sem login, ou para quem não tem conta no seu site |
| O que a pessoa faz | Nada: já entra com a conta do seu site | Cria usuário e senha dentro do widget |
| O que você faz | Uma rota no seu servidor que gera um token | Nada: só deixar a opção ligada |
| Nome e foto | Vêm do seu site, do jeito que você mandar | Usuário e nome de exibição da conta Qorum |
Com os dois ligados, o widget usa o login do seu site quando ele existe e oferece a conta Qorum para quem não está logado.
Login do seu site
A ideia: o seu servidor, que já sabe quem está logado, gera um token dizendo "esta é a Ana, id 123". O token é assinado com a sua chave secreta, então o Qorum sabe que foi você que gerou e que ninguém mexeu nele.
- A página pede um token ao seu servidor
Por uma rota sua, como
/qorum-token, só para quem está logado. - O seu servidor assina e devolve
Com a chave secreta, que nunca sai do servidor.
- A página entrega o token ao widget
Na opção
token. O widget manda o token ao Qorum em cada pedido. - O Qorum confere a assinatura
Se estiver tudo certo, a pessoa comenta com o nome e a foto que você mandou.
O que vai dentro do token
É um JWT assinado com HS256 e a chave secreta do site. Bibliotecas de JWT existem para todas as linguagens.
| Campo | Obrigatório | O que colocar |
|---|---|---|
sub | sim | O id do usuário no seu site, como texto. Até 64 caracteres: letras, números e _ . : @ -. Esse id nunca deve mudar para a mesma pessoa. |
aud | sim | A chave pública do site (pk_...). Sim, a pública: ela diz para qual site o token vale. |
iat | sim | Quando o token foi criado, em segundos desde 1970. A maioria das bibliotecas preenche sozinha. |
exp | sim | Quando o token vence. No máximo 25 horas depois do iat. Recomendado: 12 horas. |
name | sim | Nome que aparece nos comentários (até 120 caracteres). |
card | não | Objeto com o visual da pessoa. Veja O cartão. |
mod | não | true para quem é da sua equipe: pode aprovar, apagar, fixar e banir pelo widget. |
1. A rota no seu servidor
Ela devolve {"token": "..."} para quem está logado e 401 para quem não está. Guarde a chave secreta numa variável de ambiente, nunca no código que vai para o navegador.
// npm install jsonwebtoken
const jwt = require('jsonwebtoken');
app.get('/qorum-token', (req, res) => {
const user = req.user; // o usuário logado no seu site
if (!user) return res.sendStatus(401);
const token = jwt.sign(
{
name: user.name,
card: { username: user.username, avatar: user.photoUrl },
mod: user.isAdmin === true,
},
process.env.QORUM_SECRET, // sk_...
{
algorithm: 'HS256',
subject: String(user.id),
audience: 'pk_SUA_CHAVE_PUBLICA',
expiresIn: '12h',
}
);
res.json({ token });
});
<?php
// composer require firebase/php-jwt
use Firebase\JWT\JWT;
session_start();
$user = $_SESSION['user'] ?? null; // o usuário logado no seu site
if (!$user) { http_response_code(401); exit; }
$agora = time();
$token = JWT::encode([
'sub' => (string) $user['id'],
'aud' => 'pk_SUA_CHAVE_PUBLICA',
'iat' => $agora,
'exp' => $agora + 12 * 3600,
'name' => $user['name'],
'card' => ['username' => $user['username'], 'avatar' => $user['photo_url']],
'mod' => !empty($user['is_admin']),
], getenv('QORUM_SECRET'), 'HS256');
header('Content-Type: application/json');
echo json_encode(['token' => $token]);
# pip install PyJWT
import os, time, jwt
from flask import jsonify, abort
from flask_login import current_user
@app.get("/qorum-token")
def qorum_token():
if not current_user.is_authenticated:
abort(401)
agora = int(time.time())
token = jwt.encode({
"sub": str(current_user.id),
"aud": "pk_SUA_CHAVE_PUBLICA",
"iat": agora,
"exp": agora + 12 * 3600,
"name": current_user.name,
"card": {"username": current_user.username, "avatar": current_user.photo_url},
"mod": bool(current_user.is_admin),
}, os.environ["QORUM_SECRET"], algorithm="HS256")
return jsonify(token=token)
// go get github.com/golang-jwt/jwt/v5
func qorumToken(w http.ResponseWriter, r *http.Request) {
u := usuarioLogado(r) // o usuário logado no seu site
if u == nil {
http.Error(w, "", http.StatusUnauthorized)
return
}
agora := time.Now()
claims := jwt.MapClaims{
"sub": strconv.Itoa(u.ID),
"aud": "pk_SUA_CHAVE_PUBLICA",
"iat": agora.Unix(),
"exp": agora.Add(12 * time.Hour).Unix(),
"name": u.Name,
"card": map[string]any{"username": u.Username, "avatar": u.PhotoURL},
"mod": u.IsAdmin,
}
token, err := jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString([]byte(os.Getenv("QORUM_SECRET")))
if err != nil {
http.Error(w, "", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{"token": token})
}
2. O código da página
A página busca o token e entrega ao widget. Quando ele vence, o widget avisa por onExpired e a página busca outro:
<div id="comentarios"></div>
<script src="https://useqorum.com/widget.js"></script>
<script>
let token = null;
async function buscarToken() {
const r = await fetch('/qorum-token', { credentials: 'same-origin' });
token = r.ok ? (await r.json()).token : null; // 401 = ninguém logado
return token;
}
buscarToken().then(() => {
const comentarios = Qorum.montar(document.getElementById('comentarios'), {
key: 'pk_SUA_CHAVE_PUBLICA',
ref: 'post-42',
token: () => token,
onLogin: () => { location.href = '/entrar?volta=' + encodeURIComponent(location.href); },
onExpired: async () => { comentarios.definirToken(await buscarToken()); }
});
});
</script>
O cartão
O card é livre: o Qorum guarda e devolve do jeito que você mandou, no campo user de cada comentário. Quem usa a API pode colocar ali o que o próprio design precisar. O widget pronto entende estes campos:
| Campo | O que o widget mostra |
|---|---|
username | Usado quando não há displayName. |
displayName | O nome em destaque no comentário. |
avatar | Endereço https da foto. Sem foto, aparece a inicial do nome. |
rank | Um selo com nome, cor e ícone opcional: {"name": "Assinante", "nameColor": "#7c3aed", "icon": "https://..."}. A cor também pinta o nome. |
isStaff | Mostra o selo "Equipe". |
isVip | Mostra um selo de destaque. |
avatarFrame, avatarFrameScale | Imagem decorativa por cima da foto e o tamanho dela em relação à foto (padrão 1.2). |
Prefere que o Qorum busque o cartão no seu site? Veja Dados do site. Sem isso, o cartão é atualizado sempre que a pessoa usa um token novo. Para atualizar na hora (a pessoa trocou a foto, por exemplo), use PUT /v1/site/users/:id/card na API de gerência.
Segurança
- A chave secreta fica só no servidor. Se ela aparecer em algum lugar público, troque na hora em Configurações.
- Gere o token só para quem está logado, e sempre com o
subda pessoa logada, nunca com um id que veio da página. - Use validade curta (até 12 horas) e renove com
onExpired. - Sirva a rota do token só por HTTPS.
Conferindo o token
Na aba Testar conexão do painel, cole um token gerado pelo seu servidor. O teste diz exatamente o que está errado, como "aud precisa ser a chave pública deste site" ou "token vencido", e mostra o conteúdo que o Qorum leu. A API de gerência faz o mesmo teste.
Conta Qorum
Com a opção ligada, o widget mostra o botão Entrar para quem não está logado. A pessoa escolhe entre entrar ou criar conta:
- Usuário: 3 a 30 caracteres, letras minúsculas, números,
_e ponto. É com ele que ela entra (pode digitar com@na frente). - Senha: de 8 a 72 caracteres.
- Nome de exibição: opcional. Sem ele, aparece o usuário.
Não existe e-mail nem recuperação de senha. A conta vale em todos os sites que usam o Qorum, mas a pessoa entra separadamente em cada site. O login dura 30 dias. Nos comentários, o id dessa pessoa aparece como q: seguido de um número, como q:12.
Quem você adiciona como moderador no painel vira moderador no widget quando entra com a conta Qorum.
Fazendo a sua própria interface? As rotas de entrar e criar conta estão na API pública.