Construir uma Shopify POS UI Extension é uma ótima maneira de adicionar funcionalidades personalizadas ao seu Shopify POS. Você usa o Shopify CLI, gera sua extensão, personaliza o componente React e tudo parece funcionar bem... até você precisar fazer aquela chamada fetch para o seu backend.
Nesse momento podem surgir as seguintes perguntas:
- Como meu backend pode saber que essa requisição é legítima?
- Como ele sabe de qual loja (shop) essa chamada está vindo sem expor esse dado na requisição?
- Como impedir que alguém mal-intencionado finja ser a nossa extensão e envie dados falsos?
Se você já se deparou com isso, você sabe que a resposta é autenticação JWT. A Shopify resolve isso de forma elegante usando Session Tokens.
A documentação oficial mostra o caminho, mas pode ser um pouco abstrata.
Neste artigo, vou mostrar o guia prático e direto de como nós implementamos essa autenticação de ponta a ponta: do fetch na POS Extension até o middleware de verificação no nosso backend Node.js, utilizando a biblioteca oficial.
Vamos lá!
O Fluxo Geral
Antes de mergulhar no código, aqui está o resumo do fluxo de autenticação em 5 etapas:
- Frontend (POS Extension): Nossa extensão usa o Session API (
useApi) para pedir um JWT exclusivo para a Shopify no momento da requisição. - A Requisição: O frontend envia esse JWT no cabeçalho
Authorization: Bearer <token>para o nosso backend. - Backend (Seu Servidor): Nosso middleware intercepta a chamada, usa a biblioteca
@shopify/shopify-apipara verificar se o token é válido e, se for, identifica a loja e permite que a requisição continue.
Frontend - Pegando o token
Importante: A forma de resgate do token pode variar conforme a versão da API Shopify utlizada. Neste artigo estamos utilizando a versão 2025-07.
A Shopify torna essa etapa bem simples. Dentro da sua extensão (que provavelmente utiliza React), você pode usar o hook useApi do pacote @shopify/ui-extensions-react/point-of-sale.
// extensions/pos-ui-sua-extensao/src/SeuComponente.jsx
import React, {useState} from 'react';
import {
reactExtension,
useApi,
Screen,
Text,
} from '@shopify/ui-extensions-react/point-of-sale';
const SmartGridModal = () => {
const {currentSession, getSessionToken} =
useApi<'pos.home.modal.render'>().session;
const {shopId, userId, locationId, staffMemberId} = currentSession;
const [sessionToken, setSessionToken] = useState<string>();
useEffect(() => {
getSessionToken().then((newToken) => {
setSessionToken(newToken);
});
}, [])
return (
<Screen name="ScreenOne" title="Screen One Title">
<Text>
shopId: {shopId}, userId: {userId}, locationId: {locationId}, staffId:
{staffMemberId}
</Text>
<Text>sessionToken: {sessionToken}</Text>
</Screen>
);
};
export default reactExtension('pos.home.modal.render', () => (
<SmartGridModal />
));
Obs: Nos casos onde a chamada é provocada por uma ação (ex.: ao clicar no botão Enviar, é realizada uma fetch para o backend com um body que será processado) eu particularmente dou preferência para acionar o
getSessionTokendentro do handle que realizará a chamada. Isso garante que o sessionToken estará valido no momento do envio.const onClickHandle = () => { getSessionToken().then((newToken) => { const headers = { Authorization: `Bearer ${newToken}` }; fetch("https://myapi.com", { headers }).then((res) => console.log(res)); }); };
Backend - Verificando o Token
Essa é a parte crucial. O backend vai receber o token da POS Extension e precisa fazer duas coisas:
- Autenticar: É um token válido e foi realmente emitido pela Shopify?
- Autorizar: Essa loja (que o token diz representar) existe no nosso banco de dados?
Vamos usar a biblioteca oficial da Shopify para fazer o trabalho pesado.
Configuração da API (Seu shopifyApi.js)
Primeiro, precisamos garantir que o nosso backend conheça nossas chaves de API. Você provavelmente tem um arquivo de configuração parecido com esse (vamos chamar de config/shopifyApi.js) que inicializa a biblioteca da Shopify:
import "@shopify/shopify-api/adapters/node";
import { shopifyApi, ApiVersion } from "@shopify/shopify-api";
import * as dotenv from "dotenv";
dotenv.config();
// Inicializamos o objeto 'shopify' que será utilizado como instância principal da Shopify em toda a api
const shopify = shopifyApi({
apiKey: process.env.SHOPIFY_API_KEY,
apiSecretKey: process.env.SHOPIFY_API_SECRET,
hostName: process.env.HOST,
scopes: process.env.SCOPES,
apiVersion: "2025-07", // Use sua versão
});
Caso você não esteja familiarizado com a @shopify/shopify-api, acesse aqui a documentação completa.
Criando o Middleware
Agora finalmente iremos criar o middleware de autenticação e autorização para as rotas que serão utilizadas pelo POS Extension. Ele será executado antes das rotas protegidas.
// src/middlewares/auth.js
import { StoreRepository } from "../repositories/StoreRepository.js";
import { shopify } from "../config/shopifyApi.js"; // A config da Etapa 2a
export default async (req, res, next) => {
try {
// 1. Pegue o token do header da requisição
const token = req.headers["authorization"].split(" ")[1];
if (!token) {
throw new Error("Unauthorized");
}
// 2. A MÁGICA: Decodifica e Verifica o Token
// O `decodeSessionToken` faz tudo:
// - Pega as chaves da Shopify
// - Verifica a assinatura do token
// - Verifica se não expirou
// - Se tudo estiver OK, retorna o payload (os dados)
const payload = await shopify.session.decodeSessionToken(token);
// 3. Pega a URL da loja (ex: "https://minha-loja.myshopify.com")
const shopId = payload.dest;
// 4. Autorização: Verifica se essa loja existe no NOSSO banco
const store = await Store.findOne({ shopId });
if (!store) {
// O token é válido, mas a loja não está instalada no nosso app
throw new Error("Store not found");
}
// 5. Feito! Anexamos a loja na requisição
// Agora, todas as rotas seguintes terão acesso a `req.store`
req.store = store;
return next(); // Continua o fluxo para a rota
} catch (error) {
// Se `decodeSessionToken` falhar ou a loja não for encontrada,
// retornamos 401 Unauthorized.
return res.status(401).json({ message: "Unauthorized" });
}
};
Resumindo o que ocorre no código acima:
-
shopify.session.decodeSessionToken(token)realiza a autenticação (prova que o usuário é quem diz ser, ou seja, a Shopify). -
Store.findOne()realiza a autorização (prova que o usuário tem permissão para acessar o recurso, no caso do exemplo, verifica se a loja está cadastrada no sistema).
Aplicando o Middleware nas rotas
Com o middleware pronto, basta adicionar nas rotas que precisam de proteção.
// src/routes.js
import shopifyAuthMiddleware from "./middleware/shopifyAuth.js";
// ... demais rotas da aplicação
// Esta rota é pública, não precisa de token
routes.get("/api/public/health-check", (req, res) => {
res.json({ status: "ok" });
});
// Esta rota é PROTEGIDA pelo nosso middleware
routes.get(
"/api/private/meus-dados",
shopifyAuthMiddleware, // <-- O middleware entra aqui!
async (req, res) => {
// Se o código chegou aqui, o token é válido e req.store existe!
const store = req.store;
res.json({
message: `Olá, loja ${store.name}!`,
});
}
);
Conclusão
Está feito! Com essa arquitetura fechamos o ciclo de autenticação de ponta a ponta.
Vamos recapitular o que fizemos:
No Frontend (POS Extension): Usamos o hook getSessionToken para pegar um session token (JWT) válido a cada chamada de API.
No Backend (Middleware): Usamos o método shopify.session.decodeSessionToken para validar a assinatura do token contra as chaves públicas da Shopify.
Na Rota: Verificamos se a loja do token existe em nosso banco de dados (Store.findOne) antes de permitir o acesso.
Agora, seu backend tem uma forma robusta e segura de garantir que cada requisição vinda da sua POS Extension é legítima e autenticada. Chega de "token inválido" ou chamadas misteriosas!
E aí, como você faz?
Essa foi a maneira que encontramos para implementar a autenticação seguindo as melhores práticas da Shopify.
Como sua equipe lida com a autenticação de extensões? Você usa uma abordagem diferente? Encontrou algum problema no caminho?
Deixe seu comentário abaixo! Vamos trocar experiências.

Top comments (0)