Repository navigation
Pierrep/bcloud 14842 #343
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
base: develop
Are you sure you want to change the base?
Pierrep/bcloud 14842 #343
Changes from all commits
c96933d
1f60688
3632a82
3e295b3
c5c9928
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,6 +9,9 @@ | |
|
|
||
| #include <string> | ||
| #include <map> | ||
| #include <functional> | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we not use the existing callback objects?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Authentication still uses the existing IServerCallback. These new callbacks handle fetching the App Check token before authentication is sent, so their inputs differ from a server response. We can follow the existing callback-object pattern with dedicated App Check interfaces instead of std::function. |
||
| #include <memory> | ||
|
michaelc-bitheads marked this conversation as resolved.
|
||
| #include <vector> | ||
| #include "json/json.h" | ||
| #include "braincloud/BrainCloudTypes.h" | ||
| #include "braincloud/AuthenticationType.h" | ||
|
|
@@ -29,6 +32,35 @@ namespace BrainCloud | |
| // internal use only | ||
| void setClientLib(const char* lib); | ||
|
|
||
| /** | ||
| * Sets the caller-supplied Firebase App Check token for subsequent authenticate requests. | ||
| * The token is copied and retained in memory until replaced or cleared with an empty string. | ||
| * The host app is responsible for obtaining and refreshing it, including before reconnects. | ||
| * Call on the same thread as authentication; already queued requests are not updated. | ||
| * No token validation or Firebase integration is performed by this SDK. | ||
| * @param token Opaque App Check token, or an empty string to omit it from requests. | ||
| */ | ||
| void setAppCheckToken(const std::string& token); | ||
|
|
||
| typedef std::function<void(const std::string& token, const std::string& error)> AppCheckTokenCompletion; | ||
| typedef std::function<void(AppCheckTokenCompletion)> AppCheckTokenProvider; | ||
|
|
||
| /** | ||
| * Resolves a token for every authentication, including retries and automatic reconnects. | ||
| * Takes precedence over the setter; an empty provider restores setter behavior. | ||
| * Called on the authentication thread. Completion may run on any thread; only its | ||
| * first result is used. An error or empty token fails authentication without sending. | ||
| * Results and a 30-second timeout are processed by REST/ALL runCallbacks(). | ||
| * Configure on the SDK thread. Reset/destruction discards pending results without callbacks. | ||
| * The provider is retained until replaced or the client is destroyed; avoid ownership cycles. | ||
| * No Firebase dependency is required. | ||
| */ | ||
| void setAppCheckTokenProvider(AppCheckTokenProvider provider); | ||
|
|
||
| // Internal: called by BrainCloudClient on its callback thread. | ||
| void runAppCheckCallbacks(); | ||
| void cancelPendingAppCheckRequests(); | ||
|
|
||
| /** | ||
| * Initialize - initializes the identity service with a saved | ||
| * anonymous installation id and most recently used profile id | ||
|
|
@@ -475,6 +507,11 @@ namespace BrainCloud | |
| void authenticate(const char * externalId, const char * authenticationToken, AuthenticationType authenticationType, const char * externalAuthName, bool forceCreate, const std::string &extraJson, IServerCallback * callback); | ||
|
|
||
| private: | ||
| std::string _appCheckToken; | ||
| struct PendingAppCheck; | ||
| AppCheckTokenProvider _appCheckTokenProvider; | ||
| std::vector<std::shared_ptr<PendingAppCheck> > _pendingAppCheck; | ||
|
|
||
| struct PreviousAuthParams | ||
| { | ||
| std::string externalId; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,9 @@ | ||
| // Copyright 2026 bitHeads, Inc. All Rights Reserved. | ||
|
|
||
| #include "braincloud/BrainCloudAuthentication.h" | ||
| #include <mutex> | ||
| #include <chrono> | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Surely we can track the time more simply than having to spin up a new thread?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No new thread is created here. We store the start time with std::chrono::steady_clock and check elapsed time in the existing callback loop. The mutex protects the result if the token provider completes on another thread. We could use an existing SDK timing utility for consistency, but the timeout already works by polling. |
||
| #include "braincloud/reason_codes.h" | ||
|
|
||
| #include "braincloud/BrainCloudClient.h" | ||
| #include "braincloud/IServerCallback.h" | ||
|
|
@@ -29,6 +32,76 @@ namespace BrainCloud { | |
| _clientLib = lib; | ||
| } | ||
|
|
||
| void BrainCloudAuthentication::setAppCheckToken(const std::string& token) | ||
| { | ||
| _appCheckToken = token; | ||
| } | ||
|
|
||
| struct BrainCloudAuthentication::PendingAppCheck | ||
| { | ||
| std::mutex mutex; | ||
| bool completed = false; | ||
| std::string token, error; | ||
| Json::Value message; | ||
| IServerCallback* callback = nullptr; | ||
| std::chrono::steady_clock::time_point started = std::chrono::steady_clock::now(); | ||
| }; | ||
|
|
||
| void BrainCloudAuthentication::setAppCheckTokenProvider(AppCheckTokenProvider provider) | ||
| { | ||
| _appCheckTokenProvider = provider; | ||
| } | ||
|
|
||
| void BrainCloudAuthentication::cancelPendingAppCheckRequests() | ||
| { | ||
| _pendingAppCheck.clear(); | ||
| } | ||
|
|
||
| void BrainCloudAuthentication::runAppCheckCallbacks() | ||
| { | ||
| // Remove each result before invoking application code, which can reset the client. | ||
| for (size_t i = 0; i < _pendingAppCheck.size();) | ||
| { | ||
| auto pending = _pendingAppCheck[i]; | ||
| std::string token, error; | ||
| { | ||
| std::lock_guard<std::mutex> lock(pending->mutex); | ||
| if (!pending->completed) | ||
| { | ||
| if (std::chrono::steady_clock::now() - pending->started < std::chrono::seconds(30)) | ||
| { | ||
| ++i; | ||
| continue; | ||
| } | ||
| pending->completed = true; | ||
| pending->error = "App Check token provider timed out"; | ||
| } | ||
| token = pending->token; | ||
| error = pending->error; | ||
| } | ||
| _pendingAppCheck.erase(_pendingAppCheck.begin() + i); | ||
| if (!error.empty() || token.empty()) | ||
| { | ||
| if (pending->callback) | ||
| { | ||
| Json::Value response; | ||
| response["status"] = 400; | ||
| response["reason_code"] = CLIENT_APP_CHECK_TOKEN_ERROR; | ||
| response["status_message"] = error.empty() ? "App Check token provider returned an empty token" : error; | ||
| pending->callback->serverError(ServiceName::AuthenticateV2, ServiceOperation::Authenticate, | ||
| 400, CLIENT_APP_CHECK_TOKEN_ERROR, Json::FastWriter().write(response)); | ||
| } | ||
| } | ||
| else | ||
| { | ||
| pending->message[OperationParam::AuthenticateServiceAuthenticateAppCheckToken.getValue()] = token; | ||
| m_client->sendRequest(new ServerCall(ServiceName::AuthenticateV2, | ||
| ServiceOperation::Authenticate, pending->message, pending->callback)); | ||
| } | ||
| i = 0; | ||
| } | ||
| } | ||
|
|
||
| void BrainCloudAuthentication::initialize(const char * profileId, const char * anonymousId) | ||
| { | ||
| _profileId = profileId; | ||
|
|
@@ -339,6 +412,32 @@ namespace BrainCloud { | |
| message[OperationParam::AuthenticateServiceAuthenticateExtraJson.getValue()] = JsonUtil::jsonStringToValue(extraJson); | ||
| } | ||
|
|
||
| if (_appCheckTokenProvider) | ||
| { | ||
| auto pending = std::make_shared<PendingAppCheck>(); | ||
| pending->message = message; | ||
| pending->callback = callback; | ||
| _pendingAppCheck.push_back(pending); | ||
| std::weak_ptr<PendingAppCheck> weakPending = pending; | ||
| // Capture no client pointer: late completions after reset/destruction are harmless. | ||
| auto provider = _appCheckTokenProvider; | ||
| provider([weakPending](const std::string& token, const std::string& error) { | ||
| auto result = weakPending.lock(); | ||
| if (!result) return; | ||
| std::lock_guard<std::mutex> lock(result->mutex); | ||
| if (result->completed) return; | ||
| result->token = token; | ||
| result->error = error; | ||
| result->completed = true; | ||
| }); | ||
| return; | ||
| } | ||
|
|
||
| if (!_appCheckToken.empty()) | ||
| { | ||
| message[OperationParam::AuthenticateServiceAuthenticateAppCheckToken.getValue()] = _appCheckToken; | ||
| } | ||
|
|
||
| ServerCall * sc = new ServerCall(ServiceName::AuthenticateV2, ServiceOperation::Authenticate, message, callback); | ||
| m_client->sendRequest(sc); | ||
| } | ||
|
|
||
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 have the same verbosity concern here too.