MobiariDocs

Autenticação

Tokens de sessão para apps, tokens de acesso pessoal para scripts e um único modelo de permissões por trás dos dois.

Toda requisição autenticada leva um bearer token:

Authorization: Bearer <token>

A API aceita dois tipos de token, de forma intercambiável, em todos os endpoints:

TipoOnde obterValidadePara que usar
Token de sessãoPOST /v1/auth/loginexpires_in (um dia por padrão); renovado com um refresh tokenApps em que uma pessoa faz login: o painel, apps móveis, sua própria interface
Token de acesso pessoal (bod_…)POST /v1/stores/{store_id}/api-tokensAté ser revogado ou até a data de expiração opcionalScripts, integrações, automações

Em qualquer caso, o token nunca pode fazer mais do que o usuário a quem ele pertence.

Login

curl -X POST https://api.mobiari.com/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email": "ana@example.com", "password": "…", "device_name": "Stock sync laptop"}'

device_name é opcional; ele identifica a sessão na lista de sessões do usuário. A resposta é diferenciada pelo campo status:

{
  "status": "authenticated",
  "token": "eyJhbGciOiJIUzI1NiJ9…",
  "token_type": "Bearer",
  "expires_in": 86400,
  "refresh_token": "bpr_2f8K…",
  "refresh_token_expires_in": 2592000,
  "session_id": "0199a4f2-6c1e-7d3a-9b1f-3e2c8a5d7f10",
  "user": { "id": "…", "email": "ana@example.com", "...": "…" }
}
CampoSignificado
tokenO token de acesso. Envie como Authorization: Bearer <token>.
expires_inSegundos até token expirar. Leia este valor; não fixe uma validade no código.
refresh_tokenTroca-se por um novo par em POST /v1/auth/refresh. Começa com bpr_. Funciona uma única vez.
refresh_token_expires_inSegundos até o refresh token expirar, caso não seja usado.
session_idEste login, como aparece em GET /v1/me/sessions.

Autenticação em dois fatores

Se a conta tiver um segundo fator ativado, a etapa da senha responde com um desafio em vez de tokens:

{
  "status": "two_factor_required",
  "challenge": "eyJ…",
  "methods": ["totp", "whatsapp"],
  "expires_in": 300
}

Conclua em até expires_in segundos com um dos methods:

MétodoChamadas
totpPOST /v1/auth/2fa/totp/verify com challenge e totp_code (do app autenticador)
whatsappPOST /v1/auth/otp/send com challenge, depois POST /v1/auth/otp/verify com challenge e code

As duas chamadas de verificação retornam a sessão autenticada (token, refresh_token, …). Novos métodos podem ser adicionados: ignore valores de methods que você não suporta.

Renovar uma sessão

Antes de token expirar, ou quando uma chamada retornar 401 unauthorized, troque o refresh token por um novo par:

curl -X POST https://api.mobiari.com/v1/auth/refresh \
  -H "Content-Type: application/json" \
  -d '{"refresh_token": "bpr_2f8K…"}'

A resposta é uma nova sessão: os mesmos campos de um login bem-sucedido, sem status.

Refresh tokens são rotativos. Cada um funciona uma única vez: a resposta traz um novo refresh_token, e você deve armazená-lo no lugar do antigo.

Reutilizar revoga a sessão. Apresentar um refresh token que já foi usado é tratado como roubo: a sessão inteira (todos os tokens derivados daquele login) é revogada e a chamada falha com 401 refresh_token_reused. O usuário precisa fazer login de novo.

ErroSignificadoO que fazer
401 invalid_refresh_tokenRefresh token desconhecido ou expiradoFaça login de novo
401 refresh_token_reusedJá usado ou revogado; a sessão agora está encerrada em todos os dispositivos que a usavamFaça login de novo e corrija o cliente, se não foi um ataque

Renove a partir de um único lugar por vez

Duas abas ou threads renovando com o mesmo token ao mesmo tempo é indistinguível de um token roubado: a segunda chamada recebe refresh_token_reused e desconecta o usuário. Centralize as renovações em uma única promise em andamento (ou um lock) e faça todas as requisições em espera usarem o resultado dela.

let refreshing: Promise<Session> | null = null;

function refresh(session: Session): Promise<Session> {
  refreshing ??= fetch('https://api.mobiari.com/v1/auth/refresh', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ refresh_token: session.refresh_token }),
  })
    .then(async (res) => {
      if (!res.ok) throw new SignedOut((await res.json()).code);
      return saveSession(await res.json()); // persist the NEW refresh_token
    })
    .finally(() => {
      refreshing = null;
    });
  return refreshing;
}

Logout e sessões

curl -X POST https://api.mobiari.com/v1/auth/logout \
  -H "Content-Type: application/json" \
  -d '{"refresh_token": "bpr_2f8K…"}'

O logout revoga a sessão: o refresh token dela e os tokens de acesso emitidos para ela deixam de funcionar. A resposta é 204 mesmo para um token desconhecido ou já revogado, então é seguro repetir a chamada.

MétodoCaminhoFinalidade
GET/v1/me/sessionsOs dispositivos conectados do usuário (um Page), do uso mais recente para o mais antigo, com device_name, last_used_at e current.
DELETE/v1/me/sessions/{session_id}Desconecta um dispositivo.

Tokens de acesso pessoal não são sessões e não aparecem nessa lista.

Tokens de acesso pessoal

Crie um no painel em Tokens de API, ou pela API:

curl -X POST "https://api.mobiari.com/v1/stores/$STORE_ID/api-tokens" \
  -H "Authorization: Bearer $BP_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Stock sync script",
    "scopes": ["products.read", "orders.read"],
    "expires_at": null
  }'

A resposta inclui o token bruto uma única vez. O servidor guarda apenas um hash sha256; se você perder o valor, rotacione o token.

bod_<43 characters, URL-safe base64>

Mantenha o prefixo bod_: é ele que permite que scanners de segredos (do GitHub e outros) reconheçam um token vazado.

Como um token se relaciona com o usuário

Um token é sempre emitido em nome de um usuário real, e nunca pode fazer mais do que esse usuário pode. Se o usuário for removido da loja, todos os tokens que ele emitiu deixam de funcionar. Não existem tokens órfãos.

  • Atribuição na auditoria. Alterações feitas com um token são registradas em nome do usuário que o emitiu, junto com o nome do token ("Aline, via o terminal do PDV").
  • Tokens restritos. Para uma integração que precisa continuar limitada mesmo que você seja o dono, emita o token apenas com os escopos necessários, ou convide um membro dedicado de "integrações" com permissões limitadas e emita o token em nome dele.

Escopos

scopes são códigos de permissão, os mesmos da página de permissões da equipe (products.read, orders.refund, …); GET /v1/permissions/catalog lista todos eles. ["*"] significa tudo o que o usuário emissor pode fazer. Quando chega uma requisição:

effective permissions = the user's permissions ∩ the token's scopes

A restrição também se aplica ao dono da loja: um token do dono com apenas products.read é de fato somente leitura. Você não pode conceder a um token uma permissão que você mesmo não tem.

Rotacionar e revogar

AçãoEndpointEfeito
ListarGET /v1/stores/{store_id}/api-tokensNome, escopos, últimos 4 caracteres, last_used_at; nunca o valor bruto
RotacionarPOST /v1/stores/{store_id}/api-tokens/{token_id}/rotateMesmo token, novo valor; o valor antigo para de funcionar imediatamente. Use após um vazamento.
RevogarDELETE /v1/stores/{store_id}/api-tokens/{token_id}Removido definitivamente. 204 No Content.
curl -X DELETE \
  "https://api.mobiari.com/v1/stores/$STORE_ID/api-tokens/$TOKEN_ID" \
  -H "Authorization: Bearer $BP_TOKEN"

Requisições com um token revogado falham com 401 imediatamente; não há período de carência.

401 ou 403?

RespostaCausaSolução
401 unauthorizedHeader ausente, token de sessão expirado, token revogado ou digitado erradoRenove a sessão ou confira o token (o prefixo bod_ foi removido?)
401 invalid_refresh_token / refresh_token_reusedA renovação falhouFaça login de novo
403 forbidden / missing_permissionO token é válido, mas o usuário ou os escopos do token não têm a permissãoConceda a permissão ou emita um token com esse escopo

Os erros seguem o formato descrito em Convenções da API.

Nesta página