Skip to content

Releases: DavideCarvalho/adonis-agora-authkit

@adonis-agora/authkit-server@0.71.0

Choose a tag to compare

@github-actions github-actions released this 20 Sep 05:31
8956512

Minor Changes

  • #226 35195ae Thanks @DavideCarvalho! - feat(authkit-server)!: a ponte de compatibilidade de e-mail sai; entra a migração authkit:users:normalize-emails

    PASSO OBRIGATÓRIO DE UPGRADE. Quem tem contas criadas antes da 0.69 precisa rodar
    node ace authkit:users:normalize-emails --apply ao subir esta versão. Sem isso, toda
    conta gravada com o endereço mutilado (ou com maiúsculas) fica inalcançável
    — e, por o
    login ser à prova de enumeração, sem nenhuma mensagem de erro para o dono dela.

    O que saiu. A ponte login.legacyEmailFallback (0.70), que no login tentava o endereço
    exatamente como digitado e a normalização legada do validator.js quando a forma normalizada
    não achava conta. Saíram com ela: a opção de config, legacyNormalizeEmailIdentifier,
    resolveEmailIdentifier, os tipos EmailIdentifierLookup e ResolvedEmailIdentifier, a
    segunda chave de sessão do login (authkit_login_email_lookup) e o options.legacyFallback
    de importUsers. Um login: { legacyEmailFallback: ... } no config vira uma chave
    desconhecida (ignorada) — apague-a. Fica uma normalização, normalizeEmailIdentifier
    (trim + toLowerCase), e a busca direta por ela.

    O que entrou. authkit:users:normalize-emails: varre as contas, calcula a forma
    normalizada e, por padrão, só relata — quantas mudariam, quais, e as colisões (duas
    contas que colapsam no mesmo endereço). Com --apply grava, recusando-se a tocar em
    qualquer conta envolvida numa colisão
    e listando-as para decisão humana: a migração nunca
    funde contas nem escolhe vencedor. Sai com código != 0 quando sobram colisões. A escrita
    usa a capacidade nova AccountEmailRewriteCapability (rewriteAccountEmail, presente no
    store Lucid; probe por supportsAccountEmailRewrite) — que não manda e-mail, não pede
    confirmação e não mexe em email_verified_at, porque a caixa postal é a mesma, só a
    grafia gravada muda. Store sem a capacidade: o relatório funciona e o --apply recusa.

    Por quê. A ponte eram ~120 linhas de compatibilidade, uma opção de config, uma segunda
    chave de sessão e uma chamada em cada ponto que resolve e-mail (login, cadastro, social,
    admin, import) para um problema que se resolve UMA vez, no banco. E ela cobrava caro no pior
    lugar: buscas extras a cada e-mail desconhecido no login, que é o caminho de ataque.

    Anti-enumeração intacta — na verdade, mais apertada: o passo de identificador agora não
    consulta o account store nenhuma vez (a ponte consultava), então não há nem diferença de
    tempo entre "achei" e "não achei". A tela continua mostrando o que a pessoa digitou e o
    passo 1 continua respondendo o mesmo redirect incondicional.

@adonis-agora/authkit-server@0.70.0

Choose a tag to compare

@github-actions github-actions released this 20 Sep 04:48
3fff3b7

Minor Changes

  • #223 6adae6d Thanks @DavideCarvalho! - feat(authkit-server): escritas de organização e de segundo fator em JSON (/account/api/*)

    Um host que desenha as PRÓPRIAS telas de conta não tinha como criar org, convidar, trocar
    papel, remover membro, revogar/aceitar convite nem gerenciar TOTP/passkeys sem mandar o
    usuário ao console /account/*: essas operações só existiam como POST de formulário, com
    redirect. As leituras já eram JSON; faltava o outro lado.

    • Orgs: POST /account/api/orgs, /orgs/deactivate, /orgs/invitations/:token/accept,
      /orgs/:id/{activate,leave,invitations}, DELETE /orgs/:id/invitations/:invId,
      PATCH|DELETE /orgs/:id/members/:accountId. Mesmos guards do formulário (escopo por
      conta, owner/admin na org do path, catálogo de papéis, owner só por owner) e a MESMA
      política efetiva — o resolver saiu para host/org_policy.ts, compartilhado com o console.
    • Segundo fator: POST /account/api/mfa/totp/{enroll,confirm,disable},
      /mfa/recovery-codes e a cerimônia de passkey em JSON
      (/mfa/passkeys/{options,verify}) — a clássica responde 302 e não cabe numa SPA. Mesmos
      gates de sudo do console; a recusa vira 403 sudo_required em vez de um redirect.
      requireSudo passa a delegar a decisão ao novo isSudoSatisfied, que é o que o caminho
      JSON consome — uma política só, duas formas de recusar. O confirm leva o throttle do
      bucket de sudo (o form não tem; o JSON fica mais apertado, nunca mais frouxo).
    • MfaCapability ganha countRecoveryCodes e regenerateRecoveryCodes OPCIONAIS
      (capability-probe via supportsRecoveryCodeCount / supportsRecoveryCodeRegeneration),
      implementados no lucidAccountStore. GET /account/api/mfa passa a devolver
      recovery.remaining e recovery.regenerable. Novo evento de auditoria
      mfa.recovery_codes_regenerated.

    Compatível: o console HTML não muda: rota nova, resposta nova. As rotas JSON são montadas
    mesmo com as telas orgs/mfa desligadas — é justamente o host com telas próprias que
    precisa delas.

  • #224 db70f9d Thanks @DavideCarvalho! - fix(authkit-server): uma normalização só para a identidade por e-mail (cadastro e login)

    O cadastro validava o e-mail com .normalizeEmail() do VineJS — os defaults do
    validator.js, que no gmail removem os pontos e o sub-endereço +tag — enquanto o passo
    de identificador do login não normalizava nada e buscava por igualdade exata. Quem se
    cadastrou com davi.carvalho96@gmail.com teve a conta gravada como
    davicarvalho96@gmail.com e, ao tentar entrar com o endereço certo, nunca recebia e-mail;
    por a tela ser à prova de enumeração, sem nenhuma mensagem de erro. E, por o login ser
    sensível a maiúsculas, nem Davi@x.com achava davi@x.com.

    Agora há UMA normalização, conservadora e exportada — normalizeEmailIdentifier
    (trim + toLowerCase, nada além disso) —, aplicada no cadastro (com e sem senha), no
    "esqueci a senha", na troca de e-mail, na criação de usuário por admin, no convite de
    organização, no import de usuários, no cadastro social e no passo de identificador do
    login
    , que antes não usava nenhuma.

    Mudança de comportamento observável: o cadastro passa a gravar o endereço que a pessoa
    digitou. davi.carvalho96@gmail.com e davi.carvalho96+lastro@gmail.com deixam de colapsar
    em davicarvalho96@gmail.com — são identidades distintas, como em qualquer outro provedor.
    Contas já gravadas não são alteradas.

    Compatibilidade: login.legacyEmailFallback (default true) é uma ponte temporária —
    quando o endereço normalizado não acha conta, o login (OIDC e console de conta) e o
    "esqueci a senha" tentam a forma exatamente como digitada e a normalização legada, e só
    aceitam se apontarem para exatamente uma conta; empate é tratado como "não achei". O
    cadastro, o "Continuar com o Google", a criação de usuário por admin e o
    authkit:users:import usam a mesma resolução para não criar uma SEGUNDA conta para quem já
    tem uma gravada mutilada (importUsers recebe o flag por options.legacyFallback). Nada
    disso muda a tela, a mensagem ou o comportamento à prova de enumeração. Migre os endereços
    gravados e desligue a ponte.

@adonis-agora/authkit-server@0.69.0

Choose a tag to compare

@github-actions github-actions released this 20 Sep 04:05
92b0fcb

Minor Changes

  • #221 7a56652 Thanks @DavideCarvalho! - feat(authkit-server): área de conta aceita a sessão do IdP (SSO) — accountSession.acceptIdpSession

    O console de conta (/account/*, admin) só aceitava a própria sessão
    (ACCOUNT_SESSION_KEY, criada pelo POST /account/login); o login da interaction OIDC
    cria só a sessão do oidc-provider. Resultado: quem acabou de entrar num app OIDC (ou no
    próprio host, quando ele é IdP e RP) levava um segundo pedido de login ao abrir
    /account/*.

    Com accountSession: { acceptIdpSession: true }, os guards do console (e o
    AccountAuthMiddleware/aceite de convite de org) abrem o console para a conta de uma
    sessão do IdP válida (cookie assinado, não expirada, conta existente e habilitada). O
    console aberto assim fica amarrado a essa sessão: termina quando ela termina, e o "Sair"
    do console encerra também a sessão do IdP. Novo helper público ensureConsoleSession(ctx)
    para guards do host. Default false — nada muda para quem não liga.

  • #221 7a56652 Thanks @DavideCarvalho! - feat(authkit-server): política de redirect URI no registro dinâmico (RFC 7591/7592)

    O /reg aceitava qualquer redirect sintaticamente válido (qualquer https://): com
    registro aberto, qualquer um registrava um client com callback no próprio domínio e
    usava a tela de consent do IdP como isca. Nova opção
    dynamicRegistration.redirectUriPolicy (loopback, exact, appSchemes, anyHttps),
    aplicada antes do provider no POST /reg e no PUT /reg/:id, que também restringe o
    client ao fluxo de código (authorization_code + refresh_token, response_type=code)
    e registra clientes só-loopback/app instalado como application_type: native. Gancho
    dynamicRegistration.validateRegistration para regras do host (RegistrationPolicyError
    400).

    Mudança de default: registro aberto (sem initialAccessToken) passa a aceitar só
    redirects loopback (http://localhost|127.0.0.1|[::1], qualquer porta). Callbacks web de
    fornecedores e esquemas de app precisam ser listados em exact/appSchemes
    (anyHttps: true ou redirectUriPolicy: false restauram o comportamento anterior).
    Registro com initialAccessToken não muda.

  • #221 7a56652 Thanks @DavideCarvalho! - feat(authkit-server): resource indicators (RFC 8707) com access tokens opacos

    A feature resourceIndicators do oidc-provider só era montada quando algum AT era JWT;
    com tokens opacos, um resource no authorize (clientes MCP sempre mandam) era recusado
    com invalid_target. Agora accessTokens.resources também liga a feature com tokens
    opacos: o resource declarado (tolerando barra final) é concedido no consent e amarrado
    ao AT (aud), que continua opaco, encontrável por AccessToken.find e introspecionável.
    Pedidos sem resource não mudam (AT opaco sem aud, userinfo funciona).

    resource fora da lista declarada (chaves de resources + o audience no modo JWT)
    passa a ser recusado com invalid_target também no modo JWT — antes, um resource
    desconhecido saía como token opaco com aud arbitrário.

Patch Changes

  • #221 7a56652 Thanks @DavideCarvalho! - fix(authkit-server): política de organizations (allowSelfCreate, roles, invitationTtlHours) volta a valer

    Desde a remoção da config de política legada, resolveOrganizations fixava
    allowSelfCreate: false (e roles/TTL no default), e o AccountOrgsController lia só
    esse valor estático — nunca a setting organizations_policy que a doc manda usar. Pior:
    declarar organizations no defineConfig trava a setting, então um host com
    organizations: { enabled: true } não tinha jeito nenhum de ligar o self-create
    (POST /account/orgs → 403).

    • OrganizationsConfigInput aceita de novo allowSelfCreate, roles e
      invitationTtlHours — com a setting travada, eles são a política efetiva.
    • /account/orgs (tela, criação e convites) e o TTL dos convites do admin/Admin API
      resolvem a política efetiva: setting da org → setting global → config → default.

@adonis-agora/authkit-react@0.22.0

Choose a tag to compare

@github-actions github-actions released this 20 Sep 04:48
3fff3b7

Minor Changes

  • #223 6adae6d Thanks @DavideCarvalho! - feat(authkit-react): client e hooks para as escritas de conta (orgs + segundo fator)

    Lado React do espelho JSON de /account/api/*, para telas do próprio host.

    • client.account.orgs ganha create, activate, deactivate, leave, invite,
      revokeInvitation, updateMemberRole, removeMember e acceptInvitation.
    • client.account.mfa vira uma FUNÇÃO COM PROPRIEDADES: mfa() continua sendo a leitura do
      status (assinatura preservada) e as escritas penduram nela — mfa.enroll(),
      mfa.confirm(code), mfa.disable(), mfa.regenerateRecoveryCodes() e
      mfa.passkeys.{options,verify}.
    • Hooks novos: useAccount{Create,Activate,Deactivate,Leave}OrgMutationOptions,
      useAccount{InviteOrgMember,RevokeOrgInvitation,AcceptOrgInvitation,UpdateOrgMemberRole,RemoveOrgMember}MutationOptions,
      use{Enroll,Confirm,Disable}TotpMutationOptions,
      useRegenerateRecoveryCodesMutationOptions e useRegisterPasskeyMutationOptions. O
      prefixo useAccount… nos de org evita colidir com os homônimos da superfície ADMIN, que
      agem sobre qualquer org.
    • registerPasskeyJson(client, deps?): a cerimônia de registro de passkey inteira por
      fetch (options → startRegistration → verify), o par headless do usePasskeyRegistration
      (que é por form de página inteira e não muda). Sem React, para quem não usa TanStack.
    • authkitKeys.account.mutations.*: as chaves das novas mutations, com prefixos orgs() e
      mfa() para useIsMutating.
    • AccountMfaStatus.recovery ganha remaining e regenerable.

@adonis-agora/authkit-server@0.68.4

Choose a tag to compare

@github-actions github-actions released this 17 Sep 15:35
622b2c9

Patch Changes

  • #219 9279596 Thanks @DavideCarvalho! - fix(authkit-server): cookie de org assinado chega URL-encoded no contexto Koa

    Complemento do fix anterior (0.68.3). A verificação da assinatura checava
    startsWith('s:') no valor cru, mas o jar Koa do oidc-provider devolve o valor
    como está no header — sem URL-decode — e o browser reenvia o cookie exatamente
    como o host o escreveu. Na prática chega s%3A<b64>.<hmac>, então a checagem nunca
    casava e a org continuava sendo descartada: o loadExistingGrant seguia sem
    reconciliar o activeOrg.

    O leitor agora normaliza antes de decidir o formato (tenta decodeURIComponent
    e só então checa o prefixo assinado). Confirmado contra o cookie real do app, com a
    APP_KEY real:

    raw (como no header): s%3AeyJtZXNzYWdlIjoib3JnLWZtby1kZW1v…
    SEM appKey : null
    COM appKey : { orgId: 'org-fmo-demo', orgSlug: 'demo', orgRole: 'owner' }
    

    O teste ganhou o caso da forma URL-encoded, que é a que o browser de fato envia.

@adonis-agora/authkit-server@0.68.3

Choose a tag to compare

@github-actions github-actions released this 17 Sep 15:19
6811691

Patch Changes

  • #217 927c70b Thanks @DavideCarvalho! - fix(authkit-server): cookie de org assinado não era verificado no contexto Koa

    O host grava o cookie authkit_active_org via ctx.response.cookie, e o AdonisJS
    o assina (s:<base64url>.<hmac>, MessageVerifier). As duas leituras no
    contexto do oidc-provider liam o valor cru e só tentavam URL-decode, então o
    envelope assinado não parseava e a org era descartada:

    • readActiveOrgFromKoaCtx — usada por loadExistingGrant no authorize.
    • readActiveOrgFromHostCtx — no caminho Koa (o caminho Adonis já desassina sozinho).

    Efeito: loadExistingGrant nunca reconciliava o activeOrg do Grant. Como o
    consent só roda uma vez por grant, quem ativava a organização depois do
    primeiro login não recebia org_id/org_slug/org_role no token — e
    /o/:orgSlug/... (protegida pelo requireOrg) redirecionava de volta para a
    escolha de organização, indefinidamente.

    O leitor agora verifica a assinatura com a appKey antes de aceitar o valor:
    HMAC-SHA256 sobre o payload, purpose = nome do cookie, comparação em tempo
    constante. Sem a chave o valor assinado é recusado — este cookie decide a claim de
    organização, e aceitar um valor não verificado deixaria o usuário trocar de tenant
    forjando o cookie. As formas crua e URL-encoded continuam aceitas para hosts que
    não assinam.

@adonis-agora/authkit-server@0.68.2

Choose a tag to compare

@github-actions github-actions released this 17 Sep 13:45
0003c3e

Patch Changes

  • #211 035f277 Thanks @DavideCarvalho! - fix(authkit-server): formulários de organizações sem token CSRF

    account/orgs.edge era a única view da lib que renderizava formulários POST
    sem <input name="_csrf"> (ativar, desativar, sair, aceitar convite e criar org),
    e o accountOrgsController.index não passava csrfToken no contexto de render —
    ao contrário de todos os outros controllers, que passam
    csrfToken: ctx.request.csrfToken.

    Com CSRF ligado (o padrão de um app Adonis com @adonisjs/shield), todo POST de
    organização falhava com "Invalid or expired CSRF token"
    : ativar ou trocar de
    organização nunca funcionou. O usuário ficava preso no seletor de organização e,
    como o token de sessão nunca ganhava a claim de org, nenhuma rota de tenant
    (/o/:orgSlug/..., protegida pelo requireOrg) abria
    — mesmo o fluxo de código
    já emitindo as claims de org corretamente.

    O teste de _csrf em edge_views tinha a lista de views escrita à mão, e
    account/orgs.edge não estava nela, então a falha passou despercebida. Ele agora
    varre o diretório de views e exige _csrf em qualquer view que tenha form
    POST, para que uma view nova não escape do mesmo jeito.

@adonis-agora/authkit-server@0.68.1

Choose a tag to compare

@github-actions github-actions released this 17 Sep 03:48
67537f4

Patch Changes

  • #209 73f6723 Thanks @DavideCarvalho! - fix(authkit-server): claims de organização no fluxo authorization code

    As claims org_id/org_slug/org_role nunca eram emitidas no fluxo response_type=code: a org ativa tem uma única fonte — o cookie do browser — e o id_token desse fluxo é emitido no /oidc/token, uma requisição server-a-servidor do app, sem os cookies do usuário. O findAccount lia apenas o cookie, activeOrg era null e as três claims (opcionais) sumiam em silêncio — o requireOrg do authkit-client então recusava toda rota de tenant.

    A correção persiste a org no Grant durante o consent (aí a request é do browser, com o cookie presente) e a lê no mint via ctx.oidc.entities.Grant.activeOrg, com o cookie como fallback para fluxos em que o id_token sai no próprio authorize. Como o modelo Grant do oidc-provider filtra o payload por IN_PAYLOAD, a lib estende IN_PAYLOAD com activeOrg e injeta a subclasse na instância do provider (o oidc-provider v9 não expõe a opção models). Grants reaproveitados (consent já lembrado) são reconciliados com o cookie no authorize, para que uma troca de org não fique presa na org antiga até o grant expirar. O leitor do cookie via jar Koa também passou a URL-decodificar o valor, que o cookies devolve como está no header.

@adonis-agora/authkit-server@0.68.0

Choose a tag to compare

@github-actions github-actions released this 17 Sep 02:13
843bc80

Minor Changes

  • #206 84c62dc Thanks @DavideCarvalho! - Organizations passa a existir por padrão: a OrganizationsCapability não some mais em silêncio quando o host esquece organizationModels.

    O lucidAccountStore(Model, options) só anexava a capability quando options.organizationModels era true ou o trio explícito:

    ...(OrgModels ? buildOrganizations({...}) : {})

    Consequência: um host que já tinha dito organizations: { enabled: true } — e cujas rotas /account/orgs* já eram montadas por default (register_auth_host, bloco if (mountOrgs), controller capability-probed) — recebia a tela respondendo 403 em vez de funcionar, sem nenhum erro de boot. O authkit:doctor avisava, mas só para quem o rodasse.

    Agora organizationModels é opcional:

    • Ausente (novo default) → usa os models default da lib (defaultOrganizationModels, as três tabelas lib-owned criadas pelo ensureAuthkitSchema). A capability existe; a mesma tela que já estava montada passa a funcionar.
    • true → idêntico ao default. É o atalho explícito que apps existentes já usam e continua valendo.
    • { OrgModel, MemberModel, InvitationModel } → escape hatch inalterado, para quem guarda as tabelas de auth numa conexão/schema próprios.
    • false (novo opt-out explícito) → capability ausente. Preserva o comportamento antigo para quem hoje depende de supportsOrganizations === false.

    Como migrar: se você dependia da ausência da capability sem declarar nada, passe organizationModels: false. Se quer conexão/schema próprios, continue passando o trio. Caso contrário, nada a fazer — a feature que você já habilitava agora funciona.

    O authkit:doctor (checkOrganizations) acompanha: o warn de "enabled=true sem capability" passa a ser inalcançável no caminho default e menciona o false; a mensagem de ok diz de onde vêm os models (default da lib ou trio explícito). O lucidStores também aceita organizations: false para o mesmo opt-out.

    Cobertura em tests/organizations/organizations_store.spec.ts: lucidAccountStore(Model) sem options liga a capability e roda CRUD de ponta a ponta; organizationModels: false desliga mesmo com as tabelas presentes; true e o trio explícito seguem iguais; e o lucidStores cobre default + opt-out. O teste "sem tabelas org → supportsOrganizations false" foi removido porque a premissa dele era exatamente o contrato que deixou de valer.

@adonis-agora/authkit-server@0.67.0

Choose a tag to compare

@github-actions github-actions released this 17 Sep 01:03
1aafb02

Minor Changes

  • #200 f250d6e Thanks @DavideCarvalho! - Garante a coluna login_methods na tabela da conta em vez de assumir users, e faz o boot avisar quando não deu para garantir.

    O ensureAuthkitSchema adicionava a coluna numa tabela de nome fixo users. Como o nome da tabela da conta é decisão do host, isso errava nos dois sentidos: com a conta em auth_users (o nome que o próprio scaffold da lib usa) e uma tabela users qualquer no banco — o starter do AdonisJS cria uma — o ALTER acertava a tabela errada e passava; sem nenhuma users, nada acontecia. Nos dois casos a LoginMethodsPreferenceCapability ficava sem a coluna, sem erro e sem aviso.

    Agora o lucidAccountStore expõe accountTable (de Model.table e, se o model ainda não bootou, da naming strategy — a mesma função que o Lucid usa), o provider repassa em EnsureSchemaOptions.accountTable, e o ensure usa esse nome. Store próprio que não exponha o metadado continua no users de antes, então nada muda para quem já estava certo.

    O EnsureSchemaReport passa a trazer loginMethods: { table, ensured }: quando a tabela da conta não existe, o provider loga um warning — antes o sintoma só aparecia longe da causa, como login/callback OIDC quebrado por "column ...login_methods does not exist".

    Cobertura em tests/schema/ensure_schema.spec.ts: o caso da tabela homônima (a coluna vai para auth_users e não para users), o back-compat sem accountTable, o ensured: false e a derivação do nome pelo store. Reverter só o ensure.ts derruba três desses casos.

    E o catch do bloco não mente mais: o re-probe passou a ser da coluna, não da tabela. Antes, um ALTER que falhasse (permissão, lock, DDL) virava ensured: true — o report dizia sucesso com a coluna ausente e o boot não avisava nada, que é exatamente o silêncio que este bloco existe para acabar. Agora só o caso de corrida (a coluna já existe porque outra instância a criou) é engolido; qualquer outra falha propaga para o provider, que já sabe degradar logando warning. Dois testes cobrem os dois lados.

  • #200 f250d6e Thanks @DavideCarvalho! - organizationModels: true passa a usar models default das tabelas de organizations — sem o host precisar escrever model nenhum.

    As três tabelas (auth_organizations, auth_organization_members, auth_organization_invitations) são lib-owned: quem as cria e evolui é o ensureAuthkitSchema. Ainda assim, para ligar a OrganizationsCapability cada host tinha que transcrever à mão os models que espelham essas colunas. Isso rendia duas coisas ruins: boilerplate sem nenhuma decisão do host, e drift silencioso — uma coluna nova chega pelo autoManage e o model escrito à mão no host não sabe dela, sem erro nenhum. Foi o que aconteceu no próprio fixture de teste deste pacote: ele criava auth_organization_members sem updated_at, que existe na tabela real.

    Agora lucidAccountStore(Model, { organizationModels: true }) usa os models default da lib (exportados como defaultOrganizationModels, junto de AuthOrganization, AuthOrganizationMember e AuthOrganizationInvitation). O caminho explícito { OrgModel, MemberModel, InvitationModel } continua valendo como escape hatch — é para quem guarda as tabelas de auth numa conexão/schema próprios, que os defaults não declaram (static connection).

    A mensagem do authkit:doctor para o caso "organizations.enabled: true sem capability" passa a apontar o organizationModels: true primeiro.

    Cobertura em tests/organizations/organizations_store.spec.ts: organizationModels: true liga a capability e roda createOrg/findOrgBySlug/getOrgMembership/listOrgsForAccount de ponta a ponta contra as tabelas reais; o escape hatch explícito continua funcionando; e ficou registrado o comportamento de desenho — sem as tabelas, a capability está ligada e o erro é alto no uso (barulhento de propósito: o silêncio era o problema).

    O mesmo vale no caminho do lucidStores, que tem tipo próprio: organizations: true também é aceito lá — sem isso o atalho não chegava no wiring "declarado uma vez", que é o recomendado em app maior.

    A documentação foi atualizada junto (organizations.mdx, account-store.mdx): o true aparece como caminho recomendado, o objeto explícito fica como escape hatch, e a linha do enabled deixou de dizer que ele "detecta tabelas" — ele é sinal de intenção para o authkit:doctor, e quem monta as rotas é a capability.

Patch Changes

  • #200 f250d6e Thanks @DavideCarvalho! - Conserta o node ace configure, que quebrava em todos os presets, e o config/authkit.ts ejetado pelo preset React, que vinha com a API de identidade antiga.

    O configure gerava os arquivos com codemods.makeUsingStub, e o gerador de stubs compila o .stub como template delimitado por crase. Qualquer crase dentro do stub fecha o template mais cedo e o texto seguinte passa a ser parseado como JavaScript — daí os SyntaxError que apareciam com o nome de um identificador qualquer do comentário (Unexpected identifier 'views' no stub do React, Unexpected identifier 'id' no do model, Invalid or unexpected token no da migration). Só stubs/config/authkit.stub compilava, e é justamente o único sem crase: era ele que fazia o --ui=edge avançar até o stub da migration antes de estourar.

    Isso derrubava o caminho documentado de instalação (pnpm addnode ace configure --ui=...) para todo consumidor, que passava a ter que reconstruir os arquivos ejetados à mão a partir de stubs/.

    As crases saíram dos comentários dos três stubs afetados. Junto, o stubs/config/authkit_react.stub deixou de declarar findAccount/verifyCredentials: essas chaves não existem no AuthServerConfigInput (que aceita accountStore) e a lib as deriva do store — o preset React ejetava um config sem accountStore, ou seja, sem verificação de credenciais e sem o resto do store (MFA, capabilities). Agora usa o mesmo accountStore: lucidAccountStore(AuthUser) do stub sem preset.

    A lacuna que deixou isso passar foi de teste: nenhum spec chamava o makeUsingStub. O e2e da migration lê o stub direto e remove o frontmatter {{{ ... }}}, então o stub nunca era compilado como template. Entra tests/stubs_compile.spec.ts, que compila todos os .stub do build pelo mesmo mecanismo e falha listando os que quebram — com um caso separado para a crase, que é a causa raiz.