Skip to content

feat(auth): conclui o login por conexao de identidade (Sankhya Pass) a partir do codigo no fragmento (#189) - #190

Open
BrunoPinaFelipe wants to merge 1 commit into
mainfrom
feat/189-identity-connection-signin
Open

BrunoPinaFelipe wants to merge 1 commit into
mainfrom
feat/189-identity-connection-signin

Conversation

@BrunoPinaFelipe

Copy link
Copy Markdown
Contributor

Closes #189

O que muda

Login por conexão de identidade, o primeiro caso é o Sankhya Pass, para o app aberto dentro do iframe do SKW (o ERP web da Sankhya). A página de ponte do mitra-web (mitra-web#1145) faz o login no IAM (mitra-iam#672) e redireciona para a origem do app com um código de uso único no fragmento; o SDK troca esse código pela sessão.

  • src/modules/identity-connection-flow.ts (novo): lê codeMitra, stateMitra (prefixo connection.) e connectionMitra do fragmento; fragmento de outro fluxo fica intacto; dali em diante a URL é limpa antes de qualquer erro; só completa dentro de iframe; POST /api/v1/auth/identity-connections/{slug}/exchange {appId, code} com expectAuthTokenResponse (allTokens preservado).
  • auth.completeIdentityConnectionSignInRedirect(); o init() chama a conclusão depois do /info, com single-flight (boot duplo do StrictMode troca o código uma vez só). Erro da troca rejeita o init() e limpa a sessão em memória que o startup tinha restaurado, sem tocar no storage.
  • Sessão desse caminho guardada por aba (sessionStorage), porque duas bases demo da Sankhya no mesmo browser misturavam alias no legado. Regras: a sessão da aba tem precedência na leitura; gravar por aba não apaga a sessão compartilhada; gravar no localStorage (Google, e-mail, setSession, setToken) apaga a cópia da aba; signOut por aba limpa só a aba.
  • Ponte com o SDK legado: numa sessão por aba o legado recebe só o access token, porque ele persiste refresh token em localStorage['mitra-session']; o token renovado chega pelo onSessionChange. Os exports legados não renovam sozinhos nesse caso (a renovação do SDK é lazy).
  • Release 1.9.0-beta.0: package.json, lock, CHANGELOG e smoke-package.mjs.

Fora deste PR

Checar ancestorOrigins contra as allowedOrigins da conexão (follow-up de segurança); botão "Entrar com Sankhya Pass" fora do SKW.

Prova

npm run check local: lint, typecheck, 484 testes, build, pack:check e smoke, cobertura 97,2% de linhas e 93,6% de branches. Auditor isolado da skill feature-implementation em quatro iterações: 8,4, 9,4 e 9,6. A prova com token real no dev depende do IAM e da ponte.

Ordem

Release beta depois do merge (release.yml com 1.9.0-beta.0); o Governador sobe a versão e faz rebuild. Estável só depois da alpha.

🤖 Generated with Claude Code

…a partir do codigo no fragmento (#189)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@BrunoPinaFelipe
BrunoPinaFelipe requested a review from a team as a code owner October 28, 2026 18:05
// whoever obtained it.
if (browserWindow.self === browserWindow.top) {
throw new Error('Identity connection sign-in is only accepted inside the host system iframe.');
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

major O retorno é aceito apenas por stateMitra com prefixo connection. e por estar em iframe (self !== top). Isso não vincula o código ao browser/origem que iniciou a conexão: uma página de terceiro pode embutir o app com código de outra conta e induzir o usuário a concluir a troca, formando login CSRF. A RFC #979 exige vínculo a app, origem e transação. Antes do POST, valide a origem ancestral contra allowedOrigins (falhando fechado quando não verificável) ou use protocolo equivalente; cubra origem permitida e negada e prove que a negada não dispara POST.

Comment thread src/modules/auth.ts
// A token set by hand is a new session, not the identity connection's one,
// and the tab copy of that one would come back on the next reload.
this.sessionScoped = false;
removeItem(sessionStorageOrNull(), this.storageKey);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

major setToken() troca sessionScoped para false, mas retém #refreshToken da sessão por aba anterior. saveToStorage() grava access e refresh no destino escolhido; a ponte só suprime o refresh enquanto a sessão continua restrita à aba e, fora disso, o entrega ao SDK legado. Assim, setToken() após o login por conexão pode tornar compartilhável o refresh antigo e fazer uma renovação voltar à identidade anterior. Preserve o escopo ao atualizar o mesmo par ou descarte o refresh antigo antes de ampliá-lo; teste saveToStorage em ambas as variantes e createClient com a ponte real, confirmando que o refresh antigo não chega a localStorage nem a mitra-session. Veja também a gravação conjunta de access e refresh.

@gandalf-the-grey-bot

Copy link
Copy Markdown

Gandalf, análise 1 · commit 6648724

Pega-rápido: A SDK passa a concluir login por conexão de identidade dentro de um iframe, atendendo a um uso concreto do Governador. O guard atual confirma que está em um iframe, mas não prova que o browser que recebeu o retorno iniciou a conexão.

Insights: O mérito passa: o módulo reutiliza os fluxos de autenticação existentes. O caminho acrescenta troca e leitura de sessão apenas quando reconhece o retorno, sem recurso persistente ou crescimento contínuo. Permanecem dois riscos de fronteira: procedência do retorno e isolamento do refresh token por aba.

Forma da implementação: O módulo próprio e o init() idempotente com single-flight têm escopo adequado. O CI Build & Test no commit revisado terminou verde; os testes existentes, porém, não cobrem os dois cenários abaixo.

Findings:

Comentários e testes: As duas provas pedidas cabem nos testes da SDK: rejeitar origem não permitida antes da troca e garantir que o refresh antigo não chega ao armazenamento compartilhado nem ao SDK legado.

Confiança: Moderada-alta na análise estática: a cadeia de vazamento do refresh é verificável; a falta de vínculo de origem também está no fluxo. A integração específica com o produtor IAM ainda não foi confirmada.

Ativação: Mesmo após os ajustes, aguardar o endpoint IAM e a rota/CORS do gateway, a ponte web e a versão do consumidor estarem compatíveis. Merge deste PR não publica o pacote beta; a publicação é manual.

Sugestão de aprendizado: Em mudanças de escopo de sessão, conferir todas as credenciais retidas e o efeito pela ponte real.

Decisão: não aprovaria até os dois ajustes major serem resolvidos.

Não consegui confirmar o contrato do endpoint de troca: a implementação correspondente ainda não aparece no IAM.

Aqui o meu check é obrigatório: depois de um push, comente /gandalf para eu revisar o commit novo e liberar o merge.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Concluir o login por conexao de identidade (Sankhya Pass) a partir do codigo no fragmento

2 participants