Modos de Integração
Existem três formas de integrar um storefront com a Storefront API. Cada uma tem diferentes níveis de segurança e complexidade.
Comparação Rápida
| Modo | Autenticação | Segurança | Complexidade |
|---|---|---|---|
| 1. Hospedagem na plataforma | Cookies httpOnly (automático) | Máxima | Nenhuma configuração extra |
| 2. Frontend + Backend próprio | Cookies httpOnly (via proxy reverso) | Máxima | Requer proxy no servidor |
| 3. Apenas frontend próprio | Bearer Token (JWT no JavaScript) | Moderada | Simples, sem servidor |
Modo 1: Hospedagem na Plataforma (Recomendado)
A loja roda na infraestrutura da plataforma com domínio customizado (ex: www.minhaloja.com). O storefront e a API ficam no mesmo servidor, tudo same-origin.
Como funciona
- O frontend (storefront) e a API estão no mesmo domínio
- Os cookies httpOnly são definidos automaticamente pelo servidor
- O navegador envia os cookies em cada request sem intervenção do JavaScript
- Tokens nunca são acessíveis pelo JavaScript do frontend
Por que é o mais seguro
Tudo é same-origin. Cookies httpOnly, Secure, SameSite=Lax funcionam nativamente. Nenhuma configuração especial é necessária. O JavaScript do frontend nunca vê nenhum token de autenticação.
Modo 2: Frontend + Backend Próprio (Proxy Reverso)
O lojista hospeda seu próprio frontend e backend, e usa um proxy reverso para encaminhar requests à Storefront API. O resultado é a mesma segurança do Modo 1.
Como funciona
O servidor do lojista encaminha as requests GraphQL para a API da plataforma. Do ponto de vista do navegador, tudo é same-origin:
- O frontend chama
www.minhaloja.com/api/graphql(mesmo domínio) - O proxy reverso encaminha para
api.acessocomercial.com/graphql - Os cookies httpOnly são definidos no domínio
www.minhaloja.com - O navegador envia os cookies automaticamente em cada request
- Tokens nunca são acessíveis pelo JavaScript do frontend
Configuração por plataforma
Vercel (next.config.js rewrites)
module.exports = {
async rewrites() {
return [
{
source: '/api/graphql',
destination:
'https://storefront.acessocomercial.com/graphql'
}
];
}
}; Cloudflare Worker
export default {
async fetch(request) {
const url = new URL(request.url);
if (url.pathname.startsWith('/api/graphql')) {
const target =
'https://storefront.acessocomercial.com/graphql';
return fetch(target, {
method: request.method,
headers: request.headers,
body: request.body
});
}
}
}; Nginx
location /api/graphql {
proxy_pass https://storefront.acessocomercial.com/graphql;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Host $host;
proxy_pass_header Set-Cookie;
} Node.js / Express
import { createProxyMiddleware } from 'http-proxy-middleware';
app.use('/api/graphql', createProxyMiddleware({
target: 'https://storefront.acessocomercial.com',
changeOrigin: true,
pathRewrite: {
'^/api/graphql': '/graphql'
},
cookieDomainRewrite: {
'api.acessocomercial.com': ''
}
})); Como o frontend faz requests
O código do frontend é idêntico ao Modo 1. Basta chamar o endpoint local com credentials: 'include':
const response = await fetch('/api/graphql', {
method: 'POST',
credentials: 'include',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
query: `mutation {
clientLogin(input: {
email: "user@example.com"
password: "secret"
hostname: "minha-loja"
}) {
auth
}
}`
})
}); X-Auth-Mode, Authorization ou gerenciamento manual de tokens. O frontend se comporta exatamente como se estivesse hospedado na plataforma.
Modo 3: Apenas Frontend Próprio (Bearer Token)
O lojista hospeda apenas o frontend, sem servidor próprio. O frontend chama a API diretamente de um domínio diferente (ex: lovable.app, vercel.app). Cookies não funcionam neste cenário, então a API retorna tokens no corpo da resposta.
Como funciona
O modo Bearer é ativado enviando o header X-Auth-Mode: bearer nas requests GraphQL. Quando ativo:
- As mutations de login/signup retornam
jwtTokenerefreshTokenno corpo da resposta - O frontend armazena esses tokens em
sessionStorage(ou em memória) - Requests autenticadas enviam o header
Authorization: Bearer <token>
Exemplo completo: API Client
Crie um client reutilizável que gerencia tokens, refresh automático e carrinho:
const API_URL = 'https://storefront.acessocomercial.com/graphql';
const HOSTNAME = 'minha-loja';
// --- Token Storage ---
function getTokens() {
return {
jwt: sessionStorage.getItem('jwt'),
refresh: sessionStorage.getItem('refreshToken')
};
}
function saveTokens(jwtToken, refreshToken) {
sessionStorage.setItem('jwt', jwtToken);
sessionStorage.setItem('refreshToken', refreshToken);
}
function clearTokens() {
sessionStorage.removeItem('jwt');
sessionStorage.removeItem('refreshToken');
}
function isLoggedIn() {
return !!sessionStorage.getItem('jwt');
}
// --- Cart Storage ---
function getCartData() {
return localStorage.getItem('CartData') || '';
}
function saveCartData(xCookiesHeader) {
if (!xCookiesHeader) return;
try {
const cookies = JSON.parse(xCookiesHeader);
for (const cookie of cookies) {
if (cookie.name === 'CartData') {
localStorage.setItem('CartData', JSON.stringify(cookie.value));
}
}
} catch { /* ignore parse errors */ }
}
// --- GraphQL Client ---
async function graphql(query, variables = {}) {
const { jwt } = getTokens();
const cartData = getCartData();
const headers = {
'Content-Type': 'application/json',
'X-Auth-Mode': 'bearer'
};
if (jwt) {
headers['Authorization'] = `Bearer ${jwt}`;
}
if (cartData) {
headers['X-Cart'] = JSON.stringify({
CartData: JSON.parse(cartData)
});
}
const response = await fetch(API_URL, {
method: 'POST',
headers,
body: JSON.stringify({ query, variables })
});
// Save updated cart data from response
saveCartData(response.headers.get('X-Cart'));
const result = await response.json();
// If token expired, try refresh and retry
const isAuthError = result.errors?.some(
(e) => e.extensions?.code === 'UNAUTHENTICATED'
);
if (isAuthError && getTokens().refresh) {
const refreshed = await refreshSession();
if (refreshed) {
return graphql(query, variables); // retry with new token
}
}
return result;
}
// --- Auth ---
async function login(email, password) {
const result = await graphql(`
mutation Login($input: ClientLoginMutationInput!) {
clientLogin(input: $input) {
auth
jwtToken
refreshToken
}
}
`, {
input: { email, password, hostname: HOSTNAME }
});
const { auth, jwtToken, refreshToken } = result.data.clientLogin;
if (auth) {
saveTokens(jwtToken, refreshToken);
}
return auth;
}
async function refreshSession() {
const { refresh } = getTokens();
if (!refresh) return false;
const response = await fetch(API_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Auth-Mode': 'bearer'
},
body: JSON.stringify({
query: `mutation {
refreshSession(input: {
refreshToken: "${refresh}"
}) {
success
jwtToken
refreshToken
}
}`
})
});
const result = await response.json();
const data = result.data?.refreshSession;
if (data?.success) {
saveTokens(data.jwtToken, data.refreshToken);
return true;
}
clearTokens();
return false;
}
function logout() {
clearTokens();
}
// --- Usage ---
// Login
await login('user@example.com', 'secret');
// Authenticated query (token injected automatically)
const cart = await graphql(`
query { shoppingCart(hostname: "${HOSTNAME}") {
id totalPrice products { id title quantity }
}}
`);
// Add to cart
const updated = await graphql(`
mutation { addShoppingCartItem(input: {
hostname: "${HOSTNAME}" productId: "abc123" quantity: 1
}) { shoppingCart { id totalPrice } }}
`); Carrinho de compras (Cart)
O carrinho funciona via header X-Cart, que transporta o identificador do carrinho. Este mecanismo funciona cross-domain pois usa um header HTTP customizado (não depende de cookies do navegador).
- Envie
X-Cartcom o valor doCartDataem cada request - Leia o header
X-Cartda resposta para obter o CartData atualizado - Armazene o CartData localmente (
localStorage) entre requests - O exemplo do API Client acima já gerencia isso automaticamente
Login com Google (Cross-Domain)
Para domínios externos, o login com Google usa um fluxo de proxy centralizado, pois o Google requer que cada domínio seja registrado como JavaScript Origin autorizado.
- Redirecione o usuário para
/auth/google/start?hostname=STORE&redirect=CALLBACK_URL - O usuário faz login com Google na página da plataforma
- Após sucesso, o usuário é redirecionado de volta com um código temporário:
CALLBACK_URL?google_auth_code=CODE - O frontend extrai o código e chama
POST /auth/google/exchangecom o código para obter os tokens
Recomendações de segurança para o Modo 3
- Armazene tokens em
sessionStorage(limpo ao fechar a aba) — nunca emlocalStorage - Implemente Content Security Policy (CSP) no frontend
- Não exponha tokens em URLs ou logs
- Migre para o Modo 2 (proxy reverso) quando possível para produção
CORS
A Storefront API aceita requests de qualquer origem HTTPS. Não é necessário configurar CORS adicionalmente. Em desenvolvimento local, origens http://localhost também são permitidas.