Skip to content

Commit bddc8b2

Browse files
authored
Implement iOS AppCheck providers (#1114)
Adds AppAttest, DeviceCheck, and DebugProvider. Adds an integration test to verify that the debug provider produces a token.
1 parent ee4bfd2 commit bddc8b2

21 files changed

+712
-23
lines changed

app_check/CMakeLists.txt

+2
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ set(android_SRCS
4545
set(ios_SRCS
4646
src/ios/app_check_ios.mm
4747
src/ios/app_check_ios.h
48+
src/ios/util_ios.mm
49+
src/ios/util_ios.h
4850
# Supported providers
4951
src/ios/app_attest_provider_ios.mm
5052
src/ios/debug_provider_ios.mm
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<Scheme
3+
LastUpgradeVersion = "1320"
4+
version = "1.3">
5+
<BuildAction
6+
parallelizeBuildables = "YES"
7+
buildImplicitDependencies = "YES">
8+
<BuildActionEntries>
9+
<BuildActionEntry
10+
buildForTesting = "YES"
11+
buildForRunning = "YES"
12+
buildForProfiling = "YES"
13+
buildForArchiving = "YES"
14+
buildForAnalyzing = "YES">
15+
<BuildableReference
16+
BuildableIdentifier = "primary"
17+
BlueprintIdentifier = "529226D11C85F68000C89379"
18+
BuildableName = "integration_test.app"
19+
BlueprintName = "integration_test"
20+
ReferencedContainer = "container:integration_test.xcodeproj">
21+
</BuildableReference>
22+
</BuildActionEntry>
23+
</BuildActionEntries>
24+
</BuildAction>
25+
<TestAction
26+
buildConfiguration = "Debug"
27+
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
28+
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
29+
shouldUseLaunchSchemeArgsEnv = "YES">
30+
<Testables>
31+
</Testables>
32+
</TestAction>
33+
<LaunchAction
34+
buildConfiguration = "Debug"
35+
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
36+
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
37+
launchStyle = "0"
38+
useCustomWorkingDirectory = "NO"
39+
ignoresPersistentStateOnLaunch = "NO"
40+
debugDocumentVersioning = "YES"
41+
debugServiceExtension = "internal"
42+
allowLocationSimulation = "YES">
43+
<BuildableProductRunnable
44+
runnableDebuggingMode = "0">
45+
<BuildableReference
46+
BuildableIdentifier = "primary"
47+
BlueprintIdentifier = "529226D11C85F68000C89379"
48+
BuildableName = "integration_test.app"
49+
BlueprintName = "integration_test"
50+
ReferencedContainer = "container:integration_test.xcodeproj">
51+
</BuildableReference>
52+
</BuildableProductRunnable>
53+
<EnvironmentVariables>
54+
<EnvironmentVariable
55+
key = "FIRAAppCheckDebugToken"
56+
value = "REPLACE_WITH_APP_CHECK_TOKEN"
57+
isEnabled = "YES">
58+
</EnvironmentVariable>
59+
</EnvironmentVariables>
60+
</LaunchAction>
61+
<ProfileAction
62+
buildConfiguration = "Release"
63+
shouldUseLaunchSchemeArgsEnv = "YES"
64+
savedToolIdentifier = ""
65+
useCustomWorkingDirectory = "NO"
66+
debugDocumentVersioning = "YES">
67+
<BuildableProductRunnable
68+
runnableDebuggingMode = "0">
69+
<BuildableReference
70+
BuildableIdentifier = "primary"
71+
BlueprintIdentifier = "529226D11C85F68000C89379"
72+
BuildableName = "integration_test.app"
73+
BlueprintName = "integration_test"
74+
ReferencedContainer = "container:integration_test.xcodeproj">
75+
</BuildableReference>
76+
</BuildableProductRunnable>
77+
</ProfileAction>
78+
<AnalyzeAction
79+
buildConfiguration = "Debug">
80+
</AnalyzeAction>
81+
<ArchiveAction
82+
buildConfiguration = "Release"
83+
revealArchiveInOrganizer = "YES">
84+
</ArchiveAction>
85+
</Scheme>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<Scheme
3+
LastUpgradeVersion = "1320"
4+
version = "1.3">
5+
<BuildAction
6+
parallelizeBuildables = "YES"
7+
buildImplicitDependencies = "YES">
8+
<BuildActionEntries>
9+
<BuildActionEntry
10+
buildForTesting = "YES"
11+
buildForRunning = "YES"
12+
buildForProfiling = "YES"
13+
buildForArchiving = "YES"
14+
buildForAnalyzing = "YES">
15+
<BuildableReference
16+
BuildableIdentifier = "primary"
17+
BlueprintIdentifier = "9F2ABA25267A4720001A35CE"
18+
BuildableName = "integration_test_tvos.app"
19+
BlueprintName = "integration_test_tvos"
20+
ReferencedContainer = "container:integration_test.xcodeproj">
21+
</BuildableReference>
22+
</BuildActionEntry>
23+
</BuildActionEntries>
24+
</BuildAction>
25+
<TestAction
26+
buildConfiguration = "Debug"
27+
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
28+
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
29+
shouldUseLaunchSchemeArgsEnv = "YES">
30+
<Testables>
31+
</Testables>
32+
</TestAction>
33+
<LaunchAction
34+
buildConfiguration = "Debug"
35+
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
36+
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
37+
launchStyle = "0"
38+
useCustomWorkingDirectory = "NO"
39+
ignoresPersistentStateOnLaunch = "NO"
40+
debugDocumentVersioning = "YES"
41+
debugServiceExtension = "internal"
42+
allowLocationSimulation = "YES">
43+
<BuildableProductRunnable
44+
runnableDebuggingMode = "0">
45+
<BuildableReference
46+
BuildableIdentifier = "primary"
47+
BlueprintIdentifier = "9F2ABA25267A4720001A35CE"
48+
BuildableName = "integration_test_tvos.app"
49+
BlueprintName = "integration_test_tvos"
50+
ReferencedContainer = "container:integration_test.xcodeproj">
51+
</BuildableReference>
52+
</BuildableProductRunnable>
53+
<EnvironmentVariables>
54+
<EnvironmentVariable
55+
key = "FIRAAppCheckDebugToken"
56+
value = "REPLACE_WITH_APP_CHECK_TOKEN"
57+
isEnabled = "YES">
58+
</EnvironmentVariable>
59+
</EnvironmentVariables>
60+
</LaunchAction>
61+
<ProfileAction
62+
buildConfiguration = "Release"
63+
shouldUseLaunchSchemeArgsEnv = "YES"
64+
savedToolIdentifier = ""
65+
useCustomWorkingDirectory = "NO"
66+
debugDocumentVersioning = "YES">
67+
<BuildableProductRunnable
68+
runnableDebuggingMode = "0">
69+
<BuildableReference
70+
BuildableIdentifier = "primary"
71+
BlueprintIdentifier = "9F2ABA25267A4720001A35CE"
72+
BuildableName = "integration_test_tvos.app"
73+
BlueprintName = "integration_test_tvos"
74+
ReferencedContainer = "container:integration_test.xcodeproj">
75+
</BuildableReference>
76+
</BuildableProductRunnable>
77+
</ProfileAction>
78+
<AnalyzeAction
79+
buildConfiguration = "Debug">
80+
</AnalyzeAction>
81+
<ArchiveAction
82+
buildConfiguration = "Release"
83+
revealArchiveInOrganizer = "YES">
84+
</ArchiveAction>
85+
</Scheme>

app_check/integration_test/src/integration_test.cc

+67-6
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,16 @@
1515
#include <inttypes.h>
1616

1717
#include <algorithm>
18+
#include <chrono>
1819
#include <cstdio>
1920
#include <cstdlib>
2021
#include <cstring>
2122
#include <ctime>
23+
#include <future>
2224
#include <map>
25+
#include <memory>
2326
#include <string>
27+
#include <thread>
2428

2529
#include "app_framework.h" // NOLINT
2630
#include "firebase/app.h"
@@ -61,6 +65,8 @@ using testing::Pair;
6165
using testing::UnorderedElementsAre;
6266

6367
const char kIntegrationTestRootPath[] = "integration_test_data";
68+
const std::chrono::milliseconds kGetTokenTimeout =
69+
std::chrono::milliseconds(5000);
6470

6571
class FirebaseAppCheckTest : public FirebaseTest {
6672
public:
@@ -335,15 +341,57 @@ TEST_F(FirebaseAppCheckTest, TestSignIn) {
335341
EXPECT_NE(auth_->current_user(), nullptr);
336342
}
337343

344+
TEST_F(FirebaseAppCheckTest, TestDebugProviderValidToken) {
345+
firebase::app_check::DebugAppCheckProviderFactory* factory =
346+
firebase::app_check::DebugAppCheckProviderFactory::GetInstance();
347+
#if FIREBASE_PLATFORM_IOS
348+
ASSERT_NE(factory, nullptr);
349+
InitializeApp();
350+
firebase::app_check::AppCheckProvider* provider =
351+
factory->CreateProvider(app_);
352+
ASSERT_NE(provider, nullptr);
353+
auto got_token_promise = std::make_shared<std::promise<void>>();
354+
auto token_callback{
355+
[&got_token_promise](firebase::app_check::AppCheckToken token,
356+
int error_code, const std::string& error_message) {
357+
EXPECT_EQ(firebase::app_check::kAppCheckErrorNone, error_code);
358+
EXPECT_EQ("", error_message);
359+
EXPECT_NE(0, token.expire_time_millis);
360+
EXPECT_NE("", token.token);
361+
got_token_promise->set_value();
362+
}};
363+
provider->GetToken(token_callback);
364+
auto got_token_future = got_token_promise->get_future();
365+
ASSERT_EQ(std::future_status::ready,
366+
got_token_future.wait_for(kGetTokenTimeout));
367+
#else
368+
EXPECT_EQ(factory, nullptr);
369+
#endif
370+
}
371+
338372
TEST_F(FirebaseAppCheckTest, TestAppAttestProvider) {
339373
firebase::app_check::AppAttestProviderFactory* factory =
340374
firebase::app_check::AppAttestProviderFactory::GetInstance();
341375
#if FIREBASE_PLATFORM_IOS
342-
EXPECT_NE(factory, nullptr);
376+
ASSERT_NE(factory, nullptr);
343377
InitializeApp();
344378
firebase::app_check::AppCheckProvider* provider =
345379
factory->CreateProvider(app_);
346-
EXPECT_NE(provider, nullptr);
380+
ASSERT_NE(provider, nullptr);
381+
auto got_token_promise = std::make_shared<std::promise<void>>();
382+
auto token_callback{
383+
[&got_token_promise](firebase::app_check::AppCheckToken token,
384+
int error_code, const std::string& error_message) {
385+
EXPECT_EQ(firebase::app_check::kAppCheckErrorUnsupportedProvider,
386+
error_code);
387+
EXPECT_NE("", error_message);
388+
EXPECT_EQ("", token.token);
389+
got_token_promise->set_value();
390+
}};
391+
provider->GetToken(token_callback);
392+
auto got_token_future = got_token_promise->get_future();
393+
ASSERT_EQ(std::future_status::ready,
394+
got_token_future.wait_for(kGetTokenTimeout));
347395
#else
348396
EXPECT_EQ(factory, nullptr);
349397
#endif
@@ -353,11 +401,24 @@ TEST_F(FirebaseAppCheckTest, TestDeviceCheckProvider) {
353401
firebase::app_check::DeviceCheckProviderFactory* factory =
354402
firebase::app_check::DeviceCheckProviderFactory::GetInstance();
355403
#if FIREBASE_PLATFORM_IOS
356-
EXPECT_NE(factory, nullptr);
404+
ASSERT_NE(factory, nullptr);
357405
InitializeApp();
358406
firebase::app_check::AppCheckProvider* provider =
359407
factory->CreateProvider(app_);
360-
EXPECT_NE(provider, nullptr);
408+
ASSERT_NE(provider, nullptr);
409+
auto got_token_promise = std::make_shared<std::promise<void>>();
410+
auto token_callback{
411+
[&got_token_promise](firebase::app_check::AppCheckToken token,
412+
int error_code, const std::string& error_message) {
413+
EXPECT_EQ(firebase::app_check::kAppCheckErrorUnknown, error_code);
414+
EXPECT_NE("", error_message);
415+
EXPECT_EQ("", token.token);
416+
got_token_promise->set_value();
417+
}};
418+
provider->GetToken(token_callback);
419+
auto got_token_future = got_token_promise->get_future();
420+
ASSERT_EQ(std::future_status::ready,
421+
got_token_future.wait_for(kGetTokenTimeout));
361422
#else
362423
EXPECT_EQ(factory, nullptr);
363424
#endif
@@ -367,7 +428,7 @@ TEST_F(FirebaseAppCheckTest, TestPlayIntegrityProvider) {
367428
firebase::app_check::PlayIntegrityProviderFactory* factory =
368429
firebase::app_check::PlayIntegrityProviderFactory::GetInstance();
369430
#if FIREBASE_PLATFORM_ANDROID
370-
EXPECT_NE(factory, nullptr);
431+
ASSERT_NE(factory, nullptr);
371432
InitializeApp();
372433
firebase::app_check::AppCheckProvider* provider =
373434
factory->CreateProvider(app_);
@@ -381,7 +442,7 @@ TEST_F(FirebaseAppCheckTest, TestSafetyNetProvider) {
381442
firebase::app_check::SafetyNetProviderFactory* factory =
382443
firebase::app_check::SafetyNetProviderFactory::GetInstance();
383444
#if FIREBASE_PLATFORM_ANDROID
384-
EXPECT_NE(factory, nullptr);
445+
ASSERT_NE(factory, nullptr);
385446
InitializeApp();
386447
firebase::app_check::AppCheckProvider* provider =
387448
factory->CreateProvider(app_);

app_check/src/include/firebase/app_check/app_attest_provider.h

+6
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@
2020
namespace firebase {
2121
namespace app_check {
2222

23+
namespace internal {
24+
class AppAttestProviderFactoryInternal;
25+
}
26+
2327
/// Implementation of an {@link AppCheckProviderFactory} that builds
2428
/// AppAttestProviders. This is the default implementation.
2529
class AppAttestProviderFactory : public AppCheckProviderFactory {
@@ -37,6 +41,8 @@ class AppAttestProviderFactory : public AppCheckProviderFactory {
3741

3842
private:
3943
AppAttestProviderFactory();
44+
45+
internal::AppAttestProviderFactoryInternal* internal_;
4046
};
4147

4248
} // namespace app_check

app_check/src/include/firebase/app_check/debug_provider.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,12 @@ class DebugAppCheckProviderFactoryInternal;
3434
/// NOTE: Do not use the debug provider in applications used by real users.
3535
class DebugAppCheckProviderFactory : public AppCheckProviderFactory {
3636
public:
37+
~DebugAppCheckProviderFactory() override;
38+
3739
/// Gets an instance of this class for installation into a
3840
/// firebase::app_check::AppCheck instance.
3941
static DebugAppCheckProviderFactory* GetInstance();
4042

41-
virtual ~DebugAppCheckProviderFactory();
42-
4343
/// Gets the AppCheckProvider associated with the given
4444
/// {@link App} instance, or creates one if none
4545
/// already exists.

app_check/src/include/firebase/app_check/device_check_provider.h

+6
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@
2020
namespace firebase {
2121
namespace app_check {
2222

23+
namespace internal {
24+
class DeviceCheckProviderFactoryInternal;
25+
}
26+
2327
/// Implementation of an {@link AppCheckProviderFactory} that builds
2428
/// DeviceCheckProviders. This is the default implementation.
2529
class DeviceCheckProviderFactory : public AppCheckProviderFactory {
@@ -37,6 +41,8 @@ class DeviceCheckProviderFactory : public AppCheckProviderFactory {
3741

3842
private:
3943
DeviceCheckProviderFactory();
44+
45+
internal::DeviceCheckProviderFactoryInternal* internal_;
4046
};
4147

4248
} // namespace app_check

0 commit comments

Comments
 (0)