Skip to content

Commit 9086ec2

Browse files
authored
Bitwarden import (#347)
1 parent bf826d1 commit 9086ec2

File tree

10 files changed

+139
-11
lines changed

10 files changed

+139
-11
lines changed

interface/utils/language/de.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,13 @@ export const localeDE: typeof localeEN = {
116116
authmeTutorial: ["Gehen Sie in Authme zur Exportseite und wählen Sie „Authme-Datei exportieren“.", "Sie können diese Datei in Authme auf der Importseite importieren."],
117117
aegisTutorial: ["Tippen Sie auf das Hamburger-Menü in der oberen rechten Ecke des Bildschirms: Einstellungen > Importieren & Exportieren > Exportieren", "Wählen Sie das Aegis-JSON-Format aus, deaktivieren Sie das Kontrollkästchen „Verschlüsselung“ und exportieren Sie Ihren Tresor.", "Übertragen Sie die Datei auf Ihren Computer und importieren Sie sie in Authme."],
118118
twoFasTutorial: ["Tippen Sie auf Einstellungen in der Navigationsleiste: 2FAS-Backup > In Datei exportieren", "Aktivieren Sie das Kontrollkästchen „Exportdatei ohne Passwort“ und tippen Sie auf „Exportieren“.", "Übertragen Sie die Datei auf Ihren Computer und importieren Sie sie in Authme."],
119+
bitwardenTutorial: ["In Bitwarden go to the Settings page > Vault > Export vault", "Select the .json format (not encrypted) and click Export vault", "Transfer the file on your computer and import it in Authme"],
120+
// bitwarden
121+
bitwardenAuth: "Bitwarden",
122+
bitwardenAuthText: "Import all codes from your Bitwarden vault.",
123+
bitwardenFile: "Bitwarden vault export (without encryption)",
124+
bitwardenFileText: "Import all codes from an existing Bitwarden vault file without a encryption.",
125+
bitwardenFileButton: "Choose file",
119126
},
120127

121128
export: {

interface/utils/language/en.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,13 @@ export const localeEN = {
114114
authmeTutorial: ["In Authme go to the Export page and choose Export Authme file", "You can import this file in Authme on the Import page"],
115115
aegisTutorial: ["Tap on the hamburger menu in the rop right corner of the screen: Settings > Import & Export > Export", "Select the Aegis JSON format and uncheck the Encryption checkbox and export you vault", "Transfer the file on your computer and import it in Authme"],
116116
twoFasTutorial: ["Tap on settings on the navigation bar: 2FAS Backup > Export to file", "Check the export file without password checkbox and tap export", "Transfer the file on your computer and import it in Authme"],
117+
bitwardenTutorial: ["In Bitwarden go to the Settings page > Vault > Export vault", "Select the .json format (not encrypted) and click Export vault", "Transfer the file on your computer and import it in Authme"],
118+
// bitwarden
119+
bitwardenAuth: "Bitwarden",
120+
bitwardenAuthText: "Import all codes from your Bitwarden vault.",
121+
bitwardenFile: "Bitwarden vault export (without encryption)",
122+
bitwardenFileText: "Import all codes from an existing Bitwarden vault file without a encryption.",
123+
bitwardenFileButton: "Choose file",
117124
},
118125

119126
export: {

interface/utils/language/es.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,13 @@ export const localeES: typeof localeEN = {
116116
authmeTutorial: ["In Authme go to the Export page and choose Export Authme file", "You can import this file in Authme on the Import page"],
117117
aegisTutorial: ["Tap on the hamburger menu in the rop right corner of the screen: Settings > Import & Export > Export", "Select the Aegis JSON format and uncheck the Encryption checkbox and export you vault", "Transfer the file on your computer and import it in Authme"],
118118
twoFasTutorial: ["Tap on settings on the navigation bar: 2FAS Backup > Export to file", "Check the export file without password checkbox and tap export", "Transfer the file on your computer and import it in Authme"],
119+
bitwardenTutorial: ["In Bitwarden go to the Settings page > Vault > Export vault", "Select the .json format (not encrypted) and click Export vault", "Transfer the file on your computer and import it in Authme"],
120+
// bitwarden
121+
bitwardenAuth: "Bitwarden",
122+
bitwardenAuthText: "Import all codes from your Bitwarden vault.",
123+
bitwardenFile: "Bitwarden vault export (without encryption)",
124+
bitwardenFileText: "Import all codes from an existing Bitwarden vault file without a encryption.",
125+
bitwardenFileButton: "Choose file",
119126
},
120127

121128
export: {

interface/utils/language/fr.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,13 @@ export const localeFR: typeof localeEN = {
116116
authmeTutorial: ["Dans Authme, allez sur la page Exporter et choisissez Exporter le fichier Authme", "Vous pouvez importer ce fichier dans Authme sur la page Importer"],
117117
aegisTutorial: ["Appuyez sur le menu hamburger dans le coin supérieur droit de l'écran : Paramètres > Importer et exporter > Exporter", "Sélectionnez le format Aegis JSON et décochez la case Cryptage et exportez votre coffre-fort", "Transférez le fichier sur votre ordinateur et importez-le dans Authme"],
118118
twoFasTutorial: ["Appuyez sur les paramètres dans la barre de navigation : 2FAS Backup > Export to file", "Cochez la case d'exportation du fichier sans mot de passe et appuyez sur exporter", "Transférez le fichier sur votre ordinateur et importez-le dans Authme"],
119+
bitwardenTutorial: ["In Bitwarden go to the Settings page > Vault > Export vault", "Select the .json format (not encrypted) and click Export vault", "Transfer the file on your computer and import it in Authme"],
120+
// bitwarden
121+
bitwardenAuth: "Bitwarden",
122+
bitwardenAuthText: "Import all codes from your Bitwarden vault.",
123+
bitwardenFile: "Bitwarden vault export (without encryption)",
124+
bitwardenFileText: "Import all codes from an existing Bitwarden vault file without a encryption.",
125+
bitwardenFileButton: "Choose file",
119126
},
120127

121128
export: {

interface/utils/language/hu.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,13 @@ export const localeHU: typeof localeEN = {
116116
authmeTutorial: ["In Authme go to the Export page and choose Export Authme file", "You can import this file in Authme on the Import page"],
117117
aegisTutorial: ["Tap on the hamburger menu in the rop right corner of the screen: Settings > Import & Export > Export", "Select the Aegis JSON format and uncheck the Encryption checkbox and export you vault", "Transfer the file on your computer and import it in Authme"],
118118
twoFasTutorial: ["Tap on settings on the navigation bar: 2FAS Backup > Export to file", "Check the export file without password checkbox and tap export", "Transfer the file on your computer and import it in Authme"],
119+
bitwardenTutorial: ["In Bitwarden go to the Settings page > Vault > Export vault", "Select the .json format (not encrypted) and click Export vault", "Transfer the file on your computer and import it in Authme"],
120+
// bitwarden
121+
bitwardenAuth: "Bitwarden",
122+
bitwardenAuthText: "Import all codes from your Bitwarden vault.",
123+
bitwardenFile: "Bitwarden vault export (without encryption)",
124+
bitwardenFileText: "Import all codes from an existing Bitwarden vault file without a encryption.",
125+
bitwardenFileButton: "Choose file",
119126
},
120127

121128
export: {

interface/utils/language/pl.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,13 @@ export const localePL: typeof localeEN = {
107107
authmeTutorial: ["W Authme przejdź do strony Eksport i wybierz Eksportuj plik Authme", "Możesz zaimportować ten plik w Authme na stronie Import"],
108108
aegisTutorial: ["Stuknij w ikonę hamburgera w prawym górnym rogu ekranu: Ustawienia > Import i eksport > Eksport", "Wybierz format JSON Aegis i odznacz pole Szyfrowanie, a następnie wyeksportuj swój sejf", "Przenieś plik na komputer i zaimportuj go do Authme"],
109109
twoFasTutorial: ["Stuknij w ustawienia na pasku nawigacyjnym: Kopia zapasowa 2FAS > Eksportuj do pliku", "Zaznacz pole Eksportuj plik bez hasła i stuknij Eksportuj", "Przenieś plik na komputer i zaimportuj go do Authme"],
110+
bitwardenTutorial: ["In Bitwarden go to the Settings page > Vault > Export vault", "Select the .json format (not encrypted) and click Export vault", "Transfer the file on your computer and import it in Authme"],
111+
// bitwarden
112+
bitwardenAuth: "Bitwarden",
113+
bitwardenAuthText: "Import all codes from your Bitwarden vault.",
114+
bitwardenFile: "Bitwarden vault export (without encryption)",
115+
bitwardenFileText: "Import all codes from an existing Bitwarden vault file without a encryption.",
116+
bitwardenFileButton: "Choose file",
110117
},
111118
export: {
112119
exportCodes: "Eksportuj kody",

interface/utils/language/ru.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,13 @@ export const localeRU: typeof localeEN = {
116116
authmeTutorial: ["В Authme перейдите на страницу Экспорт и выберите Экспорт файла Authme", "Вы можете импортировать этот файл в Authme на странице Импорт"],
117117
aegisTutorial: ["Нажмите на гамбургер-меню в правом верхнем углу экрана: Настройки > Импорт и экспорт > Экспорт", "Выберите формат Aegis JSON, снимите флажок 'Шифрование' и экспортируйте хранилище", "Перенесите файл на компьютер и импортируйте его в Authme"],
118118
twoFasTutorial: ["Нажмите на настройки на панели навигации: 2FAS Backup > Экспорт в файл", "Установите флажок экспорта файла без пароля и нажмите экспорт", "Перенесите файл на компьютер и импортируйте его в Authme"],
119+
bitwardenTutorial: ["In Bitwarden go to the Settings page > Vault > Export vault", "Select the .json format (not encrypted) and click Export vault", "Transfer the file on your computer and import it in Authme"],
120+
// bitwarden
121+
bitwardenAuth: "Bitwarden",
122+
bitwardenAuthText: "Import all codes from your Bitwarden vault.",
123+
bitwardenFile: "Bitwarden vault export (without encryption)",
124+
bitwardenFileText: "Import all codes from an existing Bitwarden vault file without a encryption.",
125+
bitwardenFileButton: "Choose file",
119126
},
120127

121128
export: {

interface/utils/language/zh.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,13 @@ export const localeZH: typeof localeEN = {
116116
authmeTutorial: ["在 Authme 中,转到“导出”页面,然后选择“导出 Authme 文件”,“您可以在 Authme 的导入页面上再次导入此文件"],
117117
aegisTutorial: ["点击屏幕右上角的菜单: 设置 -> 导入与导出 -> 导出", "选择 Aegis JSON 格式并且取消选中加密复选框后导出", "将导出后的文件发送到电脑并从 Authme 导入"],
118118
twoFasTutorial: ["点击导航栏上的菜单: 2FAS 备份 -> 导出到文件", "选择导出文件并取消密码保护后导出", "将导出后的文件发送到电脑并从 Authme 导入"],
119+
bitwardenTutorial: ["In Bitwarden go to the Settings page > Vault > Export vault", "Select the .json format (not encrypted) and click Export vault", "Transfer the file on your computer and import it in Authme"],
120+
// bitwarden
121+
bitwardenAuth: "Bitwarden",
122+
bitwardenAuthText: "Import all codes from your Bitwarden vault.",
123+
bitwardenFile: "Bitwarden vault export (without encryption)",
124+
bitwardenFileText: "Import all codes from an existing Bitwarden vault file without a encryption.",
125+
bitwardenFileButton: "Choose file",
119126
},
120127

121128
export: {

interface/windows/import/import.svelte

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,35 @@
115115
</div>
116116
</div>
117117

118+
<div class="transparent-800 flex w-full flex-col items-start rounded-xl p-5 text-left">
119+
<div>
120+
<h2>{language.import.authme}</h2>
121+
<h3>{language.import.authmeText}</h3>
122+
</div>
123+
<div class="mt-5 flex w-full">
124+
<button class="button" on:click={() => showTutorial("authme")}>
125+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10" /><line x1="12" y1="16" x2="12" y2="12" /><line x1="12" y1="8" x2="12.01" y2="8" /></svg>
126+
{language.import.instructions}
127+
</button>
128+
</div>
129+
<div class="mt-5 flex w-full">
130+
<Details title={language.import.chooseImportMethod}>
131+
<div class="flex flex-row justify-between">
132+
<div>
133+
<h4 class="text-white">{language.import.authmeFile}</h4>
134+
<h5>{language.import.authmeFileText}</h5>
135+
</div>
136+
<div>
137+
<button class="smallButton" on:click={chooseFile}>
138+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 22h14a2 2 0 0 0 2-2V7.5L14.5 2H6a2 2 0 0 0-2 2v3" /><polyline points="14 2 14 8 20 8" /><path d="M5 17a3 3 0 1 0 0-6 3 3 0 0 0 0 6z" /><path d="m9 18-1.5-1.5" /></svg>
139+
{language.import.authmeFileButton}
140+
</button>
141+
</div>
142+
</div>
143+
</Details>
144+
</div>
145+
</div>
146+
118147
<div class="transparent-800 flex w-full flex-col items-start rounded-xl p-5 text-left">
119148
<div>
120149
<h2>{language.import.aegisAuth}</h2>
@@ -175,11 +204,11 @@
175204

176205
<div class="transparent-800 flex w-full flex-col items-start rounded-xl p-5 text-left">
177206
<div>
178-
<h2>{language.import.authme}</h2>
179-
<h3>{language.import.authmeText}</h3>
207+
<h2>{language.import.bitwardenAuth}</h2>
208+
<h3>{language.import.bitwardenAuthText}</h3>
180209
</div>
181210
<div class="mt-5 flex w-full">
182-
<button class="button" on:click={() => showTutorial("authme")}>
211+
<button class="button" on:click={() => showTutorial("bitwarden")}>
183212
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10" /><line x1="12" y1="16" x2="12" y2="12" /><line x1="12" y1="8" x2="12.01" y2="8" /></svg>
184213
{language.import.instructions}
185214
</button>
@@ -188,13 +217,13 @@
188217
<Details title={language.import.chooseImportMethod}>
189218
<div class="flex flex-row justify-between">
190219
<div>
191-
<h4 class="text-white">{language.import.authmeFile}</h4>
192-
<h5>{language.import.authmeFileText}</h5>
220+
<h4 class="text-white">{language.import.bitwardenFile}</h4>
221+
<h5>{language.import.bitwardenFileText}</h5>
193222
</div>
194223
<div>
195-
<button class="smallButton" on:click={chooseFile}>
224+
<button class="smallButton" on:click={bitwardenFile}>
196225
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 22h14a2 2 0 0 0 2-2V7.5L14.5 2H6a2 2 0 0 0-2 2v3" /><polyline points="14 2 14 8 20 8" /><path d="M5 17a3 3 0 1 0 0-6 3 3 0 0 0 0 6z" /><path d="m9 18-1.5-1.5" /></svg>
197-
{language.import.authmeFileButton}
226+
{language.import.bitwardenFileButton}
198227
</button>
199228
</div>
200229
</div>
@@ -277,7 +306,7 @@
277306
</dialog>
278307

279308
<script>
280-
import { aegisFile, captureScreen, chooseFile, chooseImages, manualEntry, showManualEntry, showTutorial, twoFasAuthFile, useWebcam } from "./index"
309+
import { aegisFile, bitwardenFile, captureScreen, chooseFile, chooseImages, manualEntry, showManualEntry, showTutorial, twoFasAuthFile, useWebcam } from "./index"
281310
import Details from "../../components/details.svelte"
282311
import { getLanguage } from "@utils/language"
283312

interface/windows/import/index.ts

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ export const showManualEntry = () => {
100100
/**
101101
* Show tutorial dialog
102102
*/
103-
type tutorialType = "google" | "totp" | "authme" | "aegis" | "2fas"
103+
type tutorialType = "google" | "totp" | "authme" | "aegis" | "2fas" | "bitwarden"
104104

105105
export const showTutorial = (type: tutorialType) => {
106106
const dialog: LibDialogElement = document.querySelector(".tutorialDialog")
@@ -149,6 +149,13 @@ export const showTutorial = (type: tutorialType) => {
149149
tutorialTitle.innerHTML = language.import.twoFasAuth
150150
tutorialDescription.innerHTML = language.import.twoFasAuthText
151151

152+
for (let i = 0; i < elements.length; i++) {
153+
list.innerHTML += `<li>${elements[i]}</li>`
154+
}
155+
} else if (type === "bitwarden") {
156+
const elements = language.import.bitwardenTutorial
157+
tutorialTitle.innerHTML = language.import.bitwardenAuth
158+
tutorialDescription.innerHTML = language.import.bitwardenAuthText
152159
for (let i = 0; i < elements.length; i++) {
153160
list.innerHTML += `<li>${elements[i]}</li>`
154161
}
@@ -237,8 +244,6 @@ export const twoFasAuthFile = async () => {
237244
for (let i = 0; i < file.services.length; i++) {
238245
const service = file.services[i]
239246

240-
console.log(service)
241-
242247
if (service.otp.tokenType === "TOTP") {
243248
if (service.otp.source === "Link" && service.otp.link !== undefined && service.otp.link.trim()) {
244249
importString += totpImageConverter(service.otp.link)
@@ -300,6 +305,44 @@ export const aegisFile = async () => {
300305
}
301306
}
302307

308+
/**
309+
* Import from a Bitwarden export file
310+
*/
311+
export const bitwardenFile = async () => {
312+
const filePath = await dialog.open({ filters: [{ name: "Bitwarden export file", extensions: ["json"] }] })
313+
314+
interface BitwardenFile {
315+
encrypted: boolean
316+
items: {
317+
login: {
318+
totp: string
319+
}
320+
}[]
321+
}
322+
323+
if (filePath !== null) {
324+
const loadedFile = await fs.readTextFile(filePath)
325+
const file: BitwardenFile = JSON.parse(loadedFile)
326+
let importString = ""
327+
328+
for (let i = 0; i < file.items.length; i++) {
329+
const entry = file.items[i]
330+
331+
if (entry.login.totp) {
332+
importString += totpImageConverter(entry.login.totp)
333+
}
334+
}
335+
336+
dialog.message(language.codes.dialog.codesImported)
337+
338+
const state = getState()
339+
state.importData = importString
340+
setState(state)
341+
342+
navigate("codes")
343+
}
344+
}
345+
303346
/**
304347
* Start a video capture, when a QR code detected try to read it
305348
*/

0 commit comments

Comments
 (0)