2FAuth icon indicating copy to clipboard operation
2FAuth copied to clipboard

gauth qr code can't be imported

Open astrakid opened this issue 1 year ago • 20 comments

Version

4.2.4

Details & Steps to reproduce

1st way: open google authenticator, export all entries --> scan the qr code with 2fauth (import). error message: invalid or not readable data by google authenticator (translated from german) 2nd way: upload the screenshot from google authenticator -> same error message

Expectation

google authenticator entities are imported

Error & Logs

[2023-11-22 19:01:13] local.ERROR: Protobuf failed to get OTP parameters from provided migration URI  
[2023-11-22 19:01:13] local.ERROR: Error occurred during parsing: Unexpected wire type.  
[2023-11-22 19:01:13] local.ERROR: Google Authenticator {"userId":1,"exception":"[object] (App\\Exceptions\\InvalidMigrationDataException(code: 0): Google Authenticator at /srv/app/Services/Migrators/GoogleAuthMigrator.php:37)
[stacktrace]
#0 /srv/app/Services/TwoFAccountService.php(59): App\\Services\\Migrators\\GoogleAuthMigrator->migrate()
#1 /srv/vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php(353): App\\Services\\TwoFAccountService->migrate()
#2 /srv/app/Api/v1/Controllers/TwoFAccountController.php(134): Illuminate\\Support\\Facades\\Facade::__callStatic()
#3 /srv/vendor/laravel/framework/src/Illuminate/Routing/Controller.php(54): App\\Api\\v1\\Controllers\\TwoFAccountController->migrate()
#4 /srv/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php(43): Illuminate\\Routing\\Controller->callAction()
#5 /srv/vendor/laravel/framework/src/Illuminate/Routing/Route.php(260): Illuminate\\Routing\\ControllerDispatcher->dispatch()
#6 /srv/vendor/laravel/framework/src/Illuminate/Routing/Route.php(205): Illuminate\\Routing\\Route->runController()
#7 /srv/vendor/laravel/framework/src/Illuminate/Routing/Router.php(799): Illuminate\\Routing\\Route->run()
#8 /srv/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(141): Illuminate\\Routing\\Router->Illuminate\\Routing\\{closure}()
#9 /srv/app/Http/Middleware/LogUserLastSeen.php(34): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#10 /srv/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): App\\Http\\Middleware\\LogUserLastSeen->handle()
#11 /srv/app/Http/Middleware/KickOutInactiveUser.php(50): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#12 /srv/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): App\\Http\\Middleware\\KickOutInactiveUser->handle()
#13 /srv/vendor/laravel/framework/src/Illuminate/Routing/Middleware/SubstituteBindings.php(50): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#14 /srv/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Routing\\Middleware\\SubstituteBindings->handle()
#15 /srv/app/Http/Middleware/SetLanguage.php(68): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#16 /srv/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): App\\Http\\Middleware\\SetLanguage->handle()
#17 /srv/vendor/laravel/framework/src/Illuminate/Auth/Middleware/Authenticate.php(57): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#18 /srv/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Auth\\Middleware\\Authenticate->handle()
#19 /srv/vendor/laravel/framework/src/Illuminate/Routing/Middleware/ThrottleRequests.php(159): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#20 /srv/vendor/laravel/framework/src/Illuminate/Routing/Middleware/ThrottleRequests.php(135): Illuminate\\Routing\\Middleware\\ThrottleRequests->handleRequest()
#21 /srv/vendor/laravel/framework/src/Illuminate/Routing/Middleware/ThrottleRequests.php(87): Illuminate\\Routing\\Middleware\\ThrottleRequests->handleRequestUsingNamedLimiter()
#22 /srv/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Routing\\Middleware\\ThrottleRequests->handle()
#23 /srv/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(116): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#24 /srv/vendor/laravel/framework/src/Illuminate/Routing/Router.php(800): Illuminate\\Pipeline\\Pipeline->then()
#25 /srv/vendor/laravel/framework/src/Illuminate/Routing/Router.php(777): Illuminate\\Routing\\Router->runRouteWithinStack()
#26 /srv/vendor/laravel/framework/src/Illuminate/Routing/Router.php(741): Illuminate\\Routing\\Router->runRoute()
#27 /srv/vendor/laravel/framework/src/Illuminate/Routing/Router.php(730): Illuminate\\Routing\\Router->dispatchToRoute()
#28 /srv/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(200): Illuminate\\Routing\\Router->dispatch()
#29 /srv/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(141): Illuminate\\Foundation\\Http\\Kernel->Illuminate\\Foundation\\Http\\{closure}()
#30 /srv/app/Http/Middleware/ForceJsonResponse.php(19): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#31 /srv/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): App\\Http\\Middleware\\ForceJsonResponse->handle()
#32 /srv/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#33 /srv/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ConvertEmptyStringsToNull.php(31): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle()
#34 /srv/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Foundation\\Http\\Middleware\\ConvertEmptyStringsToNull->handle()
#35 /srv/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#36 /srv/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TrimStrings.php(40): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle()
#37 /srv/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Foundation\\Http\\Middleware\\TrimStrings->handle()
#38 /srv/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ValidatePostSize.php(27): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#39 /srv/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize->handle()
#40 /srv/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/PreventRequestsDuringMaintenance.php(89): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#41 /srv/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Foundation\\Http\\Middleware\\PreventRequestsDuringMaintenance->handle()
#42 /srv/vendor/laravel/framework/src/Illuminate/Http/Middleware/HandleCors.php(62): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#43 /srv/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Http\\Middleware\\HandleCors->handle()
#44 /srv/vendor/laravel/framework/src/Illuminate/Http/Middleware/TrustProxies.php(39): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#45 /srv/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Http\\Middleware\\TrustProxies->handle()
#46 /srv/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(116): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#47 /srv/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(175): Illuminate\\Pipeline\\Pipeline->then()
#48 /srv/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(144): Illuminate\\Foundation\\Http\\Kernel->sendRequestThroughRouter()
#49 /srv/public/index.php(52): Illuminate\\Foundation\\Http\\Kernel->handle()
#50 {main}
"}

Execution environment

AUTH_PROXY_HEADER_FOR_EMAIL=null REDIS_PORT=6379 LOG_LEVEL=notice DB_CONNECTION=sqlite APP_DEBUG=false MAIL_USERNAME=null WEBAUTHN_NAME=2FAuth HOSTNAME=314d82d55162 [email protected] APP_URL=http://localhost IS_DEMO_APP=false MIX_ENV=local SHLVL=1 PUSHER_APP_ID= HOME=/ PUSHER_APP_SECRET= BROADCAST_DRIVER=log COMMIT=c765bfd MAIL_FROM_NAME=2FAuth DB_DATABASE=/srv/database/database.sqlite APP_NAME=2FAuth MAIL_DRIVER=SMTP WEBAUTHN_ID=null WEBAUTHN_ICON=null SESSION_DRIVER=file WEBAUTHN_USER_VERIFICATION=preferred TRUSTED_PROXIES=null LOG_CHANNEL=daily [email protected] VERSION=latest CREATED=2023-11-21T12:53:11Z CACHE_DRIVER=file LOGIN_THROTTLE=5 QUEUE_DRIVER=sync TERM=xterm MAIL_ENCRYPTION=null PUSHER_APP_KEY= PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin [email protected] PUSHER_APP_CLUSTER=mt1 MAIL_PASSWORD=null MAIL_HOST=10.a.b.c THROTTLE_API=60 AUTHENTICATION_GUARD=web-guard SESSION_LIFETIME=120 REDIS_PASSWORD=null PROXY_LOGOUT_URL=null MAIL_PORT=2525 MIX_PUSHER_APP_KEY= APP_ENV=local APP_KEY=SomeRandomStringOf32CharsExactly REDIS_HOST=127.0.0.1 MIX_PUSHER_APP_CLUSTER= PWD=/srv AUTH_PROXY_HEADER_FOR_USER=null

Containerization

  • [X] Docker

Additional information

No response

astrakid avatar Nov 22 '23 19:11 astrakid

image

astrakid avatar Nov 22 '23 19:11 astrakid

the qr-code contains: otpauth-migration://offline?data=Ck123456zy5Glu123456JlNsYW123456VkNDKT123456ZWdyb2123456NzhAZ2123456Y29tGg123456ayABKA123456[430charremoved]ChSjccaijm%2FJ6gqvp4%2FOSkHu5vKAXhIIbGlua2VkSW4gASgBMAIQARgBIAAo5J6%2Bqfr%2F%2F%2F%2F%2FAQ%3D%3D

astrakid avatar Nov 22 '23 19:11 astrakid

I just tested with GAuth 4.0.2 on iOS, no issue. What OS & GAuth version are you using?

Bubka avatar Nov 23 '23 07:11 Bubka

Android 13 (MIUI 14.0.8), Authenticator 6.0

astrakid avatar Nov 23 '23 07:11 astrakid

Same error here, saying "not a valid QR code".

Android 10, Honor 8x, Google authenticator version 6.0

The QR Code contains: otpauth-migration::offline?data=...

Addition: I see this error in the running docker conatiner error output:

[error] 41#41: *27 FastCGI sent in stderr: "PHP message: PHP Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 67108872 bytes) in /srv/vendor/khanamiryan/qrcode-detector-decoder/lib/GDLuminanceSource.php on line 85" while reading response header from upstream, client: 172.17.0.1, server: 2fauth, request: "POST /api/v1/qrcode/decode HTTP/1.1", upstream: "fastcgi://127.0.0.1:9000", host: "localhost:8081", referrer: "http://localhost:8081/account/import"

The image has a size of 376 KB.

The image was then cropped to only contain the QR code, is of the type jpg and has a size of 146 kB.

Error: "POST /api/v1/qrcode/decode HTTP/1.1" 400 44 "http://localhost:8081/account/import" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36 Edg/119.0.0.0"

Thank you.

Regards Joachim

juppwerner avatar Nov 24 '23 07:11 juppwerner

In addition to my report above:

I was able to sucessfully decode the Goggle Autheticator export QRCode screenshot using the khanamiryan/php-qrcode-detector-decoder library with this code:

$qrcode = new QrReader(__DIR__.'/Screenshot_20231124_084225_65.jpg');
$hints = [
    'TRY_HARDER' => true,
    'NR_ALLOW_SKIP_ROWS' => 0
];
$qrcode->decode($hints);
$text = $qrcode->text($hints); 

Maybe the $hints array should be added to the method app/Services/QrCodeService::decode() method() ?

juppwerner avatar Nov 24 '23 08:11 juppwerner

@juppwerner Thx for the hint. I will make some tests with these arguments, but before that I need to reproduce the issue and check exactly what is failing. The OP issue seems to be an error at data parsing while you seems to have an issue at QR decoding.

To you both: How many accounts were exported from Authenticator? With Dev Tools opened in your browser, what is the result of the POST request to .../api/v1/qrcode/decode right after you upload a (rejected) QR code image from the Import page?

Bubka avatar Nov 24 '23 12:11 Bubka

image i have 7 accounts in there. when just import 1 it is working. will now try to find the bad one.

astrakid avatar Nov 24 '23 15:11 astrakid

yes, the amount of exports seem to be the root cause. with 4 accounts it works, starting with 5 it doesn't recognize a live scan and an uploaded qr code will fail as well.

astrakid avatar Nov 24 '23 15:11 astrakid

I have 9 accounts in my export QrCode. Just to clarify, I was not able to parse my qr code image to text without using the hints array.

juppwerner avatar Nov 24 '23 16:11 juppwerner

@juppwerner

Error Allowed memory size of 134217728 bytes exhausted (tried to allocate 67108872 bytes) in /srv/vendor/khanamiryan/qrcode-detector-decoder/lib/GDLuminanceSource.php on line 85 means the memory_limit set in php.ini has been reached by the script. So trying to decode an hi-res image may be the reason why you faced it.

How did you tried the following? Using a local dev env?

I was able to sucessfully decode the Goggle Autheticator export QRCode screenshot using the khanamiryan/php-qrcode-detector-decoder library with this code:

$qrcode = new QrReader(__DIR__.'/Screenshot_20231124_084225_65.jpg');
$hints = [
    'TRY_HARDER' => true,
    'NR_ALLOW_SKIP_ROWS' => 0
];

If so, the php config of your dev env may have a different memory_limit than the one in the docker container, making this test not really reliable. Correct me if I'm wrong, this is just a guess.

Furthermore, I don't understand which "version" of the qrcode you was able to decode with the $hints option: the original one or the cropped one?

Bubka avatar Dec 12 '23 13:12 Bubka

@astrakid

yes, the amount of exports seem to be the root cause. with 4 accounts it works, starting with 5 it doesn't recognize a live scan and an uploaded qr code will fail as well.

The live scan decoding is not done by the same part of 2FAuth as the decoding of the uploaded qrcodes. In the first case it is a local js qr reader, otherwise it is a php qr reader. This is why you received the error regardless of the method. So the pb comes from the part after the = sign of the otpauth migration uri which is encoded with protobuf.

i have 7 accounts in there. when just import 1 it is working. will now try to find the bad one.

Any news on that?

Bubka avatar Dec 12 '23 15:12 Bubka

is it possible to increase the php memory limit in docker container?

kenci avatar Dec 17 '23 13:12 kenci

i have 7 accounts in there. when just import 1 it is working. will now try to find the bad one.

at least it was only the amount of imported codes causing the issue, so all codes worked at all. but not all 7 at once.

astrakid avatar Dec 18 '23 18:12 astrakid

I too am unable to import all google account,

When exporting them, it provides 3 QR codes to scan for import, I cannt add these to 2fauthy as it doesnt like them and produces a error "Server Error"

deanfourie1 avatar Jan 10 '24 00:01 deanfourie1

Leaving this here for anyone who is also struggling with this issue. I had to use an app like QtQr to get the text from the QR code, then use this tool to convert the Google-specific transfer links to plain otpauth links that work on most authenticators. Supposedly the Aegis authenticator can understand the Google links natively, but I haven't confirmed.

ericsubach avatar Mar 10 '24 02:03 ericsubach

Thx for the head up and the workaround @ericsubach, I totally forgot this issue 🫤 Will work on it asap.

Bubka avatar Mar 11 '24 12:03 Bubka

Same issue here.

xtay573269555 avatar Mar 18 '24 07:03 xtay573269555

So, I was able to reproduce the not a valid qr code error. This occurs when the uploaded image contains a qrcode which is too small or too blurry. I have changed the message in the error notification to be more relevant and to suggest how to fix the issue (cropping the image, submitting a sharper image).

I've also changed the way images with qr code are analyzed. Now, if no readable QR code is found in the image, the image is scanned again but with a more aggressive (but more expensive for the server) method. In some situations this should prevent the qrcode reading to fail.

Finally, for those who want to increase the memory limit no matter what, here is how to do so using docker-compose:

  • Create a 2fauth.ini file in a local directory, i.e /docker-php-conf.d

  • Edit the file and add the directive you need. For example, to increase memory limit, add memory_limit=256M

  • In your docker-compose.yml file, add a new volume:

      volumes:
        - /docker-php-conf.d/php-2fauth.ini:/etc/php81/conf.d/2fauth.ini
    
  • Start your container

Bubka avatar Mar 22 '24 17:03 Bubka

same issue as OP

Ver 5.1.1 (Deployed on Ubuntu via Docker Compose. ) Authenticator 6.0

5 Google accounts, successfully exported 2. However, for the remaining accounts, whether exporting individually or in bulk, it consistently prompts "Invalid or unreadable Google Authenticator data".

Here's my workaround:

After exporting the Google QR code, I scanned it using the Edge browser on my phone to obtain the text (otpauth-migration://offline?data=xxxx), saved it as a text file, and successfully imported it.

zzzdrv avatar Apr 05 '24 04:04 zzzdrv