Skip to content

Commit 8785d02

Browse files
authored
Add desktop Debug provider for App Check (#1126)
* Add desktop Debug provider for App Check * File formatting * Fix lint errors * Address feedback * Update debug_token_request.h
1 parent f35b59c commit 8785d02

File tree

7 files changed

+293
-3
lines changed

7 files changed

+293
-3
lines changed

app_check/CMakeLists.txt

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,13 +57,50 @@ set(ios_SRCS
5757
src/stub/safety_net_provider_stub.cc
5858
)
5959

60+
# Flatbuffer resource files used by desktop
61+
binary_to_array("debug_token_request_resource"
62+
"${CMAKE_CURRENT_LIST_DIR}/src/desktop/debug_token_request.fbs"
63+
"firebase::app_check"
64+
"${FIREBASE_GEN_FILE_DIR}/app_check")
65+
binary_to_array("token_response_resource"
66+
"${CMAKE_CURRENT_LIST_DIR}/src/desktop/token_response.fbs"
67+
"firebase::app_check"
68+
"${FIREBASE_GEN_FILE_DIR}/app_check")
69+
70+
# Build the generated header from the flatbuffer schema files.
71+
set(FLATBUFFERS_FLATC_SCHEMA_EXTRA_ARGS
72+
"--no-union-value-namespacing"
73+
"--gen-object-api"
74+
"--cpp-ptr-type" "flatbuffers::unique_ptr")
75+
set(flatbuffer_schemas
76+
${CMAKE_CURRENT_LIST_DIR}/src/desktop/debug_token_request.fbs
77+
${CMAKE_CURRENT_LIST_DIR}/src/desktop/token_response.fbs)
78+
# Because of a bug in the version of Flatbuffers we are pinned to,
79+
# additional flags need to be set.
80+
set(FLATC_ARGS "${FLATBUFFERS_FLATC_SCHEMA_EXTRA_ARGS}")
81+
build_flatbuffers("${flatbuffer_schemas}"
82+
""
83+
"app_check_generate_fbs"
84+
"${FIREBASE_FLATBUFFERS_DEPENDENCIES}"
85+
"${FIREBASE_GEN_FILE_DIR}/app_check"
86+
""
87+
"")
88+
6089
# Source files used by the desktop implementation.
6190
set(desktop_SRCS
6291
src/desktop/app_check_desktop.cc
6392
src/desktop/app_check_desktop.h
6493
# Supported providers
6594
src/desktop/debug_provider_desktop.cc
6695
src/desktop/debug_provider_desktop.h
96+
src/desktop/debug_token_request.h
97+
${debug_token_request_resource_source}
98+
${debug_token_request_resource_header}
99+
${FIREBASE_GEN_FILE_DIR}/app_check/debug_token_request_generated.h
100+
src/desktop/token_response.h
101+
${token_response_resource_source}
102+
${token_response_resource_header}
103+
${FIREBASE_GEN_FILE_DIR}/app_check/token_response_generated.h
67104
# Unsupported providers
68105
src/stub/app_attest_provider_stub.cc
69106
src/stub/device_check_provider_stub.cc

app_check/src/desktop/debug_provider_desktop.cc

Lines changed: 102 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,118 @@
1414

1515
#include "app_check/src/desktop/debug_provider_desktop.h"
1616

17+
#include <map>
18+
#include <string>
19+
#include <utility>
20+
21+
#include "app/rest/response.h"
22+
#include "app/rest/transport_builder.h"
23+
#include "app/rest/transport_curl.h"
24+
#include "app/rest/util.h"
25+
#include "app/src/log.h"
26+
#include "app/src/scheduler.h"
27+
#include "app_check/src/desktop/debug_token_request.h"
28+
#include "app_check/src/desktop/token_response.h"
1729
#include "firebase/app_check/debug_provider.h"
1830

1931
namespace firebase {
2032
namespace app_check {
2133
namespace internal {
2234

23-
DebugAppCheckProviderFactoryInternal::DebugAppCheckProviderFactoryInternal() {}
35+
class DebugAppCheckProvider : public AppCheckProvider {
36+
public:
37+
explicit DebugAppCheckProvider(App* app);
38+
~DebugAppCheckProvider() override;
39+
40+
void GetToken(std::function<void(AppCheckToken, int, const std::string&)>
41+
completion_callback) override;
42+
43+
private:
44+
App* app_;
45+
46+
scheduler::Scheduler scheduler_;
47+
};
48+
49+
DebugAppCheckProvider::DebugAppCheckProvider(App* app)
50+
: app_(app), scheduler_() {
51+
firebase::rest::util::Initialize();
52+
firebase::rest::InitTransportCurl();
53+
}
54+
55+
DebugAppCheckProvider::~DebugAppCheckProvider() {
56+
firebase::rest::CleanupTransportCurl();
57+
firebase::rest::util::Terminate();
58+
}
59+
60+
// Performs the given rest request, and calls the callback based on the
61+
// response.
62+
void GetTokenAsync(SharedPtr<DebugTokenRequest> request,
63+
std::function<void(AppCheckToken, int, const std::string&)>
64+
completion_callback) {
65+
TokenResponse response;
66+
firebase::rest::CreateTransport()->Perform(*request, &response);
2467

25-
DebugAppCheckProviderFactoryInternal::~DebugAppCheckProviderFactoryInternal() {}
68+
if (response.status() == firebase::rest::util::HttpSuccess) {
69+
// Call the callback with the response token
70+
AppCheckToken token;
71+
token.token = std::move(response.token());
72+
// Expected response is in seconds
73+
int64_t extra_time = strtol(response.ttl().c_str(), nullptr, 10);
74+
token.expire_time_millis = (response.fetch_time() + extra_time) * 1000;
75+
completion_callback(token, kAppCheckErrorNone, "");
76+
} else {
77+
// Create an error message, and pass it along instead.
78+
AppCheckToken token;
79+
char error_message[1000];
80+
snprintf(error_message, sizeof(error_message),
81+
"The server responded with an error.\n"
82+
"HTTP status code: %d \n"
83+
"Response body: %s\n",
84+
response.status(), response.GetBody());
85+
completion_callback(token, kAppCheckErrorUnknown, error_message);
86+
}
87+
}
88+
89+
void DebugAppCheckProvider::GetToken(
90+
std::function<void(AppCheckToken, int, const std::string&)>
91+
completion_callback) {
92+
// Identify the user's debug token
93+
// TODO(amaurice): For now uses an environment variable, but should use other
94+
// options.
95+
const char* debug_token = std::getenv("APP_CHECK_DEBUG_TOKEN");
96+
97+
// Exchange debug token with the backend to get a proper attestation token.
98+
auto request = MakeShared<DebugTokenRequest>(app_);
99+
request->SetDebugToken(debug_token);
100+
101+
// Use an async call, since we don't want to block on the server response.
102+
auto async_call =
103+
callback::NewCallback(GetTokenAsync, request, completion_callback);
104+
scheduler_.Schedule(async_call);
105+
}
106+
107+
DebugAppCheckProviderFactoryInternal::DebugAppCheckProviderFactoryInternal()
108+
: provider_map_() {}
109+
110+
DebugAppCheckProviderFactoryInternal::~DebugAppCheckProviderFactoryInternal() {
111+
// Clear the map
112+
for (auto it : provider_map_) {
113+
delete it.second;
114+
}
115+
provider_map_.clear();
116+
}
26117

27118
AppCheckProvider* DebugAppCheckProviderFactoryInternal::CreateProvider(
28119
App* app) {
29-
return nullptr;
120+
// Check the map
121+
std::map<App*, AppCheckProvider*>::iterator it = provider_map_.find(app);
122+
if (it != provider_map_.end()) {
123+
return it->second;
124+
}
125+
// Create a new provider and cache it
126+
AppCheckProvider* provider = new DebugAppCheckProvider(app);
127+
provider_map_[app] = provider;
128+
return provider;
30129
}
31130

32131
} // namespace internal

app_check/src/desktop/debug_provider_desktop.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
#ifndef FIREBASE_APP_CHECK_SRC_DESKTOP_DEBUG_PROVIDER_DESKTOP_H_
1616
#define FIREBASE_APP_CHECK_SRC_DESKTOP_DEBUG_PROVIDER_DESKTOP_H_
1717

18+
#include <map>
19+
1820
#include "firebase/app_check.h"
1921

2022
namespace firebase {
@@ -28,6 +30,9 @@ class DebugAppCheckProviderFactoryInternal : public AppCheckProviderFactory {
2830
virtual ~DebugAppCheckProviderFactoryInternal();
2931

3032
AppCheckProvider* CreateProvider(App* app) override;
33+
34+
private:
35+
std::map<App*, AppCheckProvider*> provider_map_;
3136
};
3237

3338
} // namespace internal
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright 2022 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
namespace firebase.app_check.fbs;
16+
17+
table DebugTokenRequest {
18+
debug_token:string;
19+
}
20+
21+
root_type DebugTokenRequest;
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// Copyright 2022 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#ifndef FIREBASE_APP_CHECK_SRC_DESKTOP_DEBUG_TOKEN_REQUEST_H_
16+
#define FIREBASE_APP_CHECK_SRC_DESKTOP_DEBUG_TOKEN_REQUEST_H_
17+
18+
#include <string>
19+
#include <utility>
20+
21+
#include "app/rest/request_json.h"
22+
#include "app_check/debug_token_request_generated.h"
23+
#include "app_check/debug_token_request_resource.h"
24+
#include "firebase/app.h"
25+
26+
namespace firebase {
27+
namespace app_check {
28+
namespace internal {
29+
30+
// The server url to exchange the debug token with for a attestation token.
31+
static const char* kDebugTokenRequestServerUrlBase =
32+
"https://firebaseappcheck.googleapis.com/v1beta/projects/";
33+
// The header used to pass the project's API key.
34+
static const char* kDebugTokenRequestHeader = "X-Goog-Api-Key";
35+
36+
// Request to exchange the user provided Debug Token with a valid attestation
37+
// token.
38+
class DebugTokenRequest
39+
: public firebase::rest::RequestJson<fbs::DebugTokenRequest,
40+
fbs::DebugTokenRequestT> {
41+
public:
42+
explicit DebugTokenRequest(App* app)
43+
: RequestJson(debug_token_request_resource_data) {
44+
std::string server_url(kDebugTokenRequestServerUrlBase);
45+
server_url.append(app->options().project_id());
46+
server_url.append("/apps/");
47+
server_url.append(app->options().app_id());
48+
server_url.append(":exchangeDebugToken");
49+
set_url(server_url.c_str());
50+
51+
add_header(kDebugTokenRequestHeader, app->options().api_key());
52+
}
53+
54+
void SetDebugToken(std::string debug_token) {
55+
application_data_->debug_token = std::move(debug_token);
56+
UpdatePostFields();
57+
}
58+
};
59+
60+
} // namespace internal
61+
} // namespace app_check
62+
} // namespace firebase
63+
64+
#endif // FIREBASE_APP_CHECK_SRC_DESKTOP_DEBUG_TOKEN_REQUEST_H_
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright 2022 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
namespace firebase.app_check.fbs;
16+
17+
table TokenResponse {
18+
token:string;
19+
ttl:string;
20+
}
21+
22+
root_type TokenResponse;
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright 2022 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#ifndef FIREBASE_APP_CHECK_SRC_DESKTOP_TOKEN_RESPONSE_H_
16+
#define FIREBASE_APP_CHECK_SRC_DESKTOP_TOKEN_RESPONSE_H_
17+
18+
#include <string>
19+
20+
#include "app/rest/response_json.h"
21+
#include "app_check/token_response_generated.h"
22+
#include "app_check/token_response_resource.h"
23+
#include "firebase/app.h"
24+
25+
namespace firebase {
26+
namespace app_check {
27+
namespace internal {
28+
29+
class TokenResponse : public firebase::rest::ResponseJson<fbs::TokenResponse,
30+
fbs::TokenResponseT> {
31+
public:
32+
TokenResponse() : ResponseJson(token_response_resource_data) {}
33+
34+
const std::string& token() { return application_data_->token; }
35+
const std::string& ttl() { return application_data_->ttl; }
36+
};
37+
38+
} // namespace internal
39+
} // namespace app_check
40+
} // namespace firebase
41+
42+
#endif // FIREBASE_APP_CHECK_SRC_DESKTOP_TOKEN_RESPONSE_H_

0 commit comments

Comments
 (0)