Skip to content

Commit 72f025a

Browse files
Googlera-maurice
authored andcommitted
FederatedAuthProvider signature changes to Firebase User and Auth for C++ non-mobile implementations. The implementation uses a handler/completion model where authenticated users may be simulated for testing purposes. Tests for Auth::SignInWithProvider. Tests for User::LinkWithProvider and User::ReauthenticateWithProvider will come in a subsequent CL.
PiperOrigin-RevId: 259936033
1 parent 0f7d6ab commit 72f025a

17 files changed

+726
-14
lines changed

auth/src/data.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ enum AuthApiFunction {
3737
kAuthFn_SignInAndRetrieveDataWithCredential,
3838
kAuthFn_SignInAnonymously,
3939
kAuthFn_SignInWithEmailAndPassword,
40+
kAuthFn_SignInWithProvider,
41+
kAuthFn_LinkWithProvider,
42+
kAuthFn_ReauthenticateWithProvider,
4043
kAuthFn_CreateUserWithEmailAndPassword,
4144
kAuthFn_SendPasswordResetEmail,
4245

auth/src/desktop/auth_desktop.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,12 @@ Future<User*> Auth::SignInWithCredential(const Credential& credential) {
283283
credential.impl_);
284284
}
285285

286+
Future<SignInResult> Auth::SignInWithProvider(
287+
FederatedAuthProvider* provider) {
288+
FIREBASE_ASSERT_RETURN(Future<SignInResult>(), provider);
289+
return provider->SignIn(auth_data_);
290+
}
291+
286292
Future<User*> Auth::SignInAnonymously() {
287293
Promise<User*> promise(&auth_data_->future_impl, kAuthFn_SignInAnonymously);
288294

auth/src/desktop/auth_desktop.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,27 @@ class IdTokenRefreshThread {
9999
Auth* auth;
100100
};
101101

102+
// Facilitates completion of Federated Auth operations on non-mobile
103+
// environments. Custom application logic fulfills the authentication request
104+
// and uses this completion handle in callbacks. Our callbacks observe
105+
// contextual information in these handles to access to trigger the
106+
// corresponding Future<SignInResult>.
107+
struct AuthCompletionHandle {
108+
public:
109+
AuthCompletionHandle(const SafeFutureHandle<SignInResult>& handle,
110+
AuthData* auth_data)
111+
: future_handle(handle), auth_data(auth_data) {}
112+
113+
AuthCompletionHandle() = delete;
114+
115+
virtual ~AuthCompletionHandle() {
116+
auth_data = nullptr;
117+
}
118+
119+
SafeFutureHandle<SignInResult> future_handle;
120+
AuthData* auth_data;
121+
};
122+
102123
// The desktop-specific Auth implementation.
103124
struct AuthImpl {
104125
AuthImpl() : async_sem(0), active_async_calls(0) {}
@@ -121,6 +142,9 @@ struct AuthImpl {
121142

122143
// Serializes all REST call from this object.
123144
scheduler::Scheduler scheduler_;
145+
146+
// Synchronization primative for tracking sate of FederatedAuth futures.
147+
Mutex provider_mutex;
124148
};
125149

126150
// Constant, describing how often we automatically fetch a new auth token.

auth/src/desktop/auth_providers/facebook_auth_provider.cc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,10 @@ Credential FacebookAuthProvider::GetCredential(const char* const access_token) {
2929
new CredentialImpl{new FacebookAuthCredential(access_token)}};
3030
}
3131

32+
// static
33+
const char* FacebookAuthProvider::GetProviderId() {
34+
return "facebook.com";
35+
}
36+
3237
} // namespace auth
3338
} // namespace firebase
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
// Copyright 2019 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+
#include "app/src/include/firebase/future.h"
16+
#include "auth/src/desktop/auth_desktop.h"
17+
#include "auth/src/desktop/sign_in_flow.h"
18+
19+
#include "app/src/mutex.h"
20+
#include "auth/src/include/firebase/auth.h"
21+
#include "auth/src/include/firebase/auth/types.h"
22+
23+
namespace firebase {
24+
namespace auth {
25+
26+
#ifdef INTERNAL_EXPERIMENTAL
27+
28+
FederatedOAuthProvider::FederatedOAuthProvider() { }
29+
30+
FederatedOAuthProvider::~FederatedOAuthProvider() {
31+
handler_ = nullptr;
32+
}
33+
34+
void FederatedOAuthProvider::SetAuthHandler(AuthHandler* handler) {
35+
handler_ = handler;
36+
}
37+
38+
void FederatedOAuthProvider::SetProviderData(
39+
const FederatedOAuthProviderData& provider_data) {
40+
provider_data_ = provider_data;
41+
}
42+
43+
// Helper function which returns a Future for the corresponding auth
44+
// api function. Or, if that operation is already in progress,
45+
// returns a Future in an error state instead, thereby blocking duplicate
46+
// operations on the same auth instance.
47+
Future<SignInResult> CreateAuthFuture(AuthData* auth_data,
48+
AuthApiFunction api_function) {
49+
FIREBASE_ASSERT_RETURN(Future<SignInResult>(), auth_data);
50+
auto auth_impl = static_cast<AuthImpl*>(auth_data->auth_impl);
51+
MutexLock lock(auth_impl->provider_mutex);
52+
auto future_base =
53+
auth_data->future_impl.LastResult(api_function);
54+
if (future_base.status() == kFutureStatusPending) {
55+
// There's an operation in progress. Create and return a new failed
56+
// future.
57+
SafeFutureHandle<SignInResult> handle =
58+
auth_data->future_impl.SafeAlloc<SignInResult>(api_function);
59+
auth_data->future_impl.CompleteWithResult(
60+
handle, kAuthErrorFederatedProviderAreadyInUse,
61+
"Provider operation already in progress.",
62+
/*SignInResult=*/{});
63+
return MakeFuture(&auth_data->future_impl, handle);
64+
} else if (future_base.status() == kFutureStatusInvalid) {
65+
// initialize the future.
66+
SafeFutureHandle<SignInResult> handle =
67+
auth_data->future_impl.SafeAlloc<SignInResult>(api_function);
68+
Future<SignInResult> result = MakeFuture(&auth_data->future_impl,
69+
SafeFutureHandle<SignInResult>(handle));
70+
auto future_base =
71+
auth_data->future_impl.LastResult(api_function);
72+
return result;
73+
} else {
74+
// Construct future.
75+
return MakeFuture(&auth_data->future_impl,
76+
SafeFutureHandle<SignInResult>(future_base.GetHandle()));
77+
}
78+
}
79+
80+
Future<SignInResult> FederatedOAuthProvider::SignIn(AuthData* auth_data) {
81+
FIREBASE_ASSERT_RETURN(Future<SignInResult>(), handler_);
82+
assert(auth_data);
83+
Future<SignInResult> future =
84+
CreateAuthFuture(auth_data, kAuthFn_SignInWithProvider);
85+
if (future.status() == kFutureStatusPending) {
86+
AuthCompletionHandle* auth_completion_handle = new AuthCompletionHandle(
87+
SafeFutureHandle<SignInResult>(future.GetHandle()), auth_data);
88+
handler_->OnSignIn(provider_data_, auth_completion_handle);
89+
}
90+
return future;
91+
}
92+
93+
Future<SignInResult> FederatedOAuthProvider::Link(AuthData* auth_data) {
94+
assert(auth_data);
95+
FIREBASE_ASSERT_RETURN(Future<SignInResult>(), handler_);
96+
Future<SignInResult> future =
97+
CreateAuthFuture(auth_data, kAuthFn_LinkWithProvider);
98+
if (future.status() != kFutureStatusPending) {
99+
AuthCompletionHandle* auth_completion_handle = new AuthCompletionHandle(
100+
SafeFutureHandle<SignInResult>(future.GetHandle()), auth_data);
101+
handler_->OnLink(provider_data_, auth_completion_handle);
102+
}
103+
return future;
104+
}
105+
106+
Future<SignInResult> FederatedOAuthProvider::Reauthenticate(
107+
AuthData* auth_data) {
108+
assert(auth_data);
109+
FIREBASE_ASSERT_RETURN(Future<SignInResult>(), handler_);
110+
Future<SignInResult> future =
111+
CreateAuthFuture(auth_data, kAuthFn_ReauthenticateWithProvider);
112+
if (future.status() != kFutureStatusPending) {
113+
AuthCompletionHandle* auth_completion_handle = new AuthCompletionHandle(
114+
SafeFutureHandle<SignInResult>(future.GetHandle()), auth_data);
115+
handler_->OnReauthenticate(provider_data_, auth_completion_handle);
116+
}
117+
return future;
118+
}
119+
120+
// Helper function to identify any missing required data from an
121+
// AuthetnicatedUserData struct.
122+
const char* CheckForRequiredAuthenicatedUserData(
123+
const FederatedAuthProvider::AuthenticatedUserData& user_data) {
124+
const char* error_message = nullptr;
125+
if (user_data.uid == nullptr) {
126+
error_message = "null uid";
127+
} else if (user_data.provider_id == nullptr) {
128+
error_message = "null provider_id";
129+
} else if (user_data.access_token == nullptr) {
130+
error_message = "null access_token";
131+
} else if (user_data.refresh_token == nullptr) {
132+
error_message = "null refresh_token";
133+
}
134+
return error_message;
135+
}
136+
137+
// Helper function which uses the AuthCompletionHandle to plumb an
138+
// asynchronous custom-application result into a Future<SignInResult>.
139+
// Note: error_message is an optional parameter.
140+
void CompleteAuthHandle(
141+
AuthCompletionHandle* completion_handle,
142+
const FederatedAuthProvider::AuthenticatedUserData& user_data,
143+
AuthError auth_error, const char* error_message) {
144+
assert(completion_handle);
145+
assert(completion_handle->auth_data);
146+
SignInResult sign_in_result;
147+
if (auth_error == kAuthErrorNone) {
148+
error_message = CheckForRequiredAuthenicatedUserData(user_data);
149+
if (error_message != nullptr) {
150+
auth_error = kAuthErrorInvalidAuthenticatedUserData;
151+
} else {
152+
AuthenticationResult auth_result = CompleteAuthenticedUserSignInFlow(
153+
completion_handle->auth_data, user_data);
154+
if (auth_result.IsValid()) {
155+
sign_in_result =
156+
auth_result.SetAsCurrentUser(completion_handle->auth_data);
157+
} else {
158+
auth_error = kAuthErrorInvalidAuthenticatedUserData;
159+
error_message = "Internal parse error";
160+
}
161+
}
162+
}
163+
completion_handle->auth_data->future_impl.CompleteWithResult(
164+
completion_handle->future_handle, auth_error,
165+
(error_message) ? error_message : "", sign_in_result);
166+
delete completion_handle;
167+
}
168+
169+
// Completion handlers for Federated OAuth sign-in.
170+
template <>
171+
void FederatedAuthProvider::Handler<FederatedOAuthProviderData>::SignInComplete(
172+
AuthCompletionHandle* completion_handle,
173+
const AuthenticatedUserData& user_data, AuthError auth_error,
174+
const char* error_message) {
175+
FIREBASE_ASSERT_RETURN_VOID(completion_handle);
176+
CompleteAuthHandle(completion_handle, user_data, auth_error, error_message);
177+
}
178+
179+
template <>
180+
void FederatedAuthProvider::Handler<FederatedOAuthProviderData>::LinkComplete(
181+
AuthCompletionHandle* completion_handle,
182+
const AuthenticatedUserData& user_data, AuthError auth_error,
183+
const char* error_message) {
184+
FIREBASE_ASSERT_RETURN_VOID(completion_handle);
185+
CompleteAuthHandle(completion_handle, user_data, auth_error, error_message);
186+
}
187+
188+
template <>
189+
void FederatedAuthProvider::Handler<FederatedOAuthProviderData>::
190+
ReauthenticateComplete(AuthCompletionHandle* completion_handle,
191+
const AuthenticatedUserData& user_data,
192+
AuthError auth_error, const char* error_message) {
193+
FIREBASE_ASSERT_RETURN_VOID(completion_handle);
194+
CompleteAuthHandle(completion_handle, user_data, auth_error, error_message);
195+
}
196+
#endif // INTERNAL_EXPERIMENTAL
197+
198+
} // namespace auth
199+
} // namespace firebase

auth/src/desktop/auth_providers/github_auth_provider.cc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,10 @@ Credential GitHubAuthProvider::GetCredential(const char* const token) {
2828
return Credential{new CredentialImpl{new GitHubAuthCredential(token)}};
2929
}
3030

31+
// static
32+
const char* GitHubAuthProvider::GetProviderId() {
33+
return "github.com";
34+
}
35+
3136
} // namespace auth
3237
} // namespace firebase

auth/src/desktop/auth_providers/google_auth_provider.cc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,5 +37,10 @@ Credential GoogleAuthProvider::GetCredential(const char* const id_token,
3737
}
3838
}
3939

40+
// static
41+
const char* GoogleAuthProvider::GetProviderId() {
42+
return "google.com";
43+
}
44+
4045
} // namespace auth
4146
} // namespace firebase

auth/src/desktop/auth_providers/playgames_auth_provider.cc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,10 @@ Credential PlayGamesAuthProvider::GetCredential(
3030
new CredentialImpl{new PlayGamesAuthCredential(server_auth_code)}};
3131
}
3232

33+
// static
34+
const char* PlayGamesAuthProvider::GetProviderId() {
35+
return "playgames.google.com";
36+
}
37+
3338
} // namespace auth
3439
} // namespace firebase

auth/src/desktop/auth_providers/twitter_auth_provider.cc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,10 @@ Credential TwitterAuthProvider::GetCredential(const char* const token,
3030
new CredentialImpl{new TwitterAuthCredential(token, secret)}};
3131
}
3232

33+
// static
34+
const char* TwitterAuthProvider::GetProviderId() {
35+
return "twitter.com";
36+
}
37+
3338
} // namespace auth
3439
} // namespace firebase

auth/src/desktop/authentication_result.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
#ifndef FIREBASE_AUTH_CLIENT_CPP_SRC_DESKTOP_AUTHENTICATION_RESULT_H_
1616
#define FIREBASE_AUTH_CLIENT_CPP_SRC_DESKTOP_AUTHENTICATION_RESULT_H_
1717

18+
#include <cstddef>
1819
#include <string>
20+
1921
#include "app/rest/util.h"
2022
#include "app/src/log.h"
2123
#include "auth/src/common.h"
@@ -24,6 +26,7 @@
2426
#include "auth/src/desktop/get_additional_user_info.h"
2527
#include "auth/src/desktop/rpcs/sign_up_new_user_response.h"
2628
#include "auth/src/desktop/user_desktop.h"
29+
#include "auth/src/include/firebase/auth.h"
2730

2831
namespace firebase {
2932
namespace auth {
@@ -46,6 +49,10 @@ class AuthenticationResult {
4649
template <typename ResponseT>
4750
static AuthenticationResult FromResponse(const ResponseT& response);
4851

52+
// Creates a sign-in result corresponding to the provided user data.
53+
static AuthenticationResult FromAuthenticatedUserData(
54+
const FederatedAuthProvider::AuthenticatedUserData& user_data);
55+
4956
// Signs out the currently signed-in user; no-op if no user has been signed
5057
// in. Updates to AuthData are done in a thread-safe manner.
5158
// Listeners will be notified if a user has been previously signed in.
@@ -147,6 +154,24 @@ inline AuthenticationResult AuthenticationResult::FromResponse(
147154
return result;
148155
}
149156

157+
inline AuthenticationResult AuthenticationResult::FromAuthenticatedUserData(
158+
const FederatedAuthProvider::AuthenticatedUserData& user_data) {
159+
AuthenticationResult result;
160+
161+
result.user_impl_.is_anonymous = false;
162+
result.user_impl_.uid = user_data.uid;
163+
result.user_impl_.id_token = user_data.access_token;
164+
result.user_impl_.refresh_token = user_data.refresh_token;
165+
result.user_impl_.provider_id = user_data.provider_id;
166+
result.user_impl_.access_token = user_data.access_token;
167+
result.user_impl_.access_token_expiration_date =
168+
std::time(nullptr) + user_data.token_expires_in_seconds;
169+
170+
result.info_ = GetAdditionalUserInfo(user_data);
171+
172+
return result;
173+
}
174+
150175
} // namespace auth
151176
} // namespace firebase
152177
#endif // FIREBASE_AUTH_CLIENT_CPP_SRC_DESKTOP_AUTHENTICATION_RESULT_H_

0 commit comments

Comments
 (0)