Skip to content

Commit

Permalink
Merge branch 'main' into workflow/auto-update-deps-195-20231213-090113
Browse files Browse the repository at this point in the history
  • Loading branch information
jonsimantov authored Dec 20, 2023
2 parents ec3b3fa + 08f43f1 commit e4e1d61
Show file tree
Hide file tree
Showing 8 changed files with 239 additions and 30 deletions.
1 change: 1 addition & 0 deletions auth/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ set(desktop_SRCS
src/desktop/rpcs/reset_password_request.cc
src/desktop/rpcs/secure_token_request.cc
src/desktop/rpcs/set_account_info_request.cc
src/desktop/rpcs/sign_up_request.cc
src/desktop/rpcs/sign_up_new_user_request.cc
src/desktop/rpcs/verify_assertion_request.cc
src/desktop/rpcs/verify_custom_token_request.cc
Expand Down
4 changes: 4 additions & 0 deletions auth/src/desktop/get_account_info_result.cc
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ void GetAccountInfoResult::MergeToUser(UserView::Writer& user) const {
user_impl_.has_email_password_credential;
user->creation_timestamp = user_impl_.creation_timestamp;
user->last_sign_in_timestamp = user_impl_.last_sign_in_timestamp;
// If the account info has an email, make sure is_anonymous is false
if (!user_impl_.email.empty() && user_impl_.has_email_password_credential) {
user->is_anonymous = false;
}

user.ResetUserInfos(provider_data_);
}
Expand Down
63 changes: 63 additions & 0 deletions auth/src/desktop/rpcs/sign_up_request.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include "auth/src/desktop/rpcs/sign_up_request.h"

#include <memory>
#include <string>

#include "app/src/assert.h"
#include "app/src/include/firebase/app.h"

namespace firebase {
namespace auth {

SignUpRequest::SignUpRequest(::firebase::App& app, const char* api_key)
: AuthRequest(app, request_resource_data, true) {
FIREBASE_ASSERT_RETURN_VOID(api_key);

std::string url(
"https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=");
url.append(api_key);
set_url(url.c_str());

application_data_->returnSecureToken = true;
}

std::unique_ptr<SignUpRequest>
SignUpRequest::CreateLinkWithEmailAndPasswordRequest(::firebase::App& app,
const char* api_key,
const char* email,
const char* password) {
auto request =
std::unique_ptr<SignUpRequest>(new SignUpRequest(app, api_key));

if (email) {
request->application_data_->email = email;
} else {
LogError("No email given");
}
if (password) {
request->application_data_->password = password;
} else {
LogError("No password given");
}
request->UpdatePostFields();
return request;
}

} // namespace auth
} // namespace firebase
59 changes: 59 additions & 0 deletions auth/src/desktop/rpcs/sign_up_request.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#ifndef FIREBASE_AUTH_SRC_DESKTOP_RPCS_SIGN_UP_REQUEST_H_
#define FIREBASE_AUTH_SRC_DESKTOP_RPCS_SIGN_UP_REQUEST_H_

#include <memory>

#include "app/src/include/firebase/app.h"
#include "auth/request_generated.h"
#include "auth/request_resource.h"
#include "auth/src/desktop/rpcs/auth_request.h"

namespace firebase {
namespace auth {

// Represents the request payload for the signUp HTTP API. Use this to
// upgrade anonymous accounts with email and password. The full specification of
// the HTTP API can be found at
// https://cloud.google.com/identity-platform/docs/reference/rest/v1/accounts/signUp
class SignUpRequest : public AuthRequest {
private:
explicit SignUpRequest(::firebase::App& app, const char* api_key);
static std::unique_ptr<SignUpRequest> CreateRequest(::firebase::App& app,
const char* api_key);

public:
// Initializer for linking an email and password to an account.
static std::unique_ptr<SignUpRequest> CreateLinkWithEmailAndPasswordRequest(
::firebase::App& app, const char* api_key, const char* email,
const char* password);

void SetIdToken(const char* id_token) {
if (id_token) {
application_data_->idToken = id_token;
UpdatePostFields();
} else {
LogError("No id token given.");
}
}
};

} // namespace auth
} // namespace firebase

#endif // FIREBASE_AUTH_SRC_DESKTOP_RPCS_SIGN_UP_REQUEST_H_
53 changes: 48 additions & 5 deletions auth/src/desktop/user_desktop.cc
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
#include "auth/src/desktop/rpcs/secure_token_response.h"
#include "auth/src/desktop/rpcs/set_account_info_request.h"
#include "auth/src/desktop/rpcs/set_account_info_response.h"
#include "auth/src/desktop/rpcs/sign_up_request.h"
#include "auth/src/desktop/rpcs/verify_assertion_request.h"
#include "auth/src/desktop/rpcs/verify_assertion_response.h"
#include "auth/src/desktop/rpcs/verify_password_request.h"
Expand Down Expand Up @@ -256,6 +257,47 @@ void TriggerSaveUserFlow(AuthData* const auth_data) {
}
}

template <typename ResponseT, typename ResultT>
void PerformSignUpFlow(AuthDataHandle<ResultT, SignUpRequest>* const handle) {
FIREBASE_ASSERT_RETURN_VOID(handle && handle->request);

const auto response = GetResponse<ResponseT>(*handle->request);
const AuthenticationResult auth_response =
CompleteSignInFlow(handle->auth_data, response);

if (auth_response.IsValid()) {
const AuthResult auth_result =
auth_response.SetAsCurrentUser(handle->auth_data);
// The usual SignIn flow doesn't trigger this, but since this is used
// to upgrade anonymous accounts, it is needed for SignUp
NotifyIdTokenListeners(handle->auth_data);
CompletePromise(&handle->promise, auth_result);
} else {
FailPromise(&handle->promise, auth_response.error());
}
}

template <typename ResponseT, typename ResultT>
void PerformSignUpFlow_DEPRECATED(
AuthDataHandle<ResultT, SignUpRequest>* const handle) {
FIREBASE_ASSERT_RETURN_VOID(handle && handle->request);

const auto response = GetResponse<ResponseT>(*handle->request);
const AuthenticationResult auth_response =
CompleteSignInFlow(handle->auth_data, response);

if (auth_response.IsValid()) {
const SignInResult sign_in_result =
auth_response.SetAsCurrentUser_DEPRECATED(handle->auth_data);
// The usual SignIn flow doesn't trigger this, but since this is used
// to upgrade anonymous accounts, it is needed for SignUp
NotifyIdTokenListeners(handle->auth_data);
CompletePromise(&handle->promise, sign_in_result);
} else {
FailPromise(&handle->promise, auth_response.error());
}
}

template <typename ResultT>
void PerformSetAccountInfoFlow(
AuthDataHandle<ResultT, SetAccountInfoRequest>* const handle) {
Expand Down Expand Up @@ -306,14 +348,14 @@ Future<ResultT> DoLinkWithEmailAndPassword(
const EmailAuthCredential* email_credential =
GetEmailCredential(raw_credential_impl);

typedef SetAccountInfoRequest RequestT;
typedef SignUpRequest RequestT;
auto request = RequestT::CreateLinkWithEmailAndPasswordRequest(
*auth_data->app, GetApiKey(*auth_data),
email_credential->GetEmail().c_str(),
email_credential->GetPassword().c_str());

return CallAsyncWithFreshToken(auth_data, promise, std::move(request),
PerformSetAccountInfoFlow<ResultT>);
PerformSignUpFlow<SignUpNewUserResponse>);
}

// Calls setAccountInfo endpoint to link the current user with the given email
Expand All @@ -329,14 +371,15 @@ Future<ResultT> DoLinkWithEmailAndPassword_DEPRECATED(
const EmailAuthCredential* email_credential =
GetEmailCredential(raw_credential_impl);

typedef SetAccountInfoRequest RequestT;
typedef SignUpRequest RequestT;
auto request = RequestT::CreateLinkWithEmailAndPasswordRequest(
*auth_data->app, GetApiKey(*auth_data),
email_credential->GetEmail().c_str(),
email_credential->GetPassword().c_str());

return CallAsyncWithFreshToken(auth_data, promise, std::move(request),
PerformSetAccountInfoFlow_DEPRECATED<ResultT>);
return CallAsyncWithFreshToken(
auth_data, promise, std::move(request),
PerformSignUpFlow_DEPRECATED<SignUpNewUserResponse>);
}

// Checks that the given provider wasn't already linked to the currently
Expand Down
40 changes: 26 additions & 14 deletions auth/tests/desktop/user_desktop_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -567,8 +567,32 @@ TEST_F(UserDesktopTest, TestLinkWithCredential_OauthCredential) {
}

TEST_F(UserDesktopTest, TestLinkWithCredential_EmailCredential) {
InitializeConfigWithAFake(GetUrlForApi(API_KEY, "setAccountInfo"),
FakeSetAccountInfoResponse());
FakeSetT fakes;
const auto api_url =
std::string(
"https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=") +
API_KEY;
fakes[api_url] =
FakeSuccessfulResponse("SignupNewUserResponse",
" \"idToken\": \"idtoken123\","
" \"refreshToken\": \"refreshtoken123\","
" \"expiresIn\": \"3600\","
" \"localId\": \"localid123\"");
fakes[GetUrlForApi(API_KEY, "getAccountInfo")] =
FakeSuccessfulResponse("GetAccountInfoResponse",
" \"users\": ["
" {"
" \"localId\": \"localid123\","
" \"lastLoginAt\": \"123\","
" \"createdAt\": \"456\","
" \"email\": \"[email protected]\","
" \"idToken\": \"new_fake_token\","
" \"passwordHash\": \"new_fake_hash\","
" \"emailVerified\": false," +
GetFakeProviderInfo() +
" }"
" ]");
InitializeConfigWithFakes(fakes);

// Response contains a new ID token, but user should have stayed the same.
id_token_listener.ExpectChanges(1);
Expand Down Expand Up @@ -844,18 +868,6 @@ TEST_F(UserDesktopTestSignOutOnError, Unlink) {
sem_.Wait();
}

TEST_F(UserDesktopTestSignOutOnError, LinkWithEmail) {
CheckSignOutIfUserIsInvalid(
GetUrlForApi(API_KEY, "setAccountInfo"), "USER_NOT_FOUND",
kAuthErrorUserNotFound, [&] {
sem_.Post();
return firebase_user_->LinkWithCredential_DEPRECATED(
EmailAuthProvider::GetCredential("[email protected]",
"fake_password"));
});
sem_.Wait();
}

TEST_F(UserDesktopTestSignOutOnError, LinkWithOauthCredential) {
CheckSignOutIfUserIsInvalid(
GetUrlForApi(API_KEY, "verifyAssertion"), "USER_NOT_FOUND",
Expand Down
46 changes: 35 additions & 11 deletions auth/tests/user_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -402,18 +402,42 @@ TEST_F(UserTest, TestSendEmailVerification) {
}

TEST_F(UserTest, TestLinkWithCredential) {
const std::string config =
std::string(
"{"
" config:["
" {fake:'FirebaseUser.linkWithCredential', "
"futuregeneric:{ticker:1}},"
" {fake:'FIRUser.linkWithCredential:completion:',"
" futuregeneric:{ticker:1}},") +
SET_ACCOUNT_INFO_SUCCESSFUL_RESPONSE +
// Under the hood, since this is linking an email/password,
// it is expecting a signUp call, followed by a getAccountInfo.
firebase::testing::cppsdk::ConfigSet(
"{"
" config:["
" {fake:'FirebaseUser.linkWithCredential', "
"futuregeneric:{ticker:1}},"
" {fake:'FIRUser.linkWithCredential:completion:',"
" futuregeneric:{ticker:1}},"
" "
"{fake:'https://identitytoolkit.googleapis.com/v1/"
"accounts:signUp?key=not_a_real_api_key',"
" httpresponse: {"
" header: ['HTTP/1.1 200 Ok','Server:mock server 101'],"
" body: ['{"
" \"kind\": \"identitytoolkit#SignupNewUserResponse\","
" \"idToken\": \"idtoken123\","
" \"refreshToken\": \"refreshtoken123\","
" \"expiresIn\": \"3600\","
" \"localId\": \"localid123\""
"}',]"
" }"
" },"
" {fake:'https://www.googleapis.com/identitytoolkit/v3/relyingparty/"
"getAccountInfo?key=not_a_real_api_key',"
" httpresponse: {"
" header: ['HTTP/1.1 200 Ok','Server:mock server 101'],"
" body: ['{"
" \"users\": [{"
" \"localId\": \"localid123\""
" }]}',"
" ]"
" }"
" }"
" ]"
"}";
firebase::testing::cppsdk::ConfigSet(config.c_str());
"}");

Future<User*> result = firebase_user_->LinkWithCredential_DEPRECATED(
EmailAuthProvider::GetCredential("[email protected]", "pw"));
Expand Down
3 changes: 3 additions & 0 deletions release_build_files/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -638,6 +638,9 @@ code.
- General (Android): Update to Firebase Android BoM version 32.3.1.
- General (iOS): Update to Firebase Cocoapods version 10.17.0.
- Analytics: Updated the consent management API to include new consent signals.
- Auth: Fix a bug where anonymous account can't be linked with
email password credential. For background, see
[Email Enumeration Protection](https://cloud.google.com/identity-platform/docs/admin/email-enumeration-protection#overview)
- GMA (Android) Updated dependency to play-services-ads version 22.4.0.

### 11.6.0
Expand Down

0 comments on commit e4e1d61

Please sign in to comment.