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

Inconsistent Response Headers between Android and iOS in .NET MAUI with B1Slayer #49

Open
aaron-yb opened this issue Aug 23, 2023 · 10 comments

Comments

@aaron-yb
Copy link

Your project is great! It's saved me a ton of time. However when implementing B1Slayer in a .NET MAUI app, I've noticed an inconsistency in the response headers between Android and iOS. On Android, I receive authentication headers that are missing on iOS.

SLConnection.LoginAsync on Android gives the following output:

[DOTNET] Request URL: https://URL:50000/b1s/v1/Login
[DOTNET] HTTP Method: POST
[DOTNET] Response Status Code: OK
[DOTNET] Request Headers:
[DOTNET] Response Headers:
[DOTNET] Connection: Keep-Alive
[DOTNET] Date: Wed, 23 Aug 2023 10:05:18 GMT
[DOTNET] Keep-Alive: timeout=5, max=100
[DOTNET] Server: Apache
[DOTNET] Set-Cookie: B1SESSION=9056bec8-419c-11ee-c000-000c2980395a-3812-14596;HttpOnly;;Secure;SameSite=None, ROUTEID=.node1; path=/;Secure;SameSite=None
[DOTNET] Vary: Accept-Encoding
[DOTNET] X-Android-Received-Millis: 1692785120383
[DOTNET] X-Android-Response-Source: NETWORK 200
[DOTNET] X-Android-Selected-Protocol: http/1.1
[DOTNET] X-Android-Sent-Millis: 1692785117731
[DOTNET] Response Body: {
[DOTNET] "odata.metadata" : "https://URL:50000/b1s/v1/$metadata#B1Sessions/@Element",
[DOTNET] "SessionId" : "9056bec8-419c-11ee-c000-000c2980395a-3812-14596",
[DOTNET] "Version" : "1000210",
[DOTNET] "SessionTimeout" : 30
[DOTNET] }

The same request on iOS:
2023-08-23 11:06:57.445422+0100 SLTest[82914:1147716] Request URL: https://URL:50000/b1s/v1/Login
2023-08-23 11:06:57.445727+0100 SLTest[82914:1147716] HTTP Method: POST
2023-08-23 11:06:57.446298+0100 SLTest[82914:1147716] Response Status Code: OK
2023-08-23 11:06:57.446382+0100 SLTest[82914:1147716] Request Headers:
2023-08-23 11:06:57.446466+0100 SLTest[82914:1147716] Response Headers:
2023-08-23 11:06:57.446848+0100 SLTest[82914:1147716] Server: Apache
2023-08-23 11:06:57.446985+0100 SLTest[82914:1147716] Vary: Accept-Encoding
2023-08-23 11:06:57.448994+0100 SLTest[82914:1147716] Date: Wed, 23 Aug 2023 10:06:54 GMT
2023-08-23 11:06:57.449116+0100 SLTest[82914:1147716] Keep-Alive: timeout=5, max=100
2023-08-23 11:06:57.449216+0100 SLTest[82914:1147716] Connection: Keep-Alive
2023-08-23 11:06:57.453453+0100 SLTest[82914:1147716] Response Body: {
"odata.metadata" : "https://URL:50000/b1s/v1/$metadata#B1Sessions/@Element",
"SessionId" : "c9695270-419c-11ee-c000-000c2980395a-12492-1548",
"Version" : "1000210",
"SessionTimeout" : 30
}

Any iOS request afterwards is missing headers resulting in the following:

2023-08-23 11:27:14.436896+0100 SLTest[84984:1176478] Request URL: https://URL:50000/b1s/v1/Items?$select=ItemCode%2C ItemName
2023-08-23 11:27:14.437294+0100 SLTest[84984:1176478] HTTP Method: GET
2023-08-23 11:27:14.437526+0100 SLTest[84984:1176478] Response Status Code: Unauthorized
2023-08-23 11:27:14.437677+0100 SLTest[84984:1176478] Request Headers:
2023-08-23 11:27:14.437907+0100 SLTest[84984:1176478] Response Headers:
2023-08-23 11:27:14.438118+0100 SLTest[84984:1176478] Server: Apache
2023-08-23 11:27:14.438274+0100 SLTest[84984:1176478] Vary: Accept-Encoding
2023-08-23 11:27:14.438471+0100 SLTest[84984:1176478] Date: Wed, 23 Aug 2023 10:27:14 GMT
2023-08-23 11:27:14.438668+0100 SLTest[84984:1176478] WWW-Authenticate: Basic realm=/b1s/v1/Items
2023-08-23 11:27:14.438817+0100 SLTest[84984:1176478] Keep-Alive: timeout=5, max=100
2023-08-23 11:27:14.438967+0100 SLTest[84984:1176478] Connection: Keep-Alive
2023-08-23 11:27:14.439215+0100 SLTest[84984:1176478] Response Body: {
"error" : {
"code" : 301,
"message" : {
"lang" : "en-us",
"value" : "Invalid session or session already timeout."
}
}
}

Have tried adjusting iOS info.plist file with NSAppTransportSecurity headers.

Can reproduce by making new Maui (dotnet 7) project with B1SLayer 1.3.2, creating a new SLConnection, awaiting LoginAsync and examining the response using SLConnection.AfterCall

SLTest.zip

@bgmulinari
Copy link
Owner

bgmulinari commented Aug 23, 2023

Hi, @aaron-yb.

Sorry for my ignorance, but I've never developed a MAUI/Xamarin app targeting iOS. Can I still reproduce the issue with your sample project if I don't own a Mac nor an iPhone?

@aaron-yb
Copy link
Author

Hi @bgmulinari - Just want to say again: Thank you, your project has saved me so much time!

I've come across an issue specifically with Mac Catalyst / iOS Maui builds. This persists on both the simulator and actual devices. As far as I know, iOS debugging does require a Mac - (Apple, right?)

Inspecting the SLConnection.cs class, specifically within the ExecuteLoginAsync method, I noticed that cookieJar has a count of 0 when making a request from Apple devices. Which would explain the issue with authentication as there isn't a cookie.

I've submitted a pull request which is a bit hacky.... that adjusts the ExecuteLoginAsync method to manually generate a cookie if one isn't present. It might come across as a bit of a workaround, but it resolves the issue I faced for now

@bgmulinari
Copy link
Owner

@aaron-yb, I took the liberty to modify your sample project to perform the login request using just Flurl, like B1SLayer performs in the background. Can you test it on iOS and report back, please?

From what you're describing, it sounds like it might be an issue with Flurl.

@bgmulinari
Copy link
Owner

SLTest2.zip

@aaron-yb
Copy link
Author

Hi @bgmulinari here you go

On iOS / Mac I get the following output:

2023-08-23 14:56:09.536898+0100 SLTest[10595:1499318] Request URL: https://URL:50000/b1s/v1/Login
2023-08-23 14:56:09.537186+0100 SLTest[10595:1499318] HTTP Method: POST
2023-08-23 14:56:09.537798+0100 SLTest[10595:1499318] Response Status Code: OK
2023-08-23 14:56:09.537904+0100 SLTest[10595:1499318] Request Headers:
2023-08-23 14:56:09.538140+0100 SLTest[10595:1499318] Response Headers:
2023-08-23 14:56:09.538552+0100 SLTest[10595:1499318] Server: Apache
2023-08-23 14:56:09.538721+0100 SLTest[10595:1499318] Vary: Accept-Encoding
2023-08-23 14:56:09.540703+0100 SLTest[10595:1499318] Date: Wed, 23 Aug 2023 13:56:06 GMT
2023-08-23 14:56:09.540808+0100 SLTest[10595:1499318] Keep-Alive: timeout=5, max=100
2023-08-23 14:56:09.540898+0100 SLTest[10595:1499318] Connection: Keep-Alive
2023-08-23 14:56:09.542724+0100 SLTest[10595:1499318] Request Body:
{
"odata.metadata" : "https://URL:50000/b1s/v1/$metadata#B1Sessions/@Element",
"SessionId" : "ce6f595c-41bc-11ee-c000-000c2980395a-14548-6632",
"Version" : "1000210",
"SessionTimeout" : 30
}
2023-08-23 14:56:09.558334+0100 SLTest[10595:1498480] Cookies received:

On Android I get the following:

[DOTNET] Request URL: https://URL:50000/b1s/v1/Login
[DOTNET] HTTP Method: POST
[DOTNET] Response Status Code: OK
[DOTNET] Request Headers:
[DOTNET] Response Headers:
[DOTNET] Connection: Keep-Alive
[DOTNET] Date: Wed, 23 Aug 2023 14:02:19 GMT
[DOTNET] Keep-Alive: timeout=5, max=100
[DOTNET] Server: Apache
[DOTNET] Set-Cookie: B1SESSION=ac58e080-41bd-11ee-c000-000c2980395a-14752-8204;HttpOnly;;Secure;SameSite=None, ROUTEID=.node8; path=/;Secure;SameSite=None
[DOTNET] Vary: Accept-Encoding
[DOTNET] X-Android-Received-Millis: 1692799340723
[DOTNET] X-Android-Response-Source: NETWORK 200
[DOTNET] X-Android-Selected-Protocol: http/1.1
[DOTNET] X-Android-Sent-Millis: 1692799338028
[DOTNET] Request Body:
[DOTNET] {
[DOTNET] "odata.metadata" : "https://URL:50000/b1s/v1/$metadata#B1Sessions/@Element",
[DOTNET] "SessionId" : "ac58e080-41bd-11ee-c000-000c2980395a-14752-8204",
[DOTNET] "Version" : "1000210",
[DOTNET] "SessionTimeout" : 30
[DOTNET] }
[DOTNET] Cookies received:
[DOTNET] ROUTEID: .node8
[DOTNET] B1SESSION: ac58e080-41bd-11ee-c000-000c2980395a-14752-8204
[DOTNET] Request URL: https://URL:50000/b1s/v1/Login
[DOTNET] HTTP Method: POST
[DOTNET] Response Status Code: OK
[DOTNET] Request Headers:
[DOTNET] Response Headers:
[DOTNET] Connection: Keep-Alive
[DOTNET] Date: Wed, 23 Aug 2023 14:02:19 GMT
[DOTNET] Keep-Alive: timeout=5, max=100
[DOTNET] Server: Apache
[DOTNET] Set-Cookie: B1SESSION=ac567d90-41bd-11ee-c000-000c2980395a-8440-7940;HttpOnly;;Secure;SameSite=None, ROUTEID=.node7; path=/;Secure;SameSite=None
[DOTNET] Vary: Accept-Encoding
[DOTNET] X-Android-Received-Millis: 1692799340837
[DOTNET] X-Android-Response-Source: NETWORK 200
[DOTNET] X-Android-Selected-Protocol: http/1.1
[DOTNET] X-Android-Sent-Millis: 1692799338028
[DOTNET] Request Body:
[DOTNET] {
[DOTNET] "odata.metadata" : "https://URL:50000/b1s/v1/$metadata#B1Sessions/@Element",
[DOTNET] "SessionId" : "ac567d90-41bd-11ee-c000-000c2980395a-8440-7940",
[DOTNET] "Version" : "1000210",
[DOTNET] "SessionTimeout" : 30
[DOTNET] }
[DOTNET] Cookies received:
[DOTNET] ROUTEID: .node7
[DOTNET] B1SESSION: ac567d90-41bd-11ee-c000-000c2980395a-8440-7940

@bgmulinari
Copy link
Owner

bgmulinari commented Aug 23, 2023

Thanks for the prompt response, @aaron-yb.

From what I could understand, this issue it not related to B1SLayer. It appears that for some reason, on iOS, the response headers are not received by Flurl and that results in an empty CookieJar in B1SLayer.

I appreciate your contribution, but although the workaround you provided in the PR #50 might solve this issue, it's generally not a good idea to omit the ROUTEID cookie because of Service Layer's load balancer, so I don't think it would be a good addition to B1SLayer.

I encourage you to open an issue in the Flurl repository so they can have a proper look on what's really happening in this case. You could even link this issue there.

If this proves to be an issue with Flurl and they release a fix, rest assured that I will update B1SLayer with the new Flurl version.

I will keep this issue open for now.

@bgmulinari
Copy link
Owner

@aaron-yb, could you do one more test, please?

Add the method bellow to the sample project an call it, then report back the console output.

This will perform the login request using just the native HttpClient instead of Flurl.

private async Task HttpClientLoginAsync()
{
    var handler = new HttpClientHandler();
    handler.ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => true;

    using (var client = new HttpClient(handler))
    {
        var requestUri = new Uri(new Uri(_serviceLayerUrl.EndsWith('/') ? _serviceLayerUrl : _serviceLayerUrl + "/"), "Login");
        var loginData = new { CompanyDB = _companyDB, UserName = _username, Password = _password };
        var content = new StringContent(JsonConvert.SerializeObject(loginData), Encoding.UTF8, "application/json");

        var response = await client.PostAsync(requestUri, content);
        var loginResponse = await response.Content.ReadAsStringAsync();

        Console.WriteLine("Response body:");
        Console.WriteLine(loginResponse);

        Console.WriteLine("Response Headers:");
        response.Headers?.ToList().ForEach(header => Console.WriteLine($"{header.Key}: {string.Join(",", header.Value)}"));
    }
}

@aaron-yb
Copy link
Author

Hi @bgmulinari , here you go:

2023-08-23 20:40:45.050977+0100 SLTest[31284:1761367] Response body:
2023-08-23 20:40:45.051188+0100 SLTest[31284:1761367] {
"odata.metadata" : "https://URL:50000/b1s/v1/$metadata#B1Sessions/@Element",
"SessionId" : "f1e3d0ae-41ec-11ee-c000-000c2980395a-12492-1548",
"Version" : "1000210",
"SessionTimeout" : 30
}
2023-08-23 20:40:45.051290+0100 SLTest[31284:1761367] Response Headers:
2023-08-23 20:40:45.054745+0100 SLTest[31284:1761367] Server: Apache
2023-08-23 20:40:45.054894+0100 SLTest[31284:1761367] Vary: Accept-Encoding
2023-08-23 20:40:45.054981+0100 SLTest[31284:1761367] Date: Wed, 23 Aug 2023 19:40:42 GMT
2023-08-23 20:40:45.055156+0100 SLTest[31284:1761367] Keep-Alive: timeout=5, max=100
2023-08-23 20:40:45.055258+0100 SLTest[31284:1761367] Connection: Keep-Alive
2023-08-23 20:40:45.055339+0100 SLTest[31284:1761367] Set-Cookie: B1SESSION=f1e3d0ae-41ec-11ee-c000-000c2980395a-12492-1548; Path=/b1s/v1; Domain=URL; Version=0; Discard; Secure; httponly,ROUTEID=.node2; Path=/; Domain=URL; Version=0; Discard; Secure

Guessing it's Flurl issue then

@bgmulinari
Copy link
Owner

Hi, @aaron-yb.

I see that in issue you opened in Flurl's repository, you were asked to perform another test, but didn't respond. Since this problem only seems to happen on iOS, it would be nice if you could help troubleshooting it there.

Are you still facing this problem?

@aaron-yb
Copy link
Author

Hi @bgmulinari , apologies. I have now updated the issue with the results.

The problem does still persist

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

No branches or pull requests

2 participants