-
Notifications
You must be signed in to change notification settings - Fork 164
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
RCORE-2253 Redirected user authenticated app requests cause user to be logged out and location is not updated #8011
base: master
Are you sure you want to change the base?
Conversation
…to mwb/remove-308-support
…redir_server cmd line tool
Pull Request Test Coverage Report for Build michael.wilkersonbarker_1387Details
💛 - Coveralls |
test/object-store/sync/app.cpp
Outdated
auto app_session = get_runtime_app_session(); | ||
|
||
// Skip this test if not using the redirect server | ||
if (!get_redirector()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why wouldn't we just explicitly create a redirecting server for this test regardless of whether it's enabled globally?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated test to define its own copy of the redirect server.
@@ -282,6 +283,9 @@ std::string get_real_base_url(); | |||
// your test is talking to. | |||
std::string get_admin_url(); | |||
|
|||
// Returns a reference to the redirector if it is enabled. | |||
std::optional<sync::RedirectingHttpServer>& get_redirector(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of pulling the redirect server in as an include dependency of anyone using the baas admin API why not just instantiate one where you need it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed the function in baas_admin_api.hpp
and the test is now declaring its own local redirect server.
, socket(service) | ||
, http_server(socket, logger) | ||
{ | ||
util::seed_prng_nondeterministically(random); // Throws |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need to seed this non-deterministically? If you just use the catch seed then you can actually replay the random values when you get an error?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I changed it to util::seed_prng_nondeterministically(random)
since I was having issues importing the Catch2 library for the redirect server command line tool. I reverted this change.
m_acceptor.open(m_endpoint.protocol()); | ||
m_acceptor.bind(m_endpoint); | ||
m_endpoint = m_acceptor.local_endpoint(); | ||
reset_state(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why does this type need to be stateful?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The redirect server has state for force_http_redirect()
, force_websocket_redirect()
, and the event hook callback - this resets those values back to default (such as for the end of a test).
No longer needed since the redirector is being created in the test where it is used.
network::Endpoint ep; | ||
if (listen_port != 0) { | ||
// If a port is provided, then configure the endpoint with this port num | ||
ep = network::Endpoint(network::make_address("0.0.0.0"), listen_port); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do we need to supply an explicit listen port?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was for the redirect server command line tool - removed.
}); | ||
}); | ||
} | ||
|
||
std::string trim_url(std::string_view str) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is this used for?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It trims the whitespace from both ends and the trailing /
from the redirect location URL in the constructor.
Since this was primarily for the redirect server command line tool, it has been removed.
return m_redirect_to_base_url; | ||
} | ||
|
||
std::string location_hostname() const |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this seems like something a caller of this type should already know?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This returns the values that the redirect server /location
endpoint will use so it can be displayed when using the redirect server command line tool - removed.
// value) and open a websocket conneciton to the actual server. | ||
// NOTE: the websocket will never connect if both http and websockets are | ||
// redirecting and will just keep getting the redirect close code. | ||
void force_websocket_redirect(bool force = true) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems like this is always called with an argument, do you need a default argument value here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not necessary (thought was that calling force_websocket_redirect()
would do just that - removed default
// NOTE: some http transport redirect implementations may strip the authorization | ||
// header from the request after it is redirected and the user will be logged out | ||
// from the client app as a result. | ||
void force_http_redirect(bool remote = true) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems like this is always called with an argument, do you need a default argument value here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not necessary (thought was that calling force_http_redirect()
would do just that - removed default
src/realm/object-store/sync/app.cpp
Outdated
@@ -853,7 +854,8 @@ void App::log_in_with_credentials(const AppCredentials& credentials, const std:: | |||
completion(user, error); | |||
}); | |||
}, | |||
false); | |||
// Update location to make sure we're talking to the latest server before logging in | |||
true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think getting logged out is the existing behavior that's always been there, do we really want to change that now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You do get logged out, but you will never be able to logged in again due to the authorization header being stripped from the /profile
request, which will log you out while you are trying to log in.
By updating the location when you attempt to log in, the client app will have the latest server location info and the login attempt should be successful, instead of failing when trying to query the user's profile.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So has redirection after a region migration always been totally broken? Like you cannot recover? Because I think all the SDKs HTTP implementations have been stripping the authorization header out when following a redirect this whole time...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's definitely an edge case and I doubt any customers have hit this specific use case:
they have to do a deployment change while the app is already running; and the user has to have already performed some operation that updated the location prior to the deployment change, such as logging in.
After the deployment change (and the requests start getting redirected), any app services request like updating the access token will log the user out and they won't be able to successfully log in again.
Fortunately, restarting the app will also resolve the issue, since it will require the location to be updated before sending any app services requests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We talked about this offline - instead of making every call to log_in_with_credentials()
pre-emptively request a location update, we're going to request a location update if the call to get the user's profile fails with a 401 unauthorized error. That way this error handling gets a bit slower in this edge case, but the behavior of all other log_in_with_credentials()
should stay the same.
…command line tool
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Most of your comments are around extra code that was added when running the redirect server using the command line tool. While it was helpful for creating the test, it sounds like we don't want the tool, so many of those changes in this PR have been reverted.
src/realm/object-store/sync/app.cpp
Outdated
@@ -853,7 +854,8 @@ void App::log_in_with_credentials(const AppCredentials& credentials, const std:: | |||
completion(user, error); | |||
}); | |||
}, | |||
false); | |||
// Update location to make sure we're talking to the latest server before logging in | |||
true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You do get logged out, but you will never be able to logged in again due to the authorization header being stripped from the /profile
request, which will log you out while you are trying to log in.
By updating the location when you attempt to log in, the client app will have the latest server location info and the login attempt should be successful, instead of failing when trying to query the user's profile.
test/object-store/sync/app.cpp
Outdated
auto app_session = get_runtime_app_session(); | ||
|
||
// Skip this test if not using the redirect server | ||
if (!get_redirector()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated test to define its own copy of the redirect server.
, socket(service) | ||
, http_server(socket, logger) | ||
{ | ||
util::seed_prng_nondeterministically(random); // Throws |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I changed it to util::seed_prng_nondeterministically(random)
since I was having issues importing the Catch2 library for the redirect server command line tool. I reverted this change.
return; | ||
} | ||
|
||
conn->http_server.async_receive_request([this, conn](HTTPRequest req, std::error_code ec) { | ||
static bool use_301 = false; // send 301 on first redirect response |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is actually switching between sending 301 and 308 after every message on line 353 to verify both redirect statuses are supported. It just initially starts out with 301.
I updated this to a member variable and added a comment about the code.
}); | ||
}); | ||
} | ||
|
||
std::string trim_url(std::string_view str) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It trims the whitespace from both ends and the trailing /
from the redirect location URL in the constructor.
Since this was primarily for the redirect server command line tool, it has been removed.
// NOTE: some http transport redirect implementations may strip the authorization | ||
// header from the request after it is redirected and the user will be logged out | ||
// from the client app as a result. | ||
void force_http_redirect(bool remote = true) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not necessary (thought was that calling force_http_redirect()
would do just that - removed default
// value) and open a websocket conneciton to the actual server. | ||
// NOTE: the websocket will never connect if both http and websockets are | ||
// redirecting and will just keep getting the redirect close code. | ||
void force_websocket_redirect(bool force = true) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not necessary (thought was that calling force_websocket_redirect()
would do just that - removed default
return m_redirect_to_base_url; | ||
} | ||
|
||
std::string location_hostname() const |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This returns the values that the redirect server /location
endpoint will use so it can be displayed when using the redirect server command line tool - removed.
@@ -282,6 +283,9 @@ std::string get_real_base_url(); | |||
// your test is talking to. | |||
std::string get_admin_url(); | |||
|
|||
// Returns a reference to the redirector if it is enabled. | |||
std::optional<sync::RedirectingHttpServer>& get_redirector(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed the function in baas_admin_api.hpp
and the test is now declaring its own local redirect server.
@jbreams should this just be closed or is there anything valuable to preserve? |
What, How & Why?
Updated the test http transport to enable HTTP redirection support in the Curl lib and added a test to verify the redirect operation. The
Authorization
header is not being included in the request when it is forwarded to the new location URL, leading the User to be logged out when a user authenticated appservices requests is performed. As a result, thehandle_auth_failure()
function was updated to request the location prior to refreshing the access token when an authenticated request fails on the first attempt, in order to ensure the client app is always connecting to the correct server hostname.Added a test that is run when using the redirect server, that verifies the HTTP and websocket redirect operations since this support has been removed from
App
and has been enabled in the Curl lib used by the test harness.The
RedirectingHttpServer
was updated to enable more control of the hostname and ws_hostname values returned by the location response and support was added to force a redirect response when an appservices request is received. An event hook was also added to be notified when a location request, http redirect, websocket redirect or error occurred.Fixes #8008, #8012
☑️ ToDos
[ ] C-API, if public C++ API changed[ ]bindgen/spec.yml
, if public C++ API changed