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

0.72: websockets malformed authorization headers only on iOS #38012

Closed
efstathiosntonas opened this issue Jun 21, 2023 · 18 comments
Closed

0.72: websockets malformed authorization headers only on iOS #38012

efstathiosntonas opened this issue Jun 21, 2023 · 18 comments
Labels

Comments

@efstathiosntonas
Copy link

efstathiosntonas commented Jun 21, 2023

Description

After upgrading from react-native@0.71.11 to 0.72.0 I get this error response from Hasura 2.27.0 on iOS, on Android it works fine.

socket closed : code 4400, reason : {"server_error_msg":"4400: Connection initialization failed: Malformed Authorization header"}

I'm not quite sure if the issue relies on graphql-ws or if something has changed on react-native that affects graphql-ws somehow. This is happening on both ws:localhost and wss:// on Testflight builds.

this is the GraphQLWsLink on "@apollo/client": "3.7.16" with "graphql-ws": "^5.13.1"

 const wsLink = new GraphQLWsLink(
      createClient({
        url: webSocketUri,
        lazy: true,
        shouldRetry: () => true,
        retryAttempts: Infinity,
        retryWait: (_count) => new Promise((r) => setTimeout(() => r(), 1000)),
        on: {
          ping: (received) => {
            if (!received /* sent */) {
              timedOut = setTimeout(() => {
                /* a close event `4499: Terminated` is issued to the current WebSocket and an
                 artificial `{ code: 4499, reason: 'Terminated', wasClean: false }` close-event-like
                 object is immediately emitted without waiting for the one coming from `WebSocket.onclose`
                 calling terminate is not considered fatal and a connection retry will occur as expected
                 see: https://github.com/enisdenjo/graphql-ws/discussions/290
                 */
                wsLink.client.terminate();
              }, 5000);
            }
          },
          pong: (received) => {
            if (received) {
              clearTimeout(timedOut);
            }
          },
          closed: (error: any) => {
            console.log("socket closed : code %s, reason : %s", error.code, error.reason);
            if (error.code !== 1000) {
              disconnectedVar(true);
            }
          },
          connected: () => {
            if (disconnectedVar()) {
              disconnectedVar(false);
            }
          },
          error: (err: any) => {
            console.log(`error in sockets, code: ${err.code}, reason: ${err.reason}`);
          }
        },
        connectionParams: async () => {
          const accessToken = await getAccessTokenRefresh();
          if (accessToken?.token) {
            return {
              headers: {
                Authorization: `Bearer ${accessToken.token}`
              }
            };
          }
          return {};
        }
      })
    );

I've also opened an issue on graphql-ws repo: enisdenjo/graphql-ws#485

React Native Version

0.72.0

Output of npx react-native info

System:
OS: macOS 13.4
CPU: (20) arm64 Apple M1 Ultra
Memory: 613.11 MB / 64.00 GB
Shell:
version: "5.9"
path: /bin/zsh
Binaries:
Node:
version: 18.16.0
path: ~/.nvm/versions/node/v18.16.0/bin/node
Yarn:
version: 1.22.19
path: ~/.nvm/versions/node/v18.16.0/bin/yarn
npm:
version: 9.7.1
path: ~/.nvm/versions/node/v18.16.0/bin/npm
Watchman:
version: 2023.04.10.00
path: /opt/homebrew/bin/watchman
Managers:
CocoaPods:
version: 1.12.1
path: /opt/homebrew/opt/ruby@2.7/bin/pod
SDKs:
iOS SDK:
Platforms:
- DriverKit 22.4
- iOS 16.4
- macOS 13.3
- tvOS 16.4
- watchOS 9.4
Android SDK: Not Found
IDEs:
Android Studio: 2022.2 AI-222.4459.24.2221.10121639
Xcode:
version: 14.3.1/14E300c
path: /usr/bin/xcodebuild
Languages:
Java:
version: 17.0.6
path: /usr/bin/javac
Ruby:
version: 2.7.8
path: /opt/homebrew/opt/ruby@2.7/bin/ruby
npmPackages:
"@react-native-community/cli": Not Found
react:
installed: 18.2.0
wanted: ^18.2.0
react-native:
installed: 0.72.0
wanted: 0.72.0
react-native-macos: Not Found
npmGlobalPackages:
"react-native": Not Found
Android:
hermesEnabled: true
newArchEnabled: false
iOS:
hermesEnabled: true
newArchEnabled: false

Steps to reproduce

Nothing special, just upgraded from react-native 0.71.11 to 0.72.0

Snack, code example, screenshot, or link to a repository

no snack, it needs a minimal Hasura or other websocket API, sorry :(

@efstathiosntonas
Copy link
Author

Maybe because of this PR?

@efstathiosntonas

This comment was marked as off-topic.

@efstathiosntonas
Copy link
Author

after applying this change from this PR the issue was solved.

@cipolleschi
Copy link
Contributor

Hi @efstathiosntonas, thanks for reporting. So we have to push SocketRocket team to merge the diff, make a release and update the dependency, probably.

@efstathiosntonas
Copy link
Author

@cipolleschi thanks, it took me a good amount of time to track this down 😅

I don’t know if that PR needs extra testing, it just fulfills my current authentication flow.

@cipolleschi
Copy link
Contributor

I'll try to reach out to the team handling the library to see if we can push a release soon!

@efstathiosntonas
Copy link
Author

efstathiosntonas commented Jun 22, 2023

In the meanwhile, for anyone that can't wait for this to land on main here's how to patch it with cocoapods-patch plugin:

notice: using direct gem 'cocoapods-patch', git: 'https://github.com/crherman7/cocoapods-patch.git', branch: 'feat/cocoapods_1.12.0' on Gemfile breaks the pod install so this is the only way to go I guess.

on Podfile add this plugin right below platform :ios, '13.0'

plugin 'cocoapods-patch' 

on Gemfile add:

gem 'specific_install'
gem 'cocoapods-patch'

install bundles:

bundle install

install this gem from command line to download the cocoapods-patch that supports cocoapods@1.12.x (PR)

gem specific_install 'https://github.com/crherman7/cocoapods-patch.git' -b 'feat/cocoapods_1.12.0'

open Xcode or find this file and open it in an editor: (or create SocketRocket+0.6.0.diff under ios/patches and paste the diff you'll find below into it and simply run pod install afterwards)

/Users/stathis/IdeaProjects/<your_app_name>/ios/Pods/SocketRocket/SocketRocket/Internal/Utilities/SRURLUtilities.m

Apply the changes from the PR on line 44:

extern NSString *_Nullable SRBasicAuthorizationHeaderFromURL(NSURL *url)
{
    if (!url.user || !url.password) {
        return nil;
    }

    NSData *data = [[NSString stringWithFormat:@"%@:%@", url.user, url.password] dataUsingEncoding:NSUTF8StringEncoding];
    return [NSString stringWithFormat:@"Basic %@", SRBase64EncodedStringFromData(data)];
}

on terminal go to ios folder and run:

pod patch create SocketRocket 

A patch will be applied and stored under ios/patches folder so everytime you run pod install it will be applied.

SocketRocket+0.6.0.diff:

diff --git a/cocoapods-patch-20230622-18946-1pc4h0/SocketRocket/SocketRocket/Internal/Utilities/SRURLUtilities.m b/Pods/SocketRocket/SocketRocket/Internal/Utilities/SRURLUtilities.m
index 2f66d8d9..37ec2e08 100644
--- a/cocoapods-patch-20230622-18946-1pc4h0/SocketRocket/SocketRocket/Internal/Utilities/SRURLUtilities.m
+++ b/Pods/SocketRocket/SocketRocket/Internal/Utilities/SRURLUtilities.m
@@ -43,6 +43,10 @@ extern BOOL SRURLRequiresSSL(NSURL *url)
 
 extern NSString *_Nullable SRBasicAuthorizationHeaderFromURL(NSURL *url)
 {
+    if (!url.user || !url.password) {
+        return nil;
+    }
+
     NSData *data = [[NSString stringWithFormat:@"%@:%@", url.user, url.password] dataUsingEncoding:NSUTF8StringEncoding];
     return [NSString stringWithFormat:@"Basic %@", SRBase64EncodedStringFromData(data)];
 }

if you're using a CI tool then create a step and add this line on bash right after bundle install step

gem specific_install 'https://github.com/crherman7/cocoapods-patch.git' -b 'feat/cocoapods_1.12.0'

@efstathiosntonas
Copy link
Author

fixed on SocketRocket 0.7.0

patch for react-native, after applying it run pod install, react-native+0.72.0.patch:

diff --git a/node_modules/react-native/React-Core.podspec b/node_modules/react-native/React-Core.podspec
index 27eb5c8..f49ea03 100644
--- a/node_modules/react-native/React-Core.podspec
+++ b/node_modules/react-native/React-Core.podspec
@@ -18,7 +18,7 @@ end
 
 folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32'
 folly_version = '2021.07.22.00'
-socket_rocket_version = '0.6.0'
+socket_rocket_version = '0.7.0'
 boost_compiler_flags = '-Wno-documentation'
 
 use_hermes = ENV['USE_HERMES'] == '1'
diff --git a/node_modules/react-native/React/CoreModules/React-CoreModules.podspec b/node_modules/react-native/React/CoreModules/React-CoreModules.podspec
index 0b1659a..30542d4 100644
--- a/node_modules/react-native/React/CoreModules/React-CoreModules.podspec
+++ b/node_modules/react-native/React/CoreModules/React-CoreModules.podspec
@@ -18,7 +18,7 @@ end
 
 folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32'
 folly_version = '2021.07.22.00'
-socket_rocket_version = '0.6.0'
+socket_rocket_version = '0.7.0'
 
 header_search_paths = [
   "\"$(PODS_TARGET_SRCROOT)/React/CoreModules\"",

@cipolleschi
Copy link
Contributor

So, we can't really use SocketRocket 0.7.0 because Flipper depends on SocketRocket ~> 0.6.0, and we can't bump Flipper for major compat issues with Android's NDK. So we pushed a SocketRocket 0.6.1 with the fix and we are releasing 0.72.1 with SocketRocket 0.6.1. It should be out in some hours and you won't need patches anymore! :D

@efstathiosntonas
Copy link
Author

@cipolleschi thanks for the update, I’m not using Flipper because of Firebase so I never saw this compatibility issue.

@efstathiosntonas
Copy link
Author

Fixed on 0.72.1

@billnbell
Copy link
Contributor

So, we can't really use SocketRocket 0.7.0 because Flipper depends on SocketRocket ~> 0.6.0, and we can't bump Flipper for major compat issues with Android's NDK. So we pushed a SocketRocket 0.6.1 with the fix and we are releasing 0.72.1 with SocketRocket 0.6.1. It should be out in some hours and you won't need patches anymore! :D

there are a ton of bug fixes in 0.7.0 can we get them back ported?

@cipolleschi
Copy link
Contributor

Hi there. I saw some changes passing by that were bumping a dependency for which we couldn't bring 0.7.0 in React Native. So, we will be likely able to bump Socket Rocket to 0.7.0 from React Native 0.73

@XChikuX
Copy link

XChikuX commented Aug 12, 2023

@cipolleschi I'm running 0.72.3 with:
image

So, I'm guessing its not in yet. Sigh
How far are we in term of 0.73 on a time scale?

@redpanda-bit
Copy link

@XChikuX your error looks different from the original error posted in this issue.

socket closed : code 4400, reason : {"server_error_msg":"4400: Connection initialization failed: Malformed Authorization header"}

@XChikuX
Copy link

XChikuX commented Aug 12, 2023

It occurs under the exact same conditions however. @carlosalmonte04

My websocket connections work for Android, Web and even on iOS for ws:// links

Only the secure wss:// seems to break for iOS.

@billnbell
Copy link
Contributor

mine works perfectly fine on 0.72.3 No issues.

@XChikuX
Copy link

XChikuX commented Aug 13, 2023

@billnbell Can I send you an email with my graphql subscription endpoint? (that uses websockets with graphql-ws)

I want to know if this is an older device issue. I'm using an iphone 7 to test?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

6 participants