Created
March 1, 2025 12:37
-
-
Save mdoering/4d6712871a807eaad8ce2225526c72db to your computer and use it in GitHub Desktop.
1Password importer script for unencrypted Bitwarden JSON exports
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python3 | |
import json, asyncio, os, re, sys | |
from onepassword.client import Client | |
from onepassword import * | |
VAULT_ID="XXX" # your 1Password vault identifier to import to | |
FILE="bitwarden.json" # local bitwarden json export filename, unencrypted | |
skip=0 # 1Password has rate limiting. You can call this script several times and skip the ones you already imported before | |
counter=0 | |
def appendCardField(fields, card, id, title, key, type): | |
if card[key]: | |
fields.append( ItemField(id=id, title=title, value=card[key], field_type=type, section_id="") ) | |
# I had many fields in Bitwarden taken from some registration forms which were just noise. This ignore the bulk of them | |
def isGoodField(key, val): | |
if val in [None, "✓","×","Senden","Registrieren","Anmelden"]: | |
return False | |
if key in ["remember_me","no_name","os_cookie","OptinAgb","OptinDs","wp-submit","guest-account","submit","shippingTo","describe_yourself","regtype","interested_in"]: | |
return False | |
for pre in ["form[","default_options[","signup_form[","register[","orderhead[","invadr[","default_address[","data[","customer.","address.","address[","dnn$ctr31","userfield[","textButton","registration["]: | |
if key.startswith(pre): | |
return False | |
return True | |
async def importItem(client, obj): | |
global counter | |
oid=obj['id'] | |
title=obj['name'] | |
notes=obj['notes'] | |
# not imported | |
fav=obj['favorite'] | |
modified=obj['revisionDate'] | |
sites=[] | |
fields=[] | |
sections=[ItemSection(id="", title="")] | |
counter+=1 | |
if (counter == skip+1): | |
print('\n +++ Start adding from here!\n') | |
print(counter, title) | |
if (counter <= skip): | |
return | |
if 'login' in obj: | |
login=obj['login'] | |
sites=[Website(label="Website",url=x["uri"],autofill_behavior=AutofillBehavior.ANYWHEREONWEBSITE) for x in login['uris'] if 'uri' in x] | |
if login['username']: | |
fields.append( ItemField(id='username', title='username', value=login['username'], field_type=ItemFieldType.TEXT) ) | |
if login['password']: | |
fields.append( ItemField(id='password', title='password', value=login['password'], field_type=ItemFieldType.CONCEALED) ) | |
if 'card' in obj: | |
card=obj['card'] | |
appendCardField(fields, card, id='cardholder', title='Cardholder Name', key='cardholderName', type=ItemFieldType.TEXT) | |
appendCardField(fields, card, id='type', title='Type', key='brand', type=ItemFieldType.CREDITCARDTYPE) | |
appendCardField(fields, card, id='ccnum', title='Number', key='number', type=ItemFieldType.CREDITCARDNUMBER) | |
appendCardField(fields, card, id='cvv', title='Verification Number', key='code', type=ItemFieldType.CONCEALED) | |
if card['expMonth'] and card['expYear']: | |
fields.append( ItemField(id='expiry', title='Expiry Date', value=card['expMonth']+" / "+card['expYear'], section_id="", field_type=ItemFieldType.TEXT) ) | |
if 'fields' in obj: | |
sections.append( ItemSection(id="other", title="Other") ) | |
fields.extend( [ItemField(id="field"+str(i), title=x["name"], value=x["value"], section_id="other", field_type=ItemFieldType.TEXT) for i,x in enumerate(obj['fields'], 1) if isGoodField(x["name"], x["value"])] ) | |
match obj['type']: | |
case 1: | |
category = ItemCategory.LOGIN | |
case 2: | |
category = ItemCategory.SECURENOTE | |
case 3: | |
category = ItemCategory.CREDITCARD | |
case _: | |
raise Exception("Unknown bitwarden category: " + str(obj['type'])) | |
to_create = ItemCreateParams( | |
title=title, | |
category=category, | |
vault_id=VAULT_ID, | |
fields=fields, | |
sections=sections, | |
notes=notes, | |
websites=sites, | |
) | |
created_item = await client.items.create(to_create) | |
async def main(): | |
# Gets your service account token from the OP_SERVICE_ACCOUNT_TOKEN environment variable. | |
token = os.getenv("OP_SERVICE_ACCOUNT_TOKEN") | |
# Connects to 1Password. Fill in your own integration name and version. | |
client = await Client.authenticate(auth=token, integration_name="Bitwarden Importer", integration_version="v1.0.0") | |
print("\n +++ Read Bitwarden JSON from " + FILE) | |
with open(FILE) as f: | |
data = json.load(f) | |
if skip>0: | |
print('\n +++ Skip first ' + str(skip) + ' entries\n') | |
for obj in data['items']: | |
await importItem(client, obj) | |
if __name__ == '__main__': | |
if len(sys.argv)>1: | |
skip=int(sys.argv[1]) | |
asyncio.run(main()) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment