O EmDash usa autenticação com passkeys como método principal de login. Passkeys são resistentes a phishing, não exigem senhas e funcionam em todos os dispositivos pelo navegador ou gerenciador de senhas.
Além das passkeys, você pode adicionar provedores de login plugáveis. GitHub e Google vêm com o EmDash. O provedor Atmosphere instalado separadamente adiciona contas AT Protocol, e a mesma interface de provedor está aberta a outros pacotes. Os provedores documentados GitHub, Google e Atmosphere podem criar a primeira conta de administrador ou fazer login de um usuário EmDash vinculado.
Em implantações Cloudflare, o Cloudflare Access é um modo de autenticação separado e exclusivo em produção. Ele valida credenciais Access nas rotas EmDash protegidas em vez de mostrar os métodos de login do EmDash.
Escolher um modo de autenticação
Passkeys usam WebAuthn, um padrão web que cria credenciais de chave pública armazenadas no dispositivo ou sincronizadas pelo gerenciador de senhas. Ao fazer login, o dispositivo prova a posse da credencial sem nunca enviar uma senha pela rede.
Passkeys são o padrão. Os provedores GitHub, Google e Atmosphere são métodos de login adicionais: cada um autentica o usuário, vincula ou cria uma conta EmDash e estabelece a mesma sessão EmDash usada por um login com passkey.
A autenticação com passkeys oferece:
- Sem senhas para lembrar ou vazar
- Resistente a phishing — as credenciais estão vinculadas ao domínio do site
- Sincronização entre dispositivos — funciona com iCloud Keychain, Google Password Manager, 1Password etc.
- Login rápido — um toque com biometria ou PIN
O Cloudflare Access usa a opção auth em vez de authProviders. Em produção, torna-se a autoridade das rotas /_emdash protegidas. O EmDash ainda armazena um usuário local para que papéis, propriedade e verificações de usuários desabilitados continuem funcionando.
Configurar o primeiro usuário
Na primeira vez que você acessa o painel de administração, o assistente de configuração orienta a criação da conta de administrador.
-
Vá para
http://localhost:4321/_emdash/admin -
Em Set up your site, digite o título do site e um slogan opcional. Um modelo também pode oferecer conteúdo de exemplo. Selecione Continue.
-
Em Create your account, digite seu endereço de e-mail e um nome opcional. Selecione Continue.
-
Em Secure your account, crie uma passkey ou escolha um dos provedores de login configurados. Se escolher uma passkey, o navegador pergunta onde salvá-la:
- No macOS: Touch ID, senha do dispositivo ou chave de segurança
- No Windows: Windows Hello ou chave de segurança
- No celular: Face ID, impressão digital ou PIN
-
Conclua o fluxo do navegador ou do provedor. O EmDash cria o primeiro usuário como Admin e abre o painel.
Entrar com uma passkey
Após a configuração, voltar ao painel de administração aciona a autenticação com passkey:
-
Visite
/_emdash/admin -
Se não estiver logado, você verá a página de login
-
Clique em Sign in para autenticar
-
O navegador solicita sua passkey (biometria, PIN ou chave de segurança)
-
Após a verificação, você é redirecionado ao painel de administração
Entrar com um magic link
Se não puder usar a passkey, um magic link oferece uma alternativa. O site precisa ter um provedor de e-mail configurado antes que o EmDash possa enviar o link — veja Configuração de e-mail.
-
Na página de login, clique em Sign in with email
-
Digite seu endereço de e-mail
-
Verifique a caixa de entrada para um link de login
-
Clique no link (válido por 15 minutos) e depois selecione Continue na página de confirmação
O link só é usado quando você seleciona Continue, para que scanners de segurança de e-mail que abrem links antecipadamente não o esgotem.
Configurar provedores de login
Além das passkeys, o EmDash oferece suporte a provedores de login plugáveis que aparecem na página de login e no assistente de configuração. GitHub e Google vêm com o EmDash. Atmosphere e provedores de terceiros são pacotes separados que se registram pela mesma interface.
Os provedores são aditivos — as passkeys continuam funcionando quando os provedores estão habilitados. GitHub e Google vinculam automaticamente um usuário EmDash existente apenas quando o provedor fornece o mesmo endereço de e-mail verificado. Contas Atmosphere são vinculadas pelo identificador descentralizado (DID), porque o fluxo Atmosphere do EmDash não recebe um endereço de e-mail. Cada provedor incluído pode criar o primeiro usuário, então uma instalação nova pode pular as passkeys por completo.
Adicionar provedores ao Astro
Passe os provedores ao array authProviders da integração EmDash. O exemplo a seguir habilita GitHub, Google e Atmosphere:
import { defineConfig } from "astro/config";
import emdash from "emdash/astro";
import { github } from "emdash/auth/providers/github";
import { google } from "emdash/auth/providers/google";
import { atproto } from "@emdash-cms/auth-atproto";
export default defineConfig({
integrations: [
emdash({
authProviders: [github(), google(), atproto()],
}),
],
});
A ordem importa na página de login: os provedores são renderizados na ordem em que você os lista, com provedores compactos só de botão primeiro e provedores que precisam de um formulário personalizado (como Atmosphere, que pede um handle) depois.
GitHub
O exemplo a seguir habilita o provedor GitHub:
import { github } from "emdash/auth/providers/github";
emdash({ authProviders: [github()] });
Defina as credenciais via variáveis de ambiente. O EmDash verifica primeiro os nomes com prefixo e recorre aos sem prefixo:
| Variable | Purpose |
|---|---|
EMDASH_OAUTH_GITHUB_CLIENT_ID / GITHUB_CLIENT_ID | ID do cliente do app OAuth |
EMDASH_OAUTH_GITHUB_CLIENT_SECRET / GITHUB_CLIENT_SECRET | Segredo do app OAuth |
Configure a URL de callback do seu app OAuth do GitHub como https://your-site.example.com/_emdash/api/auth/oauth/github/callback.
O exemplo a seguir habilita o provedor Google:
import { google } from "emdash/auth/providers/google";
emdash({ authProviders: [google()] });
Defina as credenciais via variáveis de ambiente. O EmDash verifica primeiro os nomes com prefixo e recorre aos sem prefixo:
| Variable | Purpose |
|---|---|
EMDASH_OAUTH_GOOGLE_CLIENT_ID / GOOGLE_CLIENT_ID | ID do cliente do app OAuth |
EMDASH_OAUTH_GOOGLE_CLIENT_SECRET / GOOGLE_CLIENT_SECRET | Segredo do app OAuth |
Configure o URI de redirecionamento do cliente OAuth do Google como https://your-site.example.com/_emdash/api/auth/oauth/google/callback.
Atmosphere (AT Protocol)
Para sites cujos colaboradores já têm uma conta Atmosphere — a identidade de propriedade do usuário por trás do Bluesky e da rede AT Protocol mais ampla — instale o provedor Atmosphere:
pnpm add @emdash-cms/auth-atproto
O exemplo a seguir habilita o provedor Atmosphere com uma lista de handles permitidos:
import { atproto } from "@emdash-cms/auth-atproto";
emdash({
authProviders: [
atproto({
allowedHandles: ["*.example.com"],
}),
],
});
Não é necessário segredo de cliente nem variável de ambiente. Consulte o guia de login Atmosphere para listas de permissão de handle/DID, mapeamento de papéis e a configuração de desenvolvimento local exigida pelo perfil OAuth do AT Protocol.
Criar um provedor
Um provedor é um AuthProviderDescriptor: um id, um rótulo legível e os componentes de administração, manipuladores de rotas, prefixos de rotas públicas e coleções de armazenamento de que seu fluxo de login precisa. Exporte um SetupStep de adminEntry se o provedor deve aparecer durante a configuração do primeiro usuário. A forma é exportada de emdash:
import type { AuthProviderDescriptor } from "emdash";
export function myProvider(): AuthProviderDescriptor {
return {
id: "my-provider",
label: "My Provider",
adminEntry: "my-provider/admin", // exports LoginButton / LoginForm / SetupStep
routes: [
{ pattern: "/_emdash/api/auth/my-provider/login", entrypoint: "my-provider/routes/login.ts" },
{ pattern: "/_emdash/api/auth/my-provider/callback", entrypoint: "my-provider/routes/callback.ts" },
],
publicRoutes: ["/_emdash/api/auth/my-provider/"],
storage: {
sessions: {},
},
};
}
O pacote Atmosphere (@emdash-cms/auth-atproto) é a referência real mais completa de um provedor que precisa de um formulário de login personalizado, manipuladores de rotas OAuth e armazenamento persistente.
Papéis de usuário
O EmDash usa controle de acesso baseado em papéis com cinco níveis:
| Role | Level | Description |
|---|---|---|
| Subscriber | 10 | Ler conteúdo publicado (sem acesso a rascunhos) |
| Contributor | 20 | Criar conteúdo (precisa de aprovação para publicar) |
| Author | 30 | Criar/editar/publicar o próprio conteúdo |
| Editor | 40 | Gerenciar todo o conteúdo |
| Admin | 50 | Acesso total, incluindo configurações |
Cada papel herda permissões de todos os níveis inferiores. O primeiro usuário é sempre criado como Admin.
Assinantes e conteúdo em rascunho
Assinantes têm a permissão content:read para que conteúdo publicado só para membros possa ser servido a leitores autenticados. Eles não podem ver rascunhos, itens agendados, itens na lixeira, revisões nem URLs de pré-visualização — esses são controlados por content:read_drafts, concedido a Contributor e acima. Os endpoints list e get filtram de forma transparente para status=published para Subscribers; as visualizações só de editor (/compare, /revisions, /trash, /preview-url) rejeitam de imediato as solicitações de Subscribers.
Convidar usuários
Administradores podem convidar novos usuários pelo painel de administração:
-
Vá para Settings > Users
-
Clique em Invite User
-
Digite o e-mail do usuário e selecione um papel
-
Clique em Send Invite
-
Se o e-mail estiver configurado, o EmDash envia o convite. Caso contrário, copie o link gerado e envie você mesmo ao usuário.
-
Eles abrem o link e criam a conta com uma passkey ou um provedor de login oferecido na página de convite.
Links de convite são de uso único e expiram após 7 dias.
Gerenciar passkeys
Os usuários podem gerenciar suas passkeys nas configurações da conta:
- Add passkey — Registrar passkeys adicionais como backup ou em outros dispositivos
- Remove passkey — Excluir passkeys que você não usa mais
- Rename passkey — Dar nomes descritivos às passkeys
Cada usuário pode ter até 10 passkeys registradas.
O EmDash não permite que um usuário remova sua última passkey. Adicione uma substituta antes de excluir a antiga.
Permitir que um grupo entre sem convites
Para permitir que um grupo entre sem convidar cada usuário, configure um provedor de login com uma lista de permissão. O provedor Atmosphere aceita allowedHandles e allowedDIDs (veja Login Atmosphere); o adaptador Cloudflare Access provisiona usuários do seu provedor de identidade via autoProvision e roleMapping. Os provedores documentados GitHub, Google e Atmosphere também podem criar a conta de administrador inicial.
Sessões
Os callbacks de passkey, magic link, convite e provedor de login armazenam o ID do usuário EmDash no armazenamento de sessões do Astro. O navegador recebe o identificador opaco astro-session do Astro; os registros de usuário e credenciais permanecem no banco de dados do EmDash.
O Cloudflare Access também grava o usuário EmDash resolvido na sessão do Astro. Isso permite que páginas públicas identifiquem um usuário logado ao ler Astro.locals.user. A sessão não substitui a autenticação Access nas rotas /_emdash protegidas: o EmDash valida novamente o JSON Web Token (JWT) Access nessas solicitações.
Limites de taxa de autenticação
O EmDash limita os endpoints que iniciam fluxos de login ou cadastro não autenticados. Os limites são separados para cada endpoint e IP de cliente confiável:
| Endpoint | Limit |
|---|---|
POST /_emdash/api/auth/passkey/options | 10 solicitações por minuto |
POST /_emdash/api/auth/magic-link/send | 3 solicitações por 5 minutos |
POST /_emdash/api/auth/signup/request | 3 solicitações por 5 minutos |
No Cloudflare, o EmDash lê o IP do cliente dos metadados de solicitação do Cloudflare. Um site auto-hospedado atrás de um proxy reverso deve configurar trustedProxyHeaders antes que o EmDash possa usar o cabeçalho de IP do cliente do proxy. Quando nenhum IP confiável está disponível, essas verificações por IP são ignoradas porque não há uma chave segura para contar.
Passkeys armazenam credenciais de chave pública; a chave privada permanece com o autenticador do usuário. Tokens de magic link são armazenados como hashes SHA-256 e excluídos após o uso.
Solução de problemas
”No passkeys registered”
Se você vir este erro ao entrar, sua passkey pode ter sido excluída do gerenciador de senhas. Peça a um administrador que envie um magic link de recuperação; o site precisa ter o e-mail configurado.
”Passkey authentication failed”
Isso geralmente significa que a passkey foi criada para um domínio diferente. Passkeys estão vinculadas ao domínio — uma passkey para localhost:4321 não funcionará em example.com. Registre uma nova passkey para cada domínio.
Todas as passkeys perdidas
Se você perdeu o acesso a todas as passkeys registradas:
- Peça a outro administrador que envie um magic link de recuperação. O site precisa ter o e-mail configurado.
- Abra o link em até 15 minutos e selecione Continue para entrar.
- Registre uma nova passkey nas configurações da conta.
Se você for o único administrador e o e-mail não estiver configurado, precisará redefinir a autenticação do site pelo banco de dados.
Cloudflare Access
Ao implantar no Cloudflare, você pode usar o Cloudflare Access no lugar dos métodos de login integrados. O Access autentica o usuário na borda com seu provedor de identidade. O EmDash valida o JWT Access assinado, carrega a identidade e os grupos da pessoa e mapeia essa identidade para um usuário EmDash local.
Quando usar o Cloudflare Access
- Single Sign-On — Os usuários autenticam com o IdP da empresa
- Controle de acesso centralizado — Gerencie quem pode acessar o admin no painel Cloudflare
- Sem gerenciamento de passkeys — Não é necessário registrar ou gerenciar passkeys
- Papéis baseados em grupos — Mapeie grupos do IdP para papéis EmDash automaticamente
Configurar o Access
- Crie um aplicativo e uma política Cloudflare Access para o caminho
/_emdash/*do site. Proteger apenas/_emdash/admin/*deixa a API REST sem o JWT que o EmDash espera. - Copie a Application Audience (AUD) Tag do aplicativo.
- Armazene a tag na variável de ambiente de runtime
CF_ACCESS_AUDIENCE. Siga o guia de segredos do EmDash para valores locais e implantados. - Configure o EmDash para ler esse valor em runtime:
import { defineConfig } from "astro/config";
import cloudflare from "@astrojs/cloudflare";
import emdash from "emdash/astro";
import { d1, access } from "@emdash-cms/cloudflare";
export default defineConfig({
output: "server",
adapter: cloudflare(),
integrations: [
emdash({
database: d1({ binding: "DB" }),
auth: access({
teamDomain: "myteam.cloudflareaccess.com",
audienceEnvVar: "CF_ACCESS_AUDIENCE",
}),
}),
],
});
A audience do aplicativo identifica qual aplicativo Access emitiu o JWT. O EmDash a verifica junto com o emissor e a assinatura; um token de outro aplicativo Access é rejeitado.
Opções de configuração
| Option | Type | Default | Description |
|---|---|---|---|
teamDomain | string | required | Seu domínio de equipe Access (ex.: myteam.cloudflareaccess.com) |
audience | string | — | Application Audience (AUD) Tag fornecida diretamente. Prefira audienceEnvVar em Workers. |
autoProvision | boolean | true | Criar usuários EmDash no primeiro login Access |
defaultRole | number | 30 | Papel para usuários que não correspondem a nenhum grupo (30 = Author) |
syncRoles | boolean | false | Atualizar o papel a cada login com base nos grupos do IdP |
roleMapping | object | — | Mapear nomes de grupos do IdP para níveis de papel |
audienceEnvVar | string | "CF_ACCESS_AUDIENCE" | Variável de ambiente que contém a tag de audience. Usada quando audience é omitido. |
Forneça audience ou um valor de ambiente sob audienceEnvVar.
Mapeamento de papéis
Mapeie seus grupos do IdP para papéis EmDash:
emdash({
auth: access({
teamDomain: "myteam.cloudflareaccess.com",
audienceEnvVar: "CF_ACCESS_AUDIENCE",
roleMapping: {
Admins: 50, // Admin
"Content Editors": 40, // Editor
Writers: 30, // Author
},
defaultRole: 20, // Contributor for users not in any group
}),
});
O primeiro grupo correspondente vence se um usuário pertencer a vários grupos. O primeiro usuário a acessar o site sempre se torna Admin, independentemente dos grupos.
Comportamento de sincronização de papéis
Por padrão (syncRoles: false), o papel de um usuário é definido no primeiro login e não muda depois. Isso permite que administradores ajustem papéis manualmente no EmDash.
Defina syncRoles: true se quiser que os grupos do IdP sejam autoritativos — o papel do usuário será atualizado a cada login com base nos grupos atuais.
Fluxo de solicitação e sessão
- O usuário visita um caminho protegido pelo aplicativo Access.
- O Cloudflare Access redireciona o usuário ao provedor de identidade quando não existe uma sessão Access.
- Após a autenticação, o Access envia um JWT assinado à origem em
Cf-Access-Jwt-Assertion. - O EmDash valida a assinatura, o emissor e a audience do token e depois lê a identidade e os grupos Access.
- O EmDash encontra ou provisiona o usuário local, aplica o comportamento de papel configurado e registra o usuário na sessão do Astro.
- Solicitações posteriores a rotas EmDash protegidas repetem a validação Access. Páginas públicas podem usar a sessão EmDash para identificar o usuário sem tratá-la como prova de uma nova solicitação Access.
Recursos substituídos pelo Access
Quando o Access está habilitado, estes recursos ficam indisponíveis:
- Página de login (
/_emdash/admin/login) - Registro e gerenciamento de passkeys
- Login GitHub, Google e Atmosphere
- Login com magic link
- Auto-cadastro
- Convites de usuário
As políticas Access decidem quem chega ao EmDash. O EmDash continua dono dos papéis locais, da propriedade do conteúdo e do indicador de usuário desabilitado. Com syncRoles: false, administradores podem alterar o papel de um usuário provisionado no EmDash. Com syncRoles: true, os grupos Access mapeados substituem esse papel a cada login.
Solução de problemas
”No Access JWT present”
A solicitação chegou ao EmDash sem um JWT Access. Isso significa:
- O Access não está configurado para proteger seu aplicativo
- A política Access não corresponde às rotas de administração
Verifique se o aplicativo Access cobre o caminho completo /_emdash/* e se a política inclui o usuário.
”JWT audience mismatch”
O audience na configuração não corresponde ao JWT. Verifique novamente a Application Audience Tag nas configurações do aplicativo Access.
”User not authorized”
O usuário autenticou via Access, mas autoProvision é false e ele não existe no EmDash. Ou:
- Defina
autoProvision: true, ou - Crie o usuário manualmente antes que ele faça login