Reddit OAuth2
Authorization Code Exchange para web e mobile — setup em reddit.com/prefs/apps, scope identity, sem e-mail do Reddit, referência da API e exemplos de frontend.
Visão geral
O login Reddit é opcional e controlado por REDDIT_AUTH_ENABLED. Quando desligado, os endpoints Reddit respondem 503 Service Unavailable. O OAuth2 do Reddit usa o scope identity e não expõe endereço de e-mail. A API atribui um e-mail sintético estável {redditId}@users.noreply.reddit.com para o upsert da conta. A troca de token usa HTTP Basic; a busca de perfil usa Bearer + User-Agent obrigatório. JWTs nunca vão na URL de redirect; a API emite um exchangeCode de uso único (Redis, 60 segundos). O mesmo POST /auth/exchange é compartilhado com os demais provedores sociais.
- • Ativado por feature flag no ambiente
- • Strategy passport-oauth2 customizada + RedditAuthGuard
- • Perfil obtido via GET https://oauth.reddit.com/api/v1/me (Authorization: Bearer + User-Agent)
- • Scope identity — apenas username e id do Reddit; sem e-mail
- • Troca de token em https://www.reddit.com/api/v1/access_token usa HTTP Basic auth
- • Sintético {redditId}@users.noreply.reddit.com — não entregável
- • Allowlist de redirect evita open redirects
- • Mesmo formato de tokens do POST /auth/login
Preferências de app Reddit
Crie um web app em reddit.com/prefs/apps. Defina o redirect uri igual a REDDIT_CALLBACK_URL e solicite o scope identity durante a autorização.
- Abra https://www.reddit.com/prefs/apps
- Role até "developed applications" e clique em create another app... (ou create app)
- Escolha o tipo web app
- Informe um nome e defina redirect uri para o callback da API Nest (ex.: http://localhost:3000/api/v1/auth/reddit/callback)
- Copie o client id (abaixo do nome do app) e o client secret
- Garanta que REDDIT_CALLBACK_URL corresponda ao redirect uri registrado no Reddit
- A API solicita scope identity — o Reddit não devolve e-mail com esse scope
Variáveis de ambiente
Carregadas do .env via src/config/reddit-oauth.config.ts (namespace redditOAuth no ConfigService). Veja .env.example.
REDDIT_AUTH_ENABLED=true REDDIT_CLIENT_ID=your-reddit-client-id REDDIT_CLIENT_SECRET=your-reddit-client-secret REDDIT_CALLBACK_URL=http://localhost:3000/api/v1/auth/reddit/callback REDDIT_REDIRECT_ALLOWLIST=myapp://success,http://localhost:5173/auth/callback REDDIT_OAUTH_DEFAULT_ROLES=
Roles padrão para novos usuários Reddit
Novos usuários Reddit recebem roles: [] por padrão (igual ao register). Defina REDDIT_OAUTH_DEFAULT_ROLES no .env como lista separada por vírgula (ex.: user ou user,manager). Usuários existentes vinculados por redditId mantêm as roles atuais.
Sequência
- Cliente abre GET /api/v1/auth/reddit?redirect=<URL na allowlist>
- Usuário autoriza no Reddit; browser chama GET /api/v1/auth/reddit/callback
- API troca o code via HTTP Basic em access_token, chama GET https://oauth.reddit.com/api/v1/me, faz upsert, grava exchangeCode no Redis e redireciona para redirect?code=...
- Cliente faz POST { code } em /api/v1/auth/exchange e recebe JWTs
Referência da API
1. Iniciar OAuth
Abre o consentimento Reddit em https://www.reddit.com/api/v1/authorize. O query redirect deve bater com um prefixo de REDDIT_REDIRECT_ALLOWLIST (ou omita para usar a primeira entrada). Scope: identity. Duração: temporary.
GET /api/v1/auth/reddit?redirect=http%3A%2F%2Flocalhost%3A5173%2Fauth%2Fcallback
400 redirect inválido · 503 feature desligada
2. Callback
Tratado pela API. A troca de token usa https://www.reddit.com/api/v1/access_token com HTTP Basic auth (client id + secret). O perfil vem de https://oauth.reddit.com/api/v1/me. Redireciona para seu app com ?code= (exchange code, não o authorization code do Reddit).
GET /api/v1/auth/reddit/callback?code=REDDIT_AUTH_CODE&state=... → 302 Location: http://localhost:5173/auth/callback?code=EXCHANGE_CODE
3. Trocar code
Endpoint público compartilhado com outros provedores sociais. Consome o code (uso único) e retorna o mesmo formato do login.
POST /api/v1/auth/exchange
Content-Type: application/json
{
"code": "EXCHANGE_CODE"
}{
"user": {
"id": "...",
"email": "[email protected]",
"firstName": "reddit_username",
"lastName": "",
"roles": [],
"redditId": "abc123",
"isActive": true
},
"accessToken": "eyJhbGciOiJIUzI1NiIs...",
"refreshToken": "eyJhbGciOiJIUzI1NiIs..."
}401 code inválido/expirado/usado · 503 social auth desligado
Frontend — Web (SPA)
Mesmo padrão dos demais provedores OAuth: iniciar login no browser; sua rota de callback lê code da query e chama exchange. O objeto user inclui redditId e e-mail sintético — não use para envio de e-mail.
const API = 'http://localhost:3000/api/v1';
const redirect = encodeURIComponent(
'http://localhost:5173/auth/callback'
);
// Redirect full-page para Nest → Reddit → volta ao SPA
window.location.href = `${API}/auth/reddit?redirect=${redirect}`;// /auth/callback no SPA
const params = new URLSearchParams(window.location.search);
const code = params.get('code');
if (!code) throw new Error('Missing exchange code');
const res = await fetch('http://localhost:3000/api/v1/auth/exchange', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ code }),
});
if (!res.ok) throw new Error(await res.text());
const { user, accessToken, refreshToken } = await res.json();
// persista tokens conforme sua aplicação exige
sessionStorage.setItem('accessToken', accessToken);
// Reddit não tem e-mail real — use redditId como identificador estável
console.log('signed in', user.redditId);- • Adicione a URL de callback do frontend em REDDIT_REDIRECT_ALLOWLIST (ex.: http://localhost:5173/auth/callback)
- • REDDIT_CALLBACK_URL deve apontar para a API Nest, não para o SPA
- • Reddit nunca devolve e-mail real — user.email é sintético e não entregável
- • Armazene accessToken / refreshToken com segurança (memória + httpOnly cookie conforme sua preferência)
Frontend — Mobile (deep link)
Use um custom scheme (ou universal link) na allowlist. Após OAuth Reddit, a API redireciona para myapp://success?code=.... O app abre, parseia o code e chama exchange.
import * as Linking from 'expo-linking';
import * as WebBrowser from 'expo-web-browser';
const API = 'https://api.example.com/api/v1';
const redirect = encodeURIComponent('myapp://success');
async function signInWithReddit() {
const url = `${API}/auth/reddit?redirect=${redirect}`;
await WebBrowser.openAuthSessionAsync(url, 'myapp://success');
}import * as Linking from 'expo-linking';
async function exchangeFromUrl(url) {
const { queryParams } = Linking.parse(url);
const code = queryParams?.code;
if (!code) return;
const res = await fetch(`${API}/auth/exchange`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ code }),
});
const data = await res.json();
// data.accessToken, data.refreshToken, data.user
return data;
}
Linking.addEventListener('url', ({ url }) => {
exchangeFromUrl(url);
});- • Registre o scheme no SO / Expo app.json
- • Prefira Linking.addEventListener('url', ...) e getInitialURL()
- • Exchange via HTTPS contra a URL base da API
- • Identifique usuários por redditId — não pelo e-mail sintético
Notas de segurança
- • URLs de redirect devem bater com prefixos de REDDIT_REDIRECT_ALLOWLIST
- • Exchange codes são de uso único e expiram em 60 segundos (Redis)
- • Reddit nunca fornece e-mail — endereço sintético é só para upsert interno, não para notificações
- • Contas existentes são vinculadas por redditId; linking por e-mail não se aplica a usuários só-Reddit
- • A API Reddit exige User-Agent descritivo nas requisições de perfil
- • Usuários só-Reddit têm password null — login por senha / change-password falham até definir senha
Modelo de usuário
Campos da entidade Postgres / interface usados pelo login Reddit:
- • redditId — id Reddit único e nullable (id de /api/v1/me)
- • email — sintético {redditId}@users.noreply.reddit.com (Reddit não expõe e-mail)
- • password — nullable para contas só OAuth
- • isActive — true para novos usuários Reddit
- • roles — de REDDIT_OAUTH_DEFAULT_ROLES no env (padrão [])
Relacionado
- Hub Social Authentication
- Guia Google OAuth2
- Guia Facebook OAuth2
- Guia X / Twitter OAuth2
- Guia GitHub OAuth2
- Guia Figma OAuth2
- Guia LinkedIn OpenID Connect
- Guia Slack OpenID Connect
- Guia Atlassian OAuth 2.0 (3LO)
- Guia GitLab OAuth2
- Guia Bitbucket OAuth2
- Guia Discord OAuth2
- Guia Twitch OAuth2
- Guia Steam OpenID 2.0
- Auth & JWT