Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Google Authenticator - Transfer/Export accounts #74

Closed
aronmal opened this issue May 11, 2022 · 17 comments
Closed

Google Authenticator - Transfer/Export accounts #74

aronmal opened this issue May 11, 2022 · 17 comments
Labels
Milestone

Comments

@aronmal
Copy link
Contributor

aronmal commented May 11, 2022

Is your feature request related to a problem? Please describe.
When transferring/exporting TOTPs from Google Authenticator, the QR code is not being recognised. This is due to Google Authenticator exporting format is encoded and can be decoded with a simple python script or this migration site which is also open source and generates a QR code to scan afterwards.

Describe the solution you'd like
An integrated converter when recognising otpauth-migration://offline?data=... pattern from the QR code and import the profile automatically.

Describe alternatives you've considered
Using the simplified python script and manually add the TOTP to the 2fauth app or use the site and scan the resulting QR codes.

Additional context (scanned from Google Authenticator and then hold my fingers in front of the camera)

@Bubka
Copy link
Owner

Bubka commented May 11, 2022

FYI there is a discussion about this feature here: #27

@Bubka Bubka added the request label May 11, 2022
@aronmal
Copy link
Contributor Author

aronmal commented May 12, 2022

Great to see it is already in Mind. But the last comments were made in Aug 2021. I would love to try to contribute at this point maybe, but please brief me real quick, the QR code is being recognized in a picture, the payload is read, in this case protobuf need to be decoded and then the new TOTP is being saved. Could you give me some sort of flow chart what library/methods you use for what step and if they're client or server side. Then I would give myself a try on this.

@aronmal
Copy link
Contributor Author

aronmal commented May 14, 2022

Ok, I am currently working on it. I got a big confusion out of my way, the migration format uses Percent-encoding. After that, it is decodeable with protobuf.

Question to you @Bubka, should the conversion from otpauth-migration://offline?data=... to otpauth://totp/... format happen client or server side?

@Bubka
Copy link
Owner

Bubka commented May 14, 2022

Question to you @Bubka, should the conversion from otpauth-migration://offline?data=... to otpauth://totp/... format happen client or server side?

Is this just a string conversion or is there some logic to implement?

@aronmal

This comment was marked as outdated.

@aronmal
Copy link
Contributor Author

aronmal commented May 15, 2022

I am blind, there is an official package. I will make a sample script, then you can implement it at the right place in your vue fronted.

@aronmal
Copy link
Contributor Author

aronmal commented May 15, 2022

The package is not needed. The .proto file is being compiled to a _pb.js file which than can be used to deserialize the base64 string.

The working code looks like this:

carbon

const Base32 = require('hi-base32'); // Install with 'npm install hi-base32'
const otpauth_migration = require('./otpauth-migration_pb'); // Is compiled with the command (works on linux) 'protoc --js_out=import_style=commonjs,binary:. ./otpauth-migration.proto' and the .proto file from 'https://github.com/digitalduke/otpauth-migration-decoder/blob/master/src/otpauth-migration.proto'

const otpauth_migration_format = 'otpauth-migration://offline?data='; // This is what needs to be replaced from the input with an empty string
const Algorithm = {
    1: 'SHA1',
    2: 'SHA256',
    3: 'SHA512',
    4: 'MD5',
};
const DigitCount = {
    1: '6',
    2: '8',
};
const OtpType = {
    1: 'hotp',
    2: 'totp',
};

const input = "otpauth-migration://offline?data=CjEKCkhlbGxvId6tvu8SGEV4YW1wbGU6YWxpY2VAZ29vZ2xlLmNvbRoHRXhhbXBsZTAC"; // The Input. Obviously.

const uriDecoded = decodeURIComponent(input.replace(otpauth_migration_format, '')); // Replacing function to get only the data and use decodeURIComponent() to decode Percent-encoding. We are left with an base64 string.
const protobufDecoded = otpauth_migration.Payload.deserializeBinary(uriDecoded).array[0]; // Here happens the real magic, the base64 string to an array of OTPs.
const otpDecode = (otpArray) => otpArray.map(otp => { // Map the OTPs array
    const otp_type = OtpType[otp[5] || 1],
        otp_name = otp[1],
        otpParams = {
            Algorithm: Algorithm[otp[3] || 1],
            DigitCount: DigitCount[otp[4] || 1],
            Issuer: otp[2],
            Period: 30,
            Secret: Base32.encode(Buffer.from(otp[0]))
        },
        otp_params = [
            `algorithm=${otpParams.Algorithm}`,
            `digits=${otpParams.DigitCount}`,
            `issuer=${otpParams.Issuer}`,
            `period=${otpParams.Period}`,
            `secret=${otpParams.Secret}`
        ].join('&'),
        otpauth = `otpauth://${otp_type}/${otp_name}?${otp_params}`; //Put together the otpauth link
    return otpauth;
})

console.log(otpDecode(protobufDecoded)); // Évoilà, there we have our exportet/transfered Google Authenticator OTPs
// Expected output: ['otpauth://totp/Example:[email protected]?algorithm=SHA1&digits=6&issuer=Example&period=30&secret=JBSWY3DPEHPK3PXP']

@aronmal
Copy link
Contributor Author

aronmal commented May 15, 2022

https://github.com/aronmal/2FAuth/commit/b61cf94f8080e80f1ea599fa83c6ddb02ff9f4bf in case you need the .proto file or want to test it.

@aronmal
Copy link
Contributor Author

aronmal commented May 17, 2022

@Bubka 👀

@Bubka
Copy link
Owner

Bubka commented May 17, 2022

Sorry it's a very busy week. I've prioritize the proxy Auth issues since Monday, I hope I could review this on Thursday.

@aronmal
Copy link
Contributor Author

aronmal commented May 22, 2022

In this file you have a regex. I don't know much about php, so no plan how to disable this check, but because I'm familiar with typescript and react I found where (here and here) to implement the conversion in vue. So my request would be to not check with the regex, but just parse the decoded QR Code and then do a check in vue if it matches the regex, otherwise 'optauth-migration' regex, in that case convert it or then throw an error.

@aronmal
Copy link
Contributor Author

aronmal commented May 22, 2022

And another question, you can export multiple accounts at the same time. You have an array,, but I would just hard code it to take the first in the array.

@Bubka
Copy link
Owner

Bubka commented May 24, 2022

Hi @aronmal

I've been reading and testing around protobuf and an import feature past days. And at the end I think that the protobuf decoding has to be done by the back-end.

The main reason is that 2FAuth has all its business logic implemented server-side, and exposes an API through the back-end capabilities. If the protobuf decoding is done by Vue then the API won't be able to provide an endpoint to import GoogleAuth data.

Thanks to your previous contributions I was able to implement the protobuf decoding with PHP 👍🏻

@aronmal
Copy link
Contributor Author

aronmal commented May 24, 2022

OK, my Idea was like:

const { data } = await this.form.upload('/api/v1/qrcode/decode', imgdata)

const otpauthRegex = /^otpauth:\/\/[h,t]otp\//i;
const decodedUri = otpauthRegex.test(data.data) ? data.data : otpmigrationDecode(data.data)

this.$router.push({ name: 'createAccount', params: { decodedUri } });

in the Start.vue and Create.vue. But when you can implement it in php it is also fine I guess.

@Bubka
Copy link
Owner

Bubka commented May 24, 2022

Sure, but it's not that simple.

2Fauth uses 2 different QR decoders, one for the live scan, run by Vue, and one for uploaded QRs, run by the back-end.

Moreover, forcing the user to import a single account whereas Google Authenticator allows to export the whole accounts would result in a bad UX. It is also necessary to create a dedicated UI to handle specific messages during the import process, especially in case of treatment error.

I'm working on it.

@Bubka Bubka added feature and removed request labels Jun 2, 2022
@Bubka Bubka changed the title Google Authenticator - Transfer/Export accounts -> No valid OTP resource in this QR code Google Authenticator - Transfer/Export accounts Jun 2, 2022
@Bubka Bubka added this to the v3.2 milestone Jun 2, 2022
@Bubka Bubka moved this to Todo in 2FAuth backlog Jun 2, 2022
@Bubka Bubka moved this from Todo to In Progress in 2FAuth backlog Jun 2, 2022
@Bubka
Copy link
Owner

Bubka commented Jun 21, 2022

FYI, I committed some changes to the dev branch this afternoon, Google Authenticator export is now supported, just scan/upload the QR code provided by G-Auth to fire the import feature.

The 2fauth/2fauth:dev docker image is up-to-date if you want to give it a try.

@aronmal
Copy link
Contributor Author

aronmal commented Jun 21, 2022

I will give it a try when I find time for it. Nice job!

@Bubka Bubka moved this from In Progress to Done in 2FAuth backlog Jun 22, 2022
@Bubka Bubka closed this as completed Jul 18, 2022
@Bubka Bubka moved this from Done to Released in 2FAuth backlog Jul 18, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
Status: Released
Development

No branches or pull requests

2 participants