> ## Documentation Index
> Fetch the complete documentation index at: https://docs.didit.me/llms.txt
> Use this file to discover all available pages before exploring further.

# Migrar utilizadores: importar palavras-passe e MFA

> Importe com a Didit utilizadores, palavras-passe, e-mails verificados, contas sociais, aplicações de autenticação e funções via CSV, JSON ou lotes.

> [!WARNING]
>
> A funcionalidade Authentication é nova. Execute todas as importações primeiro numa aplicação sandbox e só depois importe para a sua aplicação de produção.

Uma importação transfere pessoas de outra plataforma de identidade para a Didit Authentication. A palavra-passe, o e-mail verificado, as ligações de início de sessão social, a aplicação de autenticação e as funções nas organizações são transferidos com elas. Nada muda para elas até a sua aplicação as autenticar com a Didit.

As importações usam a API de gestão de Authentication da sua aplicação, com uma sessão de consola que tenha `write:authentication` (ou `read:authentication` para as listar). Nunca utilizam um token de utilizador final nem uma chave de API de verificação. Nos exemplos, `MANAGEMENT_BASE` é:

```text theme={null}
https://apx.didit.me/auth/v2/organizations/{org_id}/applications/{application_id}/authentication/
```

## Duas formas de importar

| | Tarefa de ficheiro | Pedido em lote |
| - | - | - |
| Endpoint | `POST import-jobs/` (multipart) | `POST imports/` (JSON) |
| Tamanho | Um ficheiro CSV ou JSON, até 25 MB e 100000 pessoas | De 1 a 1000 `rows` por pedido |
| Simulação (dry run) | Executa-se sempre primeiro, em segundo plano | `dry_run: true` por predefinição |
| Confirmação | `POST import-jobs/{id}/start/` | O mesmo pedido com `dry_run: false` |
| Falha parcial | Cada lote de 1000 é gravado de forma independente; uma importação interrompida retoma | Tudo ou nada |

## Importar um ficheiro

### 1. Carregar

```bash theme={null}
curl --fail-with-body "${MANAGEMENT_BASE}import-jobs/" \
  -H "Authorization: Bearer $CONSOLE_ACCESS_TOKEN" \
  -F file=@users.csv \
  -F passwords=import \
  -F keep_social=true \
  -F keep_verified=true \
  -F keep_mfa=true \
  -F keep_ids=true
```

| Campo | Significado |
| - | - |
| `file` | CSV ou JSON, em UTF-8 (é permitida uma marca de ordem de bytes), até 25 MB. |
| `passwords` | `import` (o predefinido) mantém os hashes das palavras-passe. `reset` ignora-os, para que todos definam uma nova palavra-passe. |
| `keep_social` | Mantém as ligações de início de sessão social. |
| `keep_verified` | Mantém `email_verified`. Com `false`, todos os e-mails ficam não verificados. |
| `keep_mfa` | Mantém os segredos das aplicações de autenticação. |
| `keep_ids` | Mantém `external_id`. Com `false`, `vendor_data` passa a ser o ID de identidade da Didit. |
| `firebase_signer_key`, `firebase_salt_separator`, `firebase_rounds`, `firebase_mem_cost` | Os parâmetros de hash do seu projeto do Firebase. Envie todos os quatro ou nenhum. |

Cada opção `keep_*` é `true`, salvo se enviar `false`. A chamada devolve `202` com uma tarefa. Uma aplicação mantém no máximo 5 ficheiros carregados em simultâneo.

### 2. Verificar a simulação

Cada carregamento inicia uma simulação. Esta verifica todas as pessoas e não grava nada. Consulte periodicamente `GET import-jobs/{id}/`:

| Campo | Significado |
| - | - |
| `kind` | `dry_run`, e depois `import` quando a iniciar. |
| `status` | `queued`, `running`, depois `passed` ou `failed` numa simulação, e `done` ou `error` numa importação. |
| `processed`, `total` | Progresso, em pessoas. |
| `result` | O conteúdo do ficheiro (ver [resultados](#resultados)). |
| `startable` | `true` quando a simulação foi aprovada e o ficheiro não expirou. |
| `expires_at` | O ficheiro é eliminado 24 horas após uma simulação que nunca foi importada. |

Uma simulação falhada lista os seus erros. Corrija o ficheiro e carregue-o novamente.

### 3. Iniciar a importação

`POST import-jobs/{id}/start/` importa o que a simulação verificou e devolve `202`.

* As pessoas são importadas em lotes de 1000. Cada lote é gravado de forma independente.
* Apenas uma importação é executada de cada vez por aplicação (caso contrário, devolve `import_running`).
* Uma importação interrompida retoma a partir do último lote gravado.
* As pessoas já importadas são ignoradas, por isso pode executar novamente o mesmo ficheiro.
* A pessoa que iniciou a importação recebe um e-mail quando esta termina ou para.

O ficheiro carregado é guardado encriptado. É eliminado quando a importação termina, quando a sua simulação falha, ou 24 horas após uma simulação que nunca foi importada.

## Importar em lotes

Envie até 1000 pessoas por pedido, com uma `idempotency_key` de até 100 caracteres.

```json theme={null}
{
  "rows": [
    {
      "email": "user@example.com",
      "external_id": "user-1234",
      "email_verified": true,
      "display_name": "Example User",
      "password_hash": "$2b$12$...",
      "social": [{ "provider": "google", "subject": "1234567890" }],
      "organizations": [{ "slug": "acme", "name": "Acme", "role": "admin" }]
    }
  ],
  "dry_run": true,
  "idempotency_key": "migration-batch-0001"
}
```

* Uma simulação devolve `200` com o resultado e não grava nada.
* Com `dry_run: false`, uma importação bem-sucedida devolve `201`. Um lote com qualquer erro devolve `400` e não importa ninguém.
* Repetir um pedido já gravado com a mesma chave devolve o resultado registado. A mesma chave com conteúdo diferente devolve `400 {"idempotency_key": "different_payload"}`.
* Os parâmetros do Firebase vão num objeto `firebase`: `{signer_key, salt_separator, rounds, mem_cost}`.
* `GET imports/` lista os lotes gravados.

## Formato do ficheiro

### JSON

Uma matriz de pessoas, ou um objeto cujo `users` contém essa matriz.

| Campo | Regra |
| - | - |
| `email` | Obrigatório. |
| `external_id` | Até 255 caracteres. Passa a ser o [`vendor_data`](https://docs.didit.me/pt/entities/vendor-data-linking) da pessoa. |
| `email_verified` | `true` ou `false` (também `1`/`0`, `yes`/`no`). |
| `display_name` | Até 200 caracteres. |
| `password_hash`, `password_salt`, `password_algorithm` | Ver [palavras-passe](#palavras-passe). |
| `totp_secret` | Segredo da aplicação de autenticação em Base32. |
| `created_at`, `last_signed_in_at` | ISO 8601. Uma hora sem fuso horário é considerada UTC. |
| `social` | Até 8 `{provider, subject, tenant_id}`. `provider` é `google`, `apple`, `github` ou `microsoft`. `tenant_id` é obrigatório apenas para a Microsoft. |
| `organizations` | Até 20 `{slug, name, role}`. `role` é `admin`, `member` (o predefinido) ou `billing`. |

### CSV

As mesmas colunas de valor único, mais `google_id`, `apple_id`, `github_id`, `microsoft_id`, `microsoft_tenant_id`, `organization_slug`, `organization_name` e `organization_role`. Uma pessoa em várias organizações ocupa uma linha por organização.

```csv theme={null}
email,external_id,email_verified,password_hash,google_id,organization_slug,organization_role
user@example.com,user-1234,true,$2b$12$...,1234567890,acme,admin
```

Campos ou colunas desconhecidos são erros e nunca são ignorados.

### Duplicados e contas existentes

* As linhas com o mesmo e-mail tornam-se uma só pessoa. Uma linha idêntica é contada como duplicado. Uma linha com mais ligações, organizações ou uma aplicação de autenticação é fundida com a pessoa.
* Dois hashes de palavras-passe diferentes, dois segredos de autenticação diferentes ou duas funções diferentes nas organizações para a mesma pessoa são erros. Também é erro um `external_id` ou um identificador de fornecedor reivindicado por duas pessoas.
* Se já existir uma conta com esse e-mail e uma importação anterior a tiver criado, a pessoa é ignorada (`already_imported`). Se a pessoa se tiver registado por conta própria, é um erro `identity_conflict`. Uma importação nunca altera uma conta existente.

## Palavras-passe

| Formato | Aceite |
| - | - |
| bcrypt | `$2a$`, `$2b$` ou `$2y$`, custo de 4 a 13. |
| Argon2id | `$argon2id$v=19$...`. Até 64 MiB de memória, memória multiplicada pelas passagens até 256 MiB, até 8 canais. |
| Django PBKDF2-SHA256 | `pbkdf2_sha256$...`, de 10 000 a 2 000 000 iterações. |
| Django scrypt | `scrypt$...`, fator de trabalho até 32 768, tamanho de bloco de 1 a 8, paralelismo de 1 a 5. |
| Firebase scrypt | `password_algorithm: "firebase_scrypt"`, `password_hash` e `password_salt` em base64 por pessoa, além de `signer_key`, `salt_separator`, `rounds` (de 1 a 8) e `mem_cost` (de 1 a 14) do seu projeto, uma vez por importação. |

Os parâmetros do Firebase são guardados encriptados, apenas para esta aplicação. Qualquer outro formato, ou um custo acima destes limites, significa que a pessoa precisa de uma nova palavra-passe. Depois do primeiro início de sessão bem-sucedido de uma pessoa, a Didit volta a guardar a palavra-passe dela no seu próprio formato.

### Pessoas sem palavra-passe utilizável

Quando uma pessoa não tem hash, ou tem um hash não suportado, uma tentativa de início de sessão com palavra-passe recebe a resposta normal de palavra-passe incorreta. A Didit também lhe envia por e-mail uma ligação para definir uma nova palavra-passe (válida durante 30 minutos, no máximo uma vez por dia). Isto acontece apenas quando o e-mail da pessoa está verificado, a conta não está bloqueada e o início de sessão com palavra-passe está ativado.

No resultado, `reset_required` conta as pessoas sem palavra-passe utilizável. `reset_emails` conta, entre elas, as que têm um e-mail verificado: só estas podem ser contactadas desta forma. As pessoas com um e-mail não verificado não podem iniciar sessão com palavra-passe. Podem continuar a iniciar sessão com um código enviado por e-mail, se ativar esse método.

## Ligações sociais, aplicações de autenticação e organizações

* **Ligações sociais** são mantidas para os fornecedores que a sua aplicação já configurou, com o ID de cliente atual. Um início de sessão posterior não precisa então de prova de e-mail. Os identificadores do Google e do GitHub são globais. Os identificadores da Apple são por equipa de programadores e os da Microsoft são por registo de aplicação, pelo que essas ligações só correspondem se a sua plataforma antiga usava a mesma equipa ou o mesmo registo. As ligações a fornecedores que não configurou são contadas como `social_unavailable` e não são importadas.
* **Aplicações de autenticação** (segredos TOTP: SHA-1, 6 dígitos, 30 segundos) são guardadas encriptadas. As pessoas importadas têm de as usar como segundo fator. Não recebem códigos de recuperação.
* **Organizações** que ainda não existem são criadas a partir do respetivo slug.
* **Chaves de acesso** não podem ser importadas. Pertencem ao seu antigo domínio de início de sessão, pelo que as pessoas adicionam uma nova chave de acesso depois de iniciarem sessão com a Didit.

## Resultados

Cada simulação, lote e tarefa devolve o mesmo `result`:

| Campo | Significado |
| - | - |
| `rows`, `people_total` | Linhas lidas e pessoas distintas. |
| `ready`, `already_imported`, `duplicates`, `merged`, `skipped` | O que acontece a cada pessoa. |
| `passwords` | Contagens por formato: `bcrypt`, `argon2id`, `firebase_scrypt`, `pbkdf2_sha256`, `scrypt`. |
| `reset_required`, `reset_emails`, `unsupported_hashes` | Pessoas que precisam de uma nova palavra-passe. |
| `verified_emails` | Pessoas importadas com um e-mail verificado. |
| `social_links`, `social_unavailable` | Ligações mantidas por fornecedor, e ligações ignoradas. |
| `authenticator_apps` | Pessoas com uma aplicação de autenticação importada. |
| `organizations` | `count`, `members` e até cinco `names`. |
| `error_count`, `errors` | Até 100 erros, cada um com um `row` indexado a partir de zero (linha do CSV `row + 2`), um `field` e um código `error`. |

As respostas nunca contêm hashes de palavras-passe, salts, segredos de aplicações de autenticação nem valores de campos.

| Erro | Significado |
| - | - |
| `file_too_large`, `too_many_rows`, `empty_file` | Mais de 25 MB, mais de 100 000 pessoas, ou ficheiro vazio. |
| `invalid_encoding`, `invalid_json`, `invalid_csv` | O ficheiro não pode ser lido. |
| `unknown_column`, `missing_column`, `duplicate_column`, `unknown_field` | O ficheiro não corresponde ao formato. |
| `required`, `invalid_field`, `invalid_row` | Um valor está em falta ou é inválido. |
| `identity_conflict` | Uma conta com esse e-mail foi criada por iniciativa própria. |
| `social_link_conflict`, `password_conflict`, `authenticator_conflict`, `role_conflict` | Duas linhas da mesma pessoa não coincidem. |
| `too_many_uploads`, `import_running`, `not_startable`, `file_expired` | A tarefa não pode começar agora. |

## Antes de fazer a transição

Teste na sua aplicação de sandbox:

* o início de sessão com cada formato de palavra-passe que importar,
* o e-mail para definir uma nova palavra-passe de uma pessoa sem hash utilizável,
* o início de sessão social e os códigos das aplicações de autenticação das pessoas importadas,
* a execução do mesmo ficheiro duas vezes.

Depois, verifique se o `vendor_data` de cada pessoa corresponde ao valor que usa para as suas [verificações e transações](https://docs.didit.me/pt/authentication/overview.md#manter-uma-pessoa-em-todos-os-produtos).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.