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

gauth qr code can't be imported #244

Closed
1 task done
astrakid opened this issue Nov 22, 2023 · 20 comments
Closed
1 task done

gauth qr code can't be imported #244

astrakid opened this issue Nov 22, 2023 · 20 comments
Labels
bug Something isn't working fixed The issue is fixed (in a coming release)
Milestone

Comments

@astrakid
Copy link

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
MAIL_FROM_ADDRESS=xxx@yyy.de
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
MAIL_FROM=changeme@example.com
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
SITE_OWNER=mail@example.com
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

  • Docker

Additional information

No response

@astrakid
Copy link
Author

image

@astrakid
Copy link
Author

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

@Bubka
Copy link
Owner

Bubka commented Nov 23, 2023

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

@astrakid
Copy link
Author

Android 13 (MIUI 14.0.8), Authenticator 6.0

@juppwerner
Copy link

juppwerner commented Nov 24, 2023

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
Copy link

juppwerner commented Nov 24, 2023

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() ?

@Bubka
Copy link
Owner

Bubka commented Nov 24, 2023

@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?

@astrakid
Copy link
Author

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

@astrakid
Copy link
Author

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.

@juppwerner
Copy link

juppwerner commented Nov 24, 2023

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.

@Bubka
Copy link
Owner

Bubka commented Dec 12, 2023

@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
Copy link
Owner

Bubka commented Dec 12, 2023

@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?

@kenci
Copy link

kenci commented Dec 17, 2023

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

@astrakid
Copy link
Author

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.

@deanfourie1
Copy link

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"

@ericsubach
Copy link

ericsubach commented Mar 10, 2024

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.

@Bubka
Copy link
Owner

Bubka commented Mar 11, 2024

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

@xtay573269555
Copy link

Same issue here.

@Bubka
Copy link
Owner

Bubka commented Mar 22, 2024

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 Bubka added bug Something isn't working fixed The issue is fixed (in a coming release) labels Mar 22, 2024
@Bubka Bubka added this to the v5.1.2 milestone Mar 28, 2024
@zzzdrv
Copy link

zzzdrv commented Apr 5, 2024

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.

@Bubka Bubka modified the milestones: v5.1.2, v5.2.0 Apr 26, 2024
@Bubka Bubka closed this as completed in 2db5adf May 29, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working fixed The issue is fixed (in a coming release)
Projects
Status: Released
Development

No branches or pull requests

8 participants