Oston Developer Docs Guia de integração

Uma integração.
Dois fluxos.

Autentique usuários NIO no seu serviço e envie notificações de ativação ou cancelamento. Cada fluxo tem seu próprio contrato e sua própria credencial.

Antes de começar

A URL base de produção é https://oauth-nio.oston.io. Para integrar, solicite à Oston a chave do seu app e o segredo do backend (veja Cadastro e homologação). Os exemplos usam seu_app no lugar da chave real.

02 / LOGIN

Autenticação do parceiro

A integração exige um backend. O usuário informa suas credenciais somente no NIO. A Oston verifica a identidade e a elegibilidade para os produtos cadastrados no app solicitado.

Cadastre a integração

A Oston cadastra seu app com uma chave (por exemplo seu_app), a URL HTTPS exata de retorno que você informar e um segredo exclusivo do seu backend. Cada app tem sua própria chave e seu próprio segredo; não compartilhe credenciais entre apps ou ambientes.

O segredo tem 32 bytes aleatórios, representados por 64 caracteres hexadecimais minúsculos. Guarde-o apenas no backend; nunca em aplicativos, páginas, URLs ou logs.

Inicie com state e PKCE

  1. Gere um state imprevisível de 32 a 128 caracteres e um code_verifier aleatório de 43 a 128 caracteres. Use apenas A–Z a–z 0–9 . _ ~ -.
  2. Guarde ambos na sessão de login do seu backend. Calcule code_challenge = BASE64URL(SHA256(code_verifier)), sem padding.
  3. Redirecione o navegador para /login com os parâmetros abaixo. A sessão do navegador com a Oston deve sobreviver à ida ao NIO e ao retorno.
GET/loginRedirecionamento do navegador
https://oauth-nio.oston.io/login
  ?app=seu_app
  &state={state_da_sessao}
  &code_challenge={challenge_base64url}
  &code_challenge_method=S256

As quebras de linha são apenas para leitura. Envie uma única URL com os parâmetros codificados.

Uma nova tentativa no mesmo navegador substitui a anterior. GET /callback é reservado ao retorno do NIO; o parceiro não chama esse endpoint diretamente.

Receba o retorno no backend

https://backend.parceiro.example/callback
  ?code={codigo_de_uso_unico}
  &state={state_original}

Compare o state recebido com o da sessão e consuma essa transação uma única vez. Rejeite divergências. Remova os parâmetros do navegador e impeça cache, envio de referrer e registro da query string nos seus logs.

03 / SESSÃO

Troque o código por uma decisão

Faça a troca no backend em até 60 segundos. O código é ligado ao app e ao PKCE e só pode ser consumido uma vez, inclusive em requisições concorrentes.

POST/api/auth/exchangeBackend → Oston
Authorization: Basic BASE64(app:segredo_do_backend)
Content-Type: application/json

{
  "code": "codigo_recebido",
  "code_verifier": "verificador_original"
}

Resposta de sucesso · 200

{
  "app": "seu_app",
  "subject": "identificador-opaco-estavel-especifico-do-app",
  "eligible": true,
  "eligibility_source": "open",
  "authenticated_at": 1792500000,
  "expires_at": 1792503600
}
CampoComo usar
appDeve coincidir com o app que iniciou o login.
subjectIdentificador opaco e estável para esse app. Não é CPF e difere entre apps.
eligibleExija true para conceder acesso.
eligibility_sourceAceite live ou open em produção; rejeite qualquer outro valor (por exemplo mock, que existe somente em testes).
authenticated_at / expires_atSegundos Unix UTC. Os valores acima são ilustrativos; valide a expiração recebida.
Elegibilidade aberta (temporária)

A pedido do NIO, enquanto a nova verificação de elegibilidade não está disponível, todo usuário autenticado no NIO recebe eligible: true com eligibility_source: "open". A autenticação NIO (PKCE, nonce e assinatura) continua obrigatória. Quando a verificação voltar, o valor passa a ser live e usuários sem produto recebem access_denied. Não trate open como prova de assinatura.

Exemplo de troca (Node.js)

const response = await fetch('https://oauth-nio.oston.io/api/auth/exchange', {
  method: 'POST',
  headers: {
    Authorization: 'Basic ' + Buffer.from(`${APP_KEY}:${APP_SECRET}`).toString('base64'),
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ code, code_verifier: session.codeVerifier }),
  signal: AbortSignal.timeout(10_000),
});
const result = await response.json();
const valid = response.ok
  && result.app === APP_KEY
  && result.eligible === true
  && ['live', 'open'].includes(result.eligibility_source)
  && result.expires_at > Date.now() / 1000;
if (!valid) return denyAccess();
// result.subject identifica o usuário no seu app; a sessão expira até result.expires_at.
A sessão pertence ao parceiro

Crie sua sessão com expiração absoluta no máximo em expires_at. Após uma hora, reinicie o login para consultar novamente a elegibilidade. Não há refresh token, logout global ou revogação instantânea. A resposta não é um JWT transferível.

Tokens NIO/Salesforce e CPF não são retornados ao parceiro. Esta integração não oferece as antigas rotas /api/auth/refresh, /api/user ou /api/user/products.

04 / MOBILE

Apps móveis

Apps iOS e Android usam o mesmo contrato, sempre com um backend. O segredo nunca vai para o aplicativo: qualquer valor embutido em um app publicado pode ser extraído.

  1. O app gera o próprio verifier aleatório e envia apenas o challenge (S256) ao seu backend, abrindo a URL de início do login no navegador do sistema: ASWebAuthenticationSession no iOS ou Custom Tabs no Android (por exemplo WebBrowser.openAuthSessionAsync no Expo). Não use WebView: o NIO e as lojas desaconselham login em navegador embutido.
  2. Seu backend cria state e code_verifier, guarda junto com o challenge do app e redireciona para https://oauth-nio.oston.io/login.
  3. O retorno chega à URL HTTPS cadastrada do seu backend, que valida o state e faz a troca com o segredo.
  4. O backend redireciona para o esquema do app (por exemplo seuapp://auth?handoff=…) com um código de uso único e curta duração, sem tokens.
  5. O app troca esse código no seu backend enviando o verifier original e recebe a sessão do seu serviço.

Ligar o código de handoff ao verifier do app impede que outro aplicativo que registre o mesmo esquema use o código interceptado. Prefira Universal Links ou App Links quando disponíveis.

Exemplo (React Native / Expo)

const verifier = Crypto.randomUUID() + Crypto.randomUUID();
const challenge = base64url(await Crypto.digestStringAsync(
  Crypto.CryptoDigestAlgorithm.SHA256, verifier, { encoding: Crypto.CryptoEncoding.BASE64 }));

const result = await WebBrowser.openAuthSessionAsync(
  `https://login.seubackend.example/nio/login?challenge=${challenge}`,
  'seuapp://auth',
);
if (result.type !== 'success') return;
const { handoff, error } = Linking.parse(result.url).queryParams ?? {};
// POST handoff + verifier ao seu backend → sessão do seu serviço

No iOS, o sistema exibe "deseja usar <domínio> para iniciar sessão" com o domínio da primeira URL aberta. Use um domínio da sua marca para o início do login.

05 / EVENTOS

Notificações de ativação e cancelamento

O NIO envia eventos de entrada e ativação de produto, ou de cancelamento, referentes a um usuário e serviço. A rota informa o tipo do evento. O esquema dos campos NIO ainda será confirmado: o recebimento atual preserva o corpo sem interpretar usuário, serviço ou assinatura.

POST/api/webhooks/nio/activation
POST/api/webhooks/nio/cancellation

Credencial independente

Use Authorization: Bearer <token>. O token contém 64 caracteres hexadecimais minúsculos, gerados a partir de 32 bytes aleatórios e entregues por canal seguro. Ele é independente dos tokens OAuth e do segredo do parceiro.

POST /api/webhooks/nio/activation
Authorization: Bearer <token_entregue_ao_NIO>
Content-Type: application/json

{}

{} demonstra apenas o transporte; não define o esquema comercial do evento. Para cancelamentos, use a rota /api/webhooks/nio/cancellation.

Formato e tamanho

Envie texto UTF-8 com application/json, application/xml, text/xml ou text/plain. É aceito charset=utf-8 ou charset=UTF-8, com ou sem espaço após o ponto e vírgula. Sem Content-Type, o padrão é application/json.

O corpo é opaco: JSON malformado, arrays, XML, espaços e corpo vazio são preservados. Não envie binários, compressão ou outras codificações. Cabeçalhos arbitrários, query strings e credenciais não fazem parte do registro.

Mantenha as notificações abaixo de 190 KiB. O limite real é 262.144 bytes para o envelope inteiro na fila, incluindo o corpo em base64 e metadados. Portanto, 256 KiB não é o limite do corpo original.

Confirmação de recebimento · 200

{
  "accepted": true,
  "receipt_id": "identificador-gerado-pelo-gateway"
}
200 significa recebido na fila

A confirmação acontece depois que a fila aceita a mensagem. O armazenamento é assíncrono, com retenção prevista de cinco anos. Isso não significa que uma assinatura já foi ativada ou cancelada; o processamento comercial ainda depende do esquema NIO.

Uma nova tentativa HTTP gera outro recibo, mesmo com corpo idêntico. Não há garantia de ordenação entre eventos. O armazenamento trata repetições do mesmo recibo, mas a deduplicação de eventos comerciais ainda não faz parte do contrato. Esses eventos não alteram o login ou revogam sessões no estado atual.

06 / RECUPERAÇÃO

Erros e retentativas

Login e troca de código

Após uma transação válida, falhas de login retornam à URL cadastrada com error e o state original, sem código. Transações inválidas ou expiradas retornam JSON HTTP 400 sem redirecionamento.

Erro / situaçãoAção do parceiro
access_deniedNão conceda acesso: não foi encontrado um produto permitido para o app.
salesforce_unavailableA consulta falhou. Não interprete como ausência de assinatura.
customer_document_missing, invalid_customer_document, identity_mismatch, invalid_token, authorization_failedNão crie sessão. Reinicie o login quando apropriado; acione a Oston se persistir.
salesforce_environment_mismatchNegue acesso e acione a Oston para verificar a configuração.
401 · invalid_client na trocaVerifique app e segredo do backend.
400 · invalid_request / invalid_grant na trocaVerifique o formato; reinicie o login para obter um novo código e PKCE.
429 / 503Aguarde antes de uma nova tentativa. Não conceda acesso com base em uma falha.

Timeout na troca: o código pode já ter sido consumido. Reinicie o login; a troca não oferece repetição idempotente da resposta.

Notificações NIO

RespostaAção do NIO
200 · accepted: trueGuarde o receipt_id para correlação. A mensagem foi aceita na fila.
401 / 403Verifique a credencial. Um 403 persistente também pode ser bloqueio WAF; solicite análise à Oston antes de rotacionar o token.
413Reduza o tamanho do evento antes de reenviar.
415Use um dos Content-Types suportados em UTF-8.
429, 5xx ou timeoutRetente com espera exponencial e jitter. Preserve o evento para replay assistido.

Um timeout pode ocorrer depois do enfileiramento; uma retentativa pode produzir outro recibo. Falhas da fila retornam 503 sanitizado, e o gateway ou o autorizador podem gerar outros 5xx. O serviço não transforma falhas em confirmações falsas.

Alta disponibilidade não significa 100% de respostas bem-sucedidas. O comportamento de retentativa e replay do NIO deve ser validado na homologação.

07 / CADASTRO

Cadastro e homologação

Envie à Oston

Você recebe

Antes de entrar em produção

Resultados sintéticos ou eligibility_source=mock não comprovam a integração real.

Para suporte, informe ambiente, horário UTC e identificadores de correlação disponíveis. Não envie tokens, senhas, CPF ou payloads pessoais em tickets.