Autenticação
Este guia fornece uma visão geral do sistema de autenticação OAuth2 do Meaple e ajuda desenvolvedores a entender como utilizá-lo de forma eficaz.
Visão Geral
O Meaple utiliza OAuth 2.0 com PKCE (Proof Key for Code Exchange) para autenticação segura de usuários. O PKCE é obrigatório para todas as integrações, garantindo segurança mesmo sem client secret.
⚠️ Importante: Atualmente não oferecemos suporte para OpenID Connect (OIDC). A autenticação é feita exclusivamente via OAuth 2.0.
Pré-requisitos
Antes de começar, você precisa:
1. Obter o Client ID
O client_id é o ID do seu canal, obtido no painel em https://meaple.com.br
2. Registrar a Redirect URI
Registre a(s) URL(s) de callback que sua aplicação utilizará
⚠️ Importante: A
redirect_uriusada na requisição de autorização deve corresponder exatamente a uma das URIs registradas.
💡 Suporte: Atualmente, o registro de redirect URIs e domínios é feito diretamente no banco de dados. Entre em contato com o suporte para configurar sua aplicação.
Endpoints
| Endpoint | URL | Descrição |
|---|---|---|
| Authorization | https://whitelabel.meaple.app/api/oauth/authorize | Página de login OAuth |
| Token | https://whitelabel.meaple.app/api/oauth/token | Obter/renovar tokens |
| User Info | https://api.meaple.com.br/v1/users/me | Dados do usuário autenticado |
1. Authorization Request
Para redirecionar um usuário para a página de login, construa a URL de autorização com os parâmetros necessários.
Parâmetros Obrigatórios
| Parâmetro | Tipo | Descrição |
|---|---|---|
response_type | code | Indica que o fluxo Authorization Code será utilizado |
client_id | string | ID único do seu canal/aplicação |
redirect_uri | string | URL para redirecionamento após autenticação (deve estar registrada) |
state | string | String aleatória para prevenir ataques CSRF |
scope | string | Escopos separados por espaço (ex: profile email) |
code_challenge | string | Challenge derivado do code_verifier (PKCE) |
code_challenge_method | S256 | Método de hash utilizado |
Parâmetros Opcionais (Customização)
| Parâmetro | Tipo | Descrição |
|---|---|---|
theme | dark | light | Tema da página de login |
preferred_color | string | Cor primária em hex (ex: #fb923c) |
Exemplo de URL
https://whitelabel.meaple.app/oauth/sign-in
?response_type=code
&client_id=cmkeg9fpq001cmm0mu61qa6ks
&redirect_uri=https://seu-app.com/api/auth/callback
&state=OaQi-xy2CBAvzrNv_ed7gMuglH5NaoYp
&scope=profile+email
&code_challenge_method=S256
&code_challenge=1CZBq_T9lnVeGFc44Ph6voMwJj_gLlwp3ECFWjeaGLc
&theme=dark
&preferred_color=%23fb923cVerificando se a URL está correta
Ao acessar a URL de autorização, você deve verificar se todos os parâmetros foram configurados corretamente:
❌ Erro - Parâmetros faltando
Se você ver uma mensagem de erro em vermelho “missing required parameters”, verifique se todos os parâmetros obrigatórios estão presentes.
✅ Sucesso - Tela de login
Quando correto, você verá a tela de login com logo do canal, login social, campos de e-mail/senha e links de recuperação.

Implementação
const clientId = process.env.MEAPLE_CLIENT_ID;
const redirectUri = 'https://seu-app.com/api/auth/callback';
// Gerar PKCE
const verifier = await generateCodeVerifier();
const challenge = await generateCodeChallenge(verifier);
// Armazenar verifier para usar na próxima etapa
sessionStorage.setItem('code_verifier', verifier);
// Gerar state para proteção CSRF
const state = generateState();
sessionStorage.setItem('oauth_state', state);
// Construir URL de autorização
const authorizationUrl = new URL('https://whitelabel.meaple.app/oauth/sign-in');
authorizationUrl.searchParams.append('response_type', 'code');
authorizationUrl.searchParams.append('client_id', clientId);
authorizationUrl.searchParams.append('redirect_uri', redirectUri);
authorizationUrl.searchParams.append('state', state);
authorizationUrl.searchParams.append('scope', 'profile email');
authorizationUrl.searchParams.append('code_challenge_method', 'S256');
authorizationUrl.searchParams.append('code_challenge', challenge);
// Parâmetros opcionais de customização
authorizationUrl.searchParams.append('theme', 'dark');
authorizationUrl.searchParams.append('preferred_color', '#fb923c');
// Redirecionar usuário
window.location.href = authorizationUrl.toString();2. Retrieve Authorization Code
Quando o usuário completa o login com sucesso, ele é redirecionado para a redirect_uri especificada com os seguintes parâmetros:
https://seu-app.com/api/auth/callback?code=AUTH_CODE&state=OaQi-xy2CBAvzrNv_ed7gMuglH5NaoYpValidação do State
Importante: Sempre valide o parâmetro state para prevenir ataques CSRF.
const urlParams = new URLSearchParams(window.location.search);
const code = urlParams.get('code');
const returnedState = urlParams.get('state');
const savedState = sessionStorage.getItem('oauth_state');
if (returnedState !== savedState) {
throw new Error('State mismatch - possível ataque CSRF');
}
// State válido, prosseguir com a troca do código3. Obtain Access Token
Para trocar o authorization code por tokens, faça uma requisição POST para o endpoint de token.
Request
const tokenUrl = 'https://whitelabel.meaple.app/api/oauth/token';
const codeVerifier = sessionStorage.getItem('code_verifier');
const body = new URLSearchParams();
body.append('grant_type', 'authorization_code');
body.append('client_id', clientId);
body.append('redirect_uri', redirectUri);
body.append('code', code);
body.append('code_verifier', codeVerifier); // PKCE
const response = await fetch(tokenUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body,
});
const tokens = await response.json();Parâmetros
| Parâmetro | Tipo | Descrição |
|---|---|---|
grant_type | authorization_code | Tipo de concessão |
client_id | string | Mesmo client_id usado na autorização |
redirect_uri | string | Mesmo redirect_uri usado na autorização |
code | string | Código recebido na etapa anterior |
code_verifier | string | O verifier original usado para gerar o challenge |
Response
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "dGhpcyBpcyBhIHJlZnJlc2ggdG9rZW4..."
}| Campo | Tipo | Descrição |
|---|---|---|
access_token | string | Token para acessar a API |
token_type | Bearer | Tipo do token |
expires_in | number | Tempo de expiração em segundos |
refresh_token | string | Token para renovar o access_token |
4. Refresh Token
Quando o access_token expira, use o refresh_token para obter um novo.
const body = new URLSearchParams();
body.append('grant_type', 'refresh_token');
body.append('client_id', clientId);
body.append('refresh_token', refreshToken);
const response = await fetch('https://whitelabel.meaple.app/api/oauth/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body,
});
const { access_token, expires_in, refresh_token } = await response.json();5. Get User Info
Com o access_token, você pode obter os dados completos do usuário autenticado.
const response = await fetch('https://api.meaple.com.br/v1/users/me', {
headers: {
Authorization: `Bearer ${accessToken}`,
'X-Channel-Id': clientId,
},
});
const { user } = await response.json();Response
{
"user": {
"id": "cml5kizep000b0vr1uxdq1tte",
"name": "John Doe",
"email": "johndoe@meaple.com.br",
"document": "11111111111",
"phoneNumber": "+5511999999999",
"birthdate": "2001-01-01T00:00:00.000Z",
"role": "ADMIN",
"avatar": {
"url": "http://localhost:3333/a58de3cb-89b7-423e-bfbd-b95f45de2c5b.png"
}
}
}Campos do Usuário
| Campo | Tipo | Descrição |
|---|---|---|
id | string | ID único do usuário |
name | string | Nome completo do usuário |
email | string | E-mail do usuário |
document | string | CPF do usuário (apenas números) |
phoneNumber | string | Telefone no formato internacional (+55…) |
birthdate | string | Data de nascimento (ISO 8601) |
role | string | Papel do usuário (ADMIN, USER, etc.) |
avatar | object | Objeto contendo a URL do avatar do usuário |
avatar.url | string | URL da imagem de avatar |
Code Challenge e Verifier (PKCE)
O PKCE (Proof Key for Code Exchange) é necessário para clientes públicos para verificar que o cliente que iniciou a requisição de autorização é o mesmo que está trocando o código.
Implementação
export async function generateCodeVerifier(): Promise<string> {
const randomBytes = crypto.getRandomValues(new Uint8Array(32));
return base64UrlEncode(String.fromCharCode(...randomBytes));
}
export async function generateCodeChallenge(
codeVerifier: string
): Promise<string> {
const encoder = new TextEncoder();
const data = encoder.encode(codeVerifier);
const digest = await crypto.subtle.digest('SHA-256', data);
return base64UrlEncode(String.fromCharCode(...new Uint8Array(digest)));
}
function base64UrlEncode(str: string): string {
const base64 = btoa(str);
return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
}Generating State
O parâmetro state é usado para manter o estado da aplicação durante a autorização e prevenir ataques CSRF.
export function generateState(): string {
const timestamp = Date.now().toString();
const randomString = Math.random().toString(36).substring(2);
return timestamp + randomString;
}Usando com o SDK
Após obter o access_token, configure-o no SDK para usar rotas autenticadas.
import { MeapleSDK } from '@meaple-com/core';
const sdk = new MeapleSDK({
publicKey: 'pk_seu_channel_id',
baseURL: 'https://api.meaple.com.br',
});
// Configurar token global
sdk.setGlobalToken(accessToken);
// Agora você pode usar rotas autenticadas
const user = await sdk.users.getMe();
const orders = await sdk.users.getOrders();
const events = await sdk.users.getEvents();Rotas Públicas vs Autenticadas
Rotas Públicas (não requerem token)
// Buscar eventos
const { events } = await sdk.events.find();
// Buscar evento por slug
const event = await sdk.events.getBySlug('event-slug');
// Buscar categorias
const { categories } = await sdk.categories.find();
// Buscar ingressos de um evento
const { tickets } = await sdk.events.getTickets(eventId);Rotas Autenticadas (requerem token)
// Configurar token primeiro
sdk.setGlobalToken('seu_access_token');
// Perfil do usuário
const user = await sdk.users.getMe();
// Pedidos do usuário
const orders = await sdk.users.getOrders();
// Eventos com ingressos do usuário
const events = await sdk.users.getEvents();6. Logout OAuth
Use o logout OAuth para encerrar a sessão do usuário e invalidar o refresh_token.
Endpoint de Logout
- URL (whitelabel/site):
https://whitelabel.meaple.app/api/oauth/logout - Métodos suportados:
GETePOST
💡 Importante: A
post_logout_redirect_urique você usar precisa estar registrada para o seu canal.
Atualmente o registro de redirect URLs é feito via suporte — entre em contato com o suporte Meaple para cadastrar/alterar essas URLs.
Parâmetros
Envie os parâmetros abaixo na query string (tanto para GET quanto POST):
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
client_id | string | Sim | ID do canal (channel) que está realizando o logout |
post_logout_redirect_uri | string | Sim | URL para onde o usuário será redirecionado após o logout |
Exemplo de URL de Logout
GET https://whitelabel.meaple.app/api/oauth/logout
?client_id={CHANNEL_ID}
&post_logout_redirect_uri=https://seu-app.com/logged-outApós logout, o usuário será redirecionado para a URL especificada.
Integração com better-auth
Para aplicações Next.js, recomendamos usar a biblioteca better-auth com o plugin genericOAuth.
import { betterAuth } from 'better-auth';
import { genericOAuth } from 'better-auth/plugins';
export const auth = betterAuth({
plugins: [
genericOAuth({
config: [
{
providerId: 'meaple',
clientId: process.env.MEAPLE_CLIENT_ID!,
authorizationUrl: (() => {
const params = new URLSearchParams({
theme: 'dark',
preferred_color: '#fb923c',
});
return `https://whitelabel.meaple.app/oauth/sign-in?${params.toString()}`;
})(),
tokenUrl: 'https://whitelabel.meaple.app/api/oauth/token',
getUserInfo: async tokens => {
const response = await fetch(
'https://api.meaple.com.br/v1/users/me',
{
headers: {
Authorization: `Bearer ${tokens.accessToken}`,
'X-Channel-Id': process.env.MEAPLE_CLIENT_ID!,
},
}
);
const { user } = await response.json();
return {
id: user.id,
name: user.name,
email: user.email,
emailVerified: true,
};
},
scopes: ['profile', 'email'],
responseType: 'code',
pkce: true,
},
],
}),
],
});Próximos Passos
- Tratamento de erros (Core) — Tratamento de erros do SDK
- Guia rápido — Instalação, instância e primeira chamada
- React Query — Hooks para React