Account: implements the /v2/verify/init endpoint (#35678)

This commit is contained in:
Szilard Szaloki
2026-04-20 13:32:10 -06:00
committed by GitHub
parent 37e0eeb8f1
commit 1ab99aa8a1
4 changed files with 172 additions and 0 deletions
@@ -16,6 +16,7 @@ source_set("endpoints") {
"service_token.h",
"verify_complete.h",
"verify_delete.h",
"verify_init.h",
"verify_resend.h",
]
@@ -37,6 +38,7 @@ generated_types("generated_api_types") {
"service_token_bodies.idl",
"verify_complete_bodies.idl",
"verify_delete_bodies.idl",
"verify_init_bodies.idl",
"verify_resend_bodies.idl",
]
@@ -59,6 +61,7 @@ source_set("unit_tests") {
"service_token_unittest.cc",
"verify_complete_unittest.cc",
"verify_delete_unittest.cc",
"verify_init_unittest.cc",
"verify_resend_unittest.cc",
]
@@ -0,0 +1,25 @@
/* Copyright (c) 2026 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/. */
#ifndef BRAVE_COMPONENTS_BRAVE_ACCOUNT_ENDPOINTS_VERIFY_INIT_H_
#define BRAVE_COMPONENTS_BRAVE_ACCOUNT_ENDPOINTS_VERIFY_INIT_H_
#include "brave/components/brave_account/endpoint_client/brave_endpoint.h"
#include "brave/components/brave_account/endpoint_client/request_types.h"
#include "brave/components/brave_account/endpoint_client/response.h"
#include "brave/components/brave_account/endpoints/error_body.h"
#include "brave/components/brave_account/endpoints/verify_init_bodies.h"
namespace brave_account::endpoints {
using VerifyInit = endpoint_client::BraveEndpoint<
"accounts.bsg",
"/v2/verify/init",
endpoint_client::POST<VerifyInitRequestBody>,
endpoint_client::Response<VerifyInitSuccessBody, ErrorBody>>;
} // namespace brave_account::endpoints
#endif // BRAVE_COMPONENTS_BRAVE_ACCOUNT_ENDPOINTS_VERIFY_INIT_H_
@@ -0,0 +1,18 @@
/* Copyright (c) 2026 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/. */
// Schema for the /v2/verify/init request and response.
namespace endpoints {
dictionary VerifyInitRequestBody {
DOMString email;
DOMString intent;
DOMString locale;
DOMString service;
};
dictionary VerifyInitSuccessBody {
DOMString verificationToken;
};
};
@@ -0,0 +1,126 @@
/* Copyright (c) 2026 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/. */
#include "brave/components/brave_account/endpoints/verify_init.h"
#include "base/no_destructor.h"
#include "base/types/expected.h"
#include "brave/components/brave_account/endpoints/endpoint_test.h"
#include "net/base/net_errors.h"
#include "net/http/http_status_code.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace brave_account::endpoints {
bool operator==(const VerifyInit::Response::SuccessBody& lhs,
const VerifyInit::Response::SuccessBody& rhs) {
return lhs.verification_token == rhs.verification_token;
}
namespace {
using VerifyInitTestCase = EndpointTestCase<VerifyInit>;
const VerifyInitTestCase* Success() {
static const base::NoDestructor<VerifyInitTestCase> kSuccess(
{.test_name = "success",
.http_status_code = net::HTTP_OK,
.raw_response_body = R"({ "verificationToken": "eyJhbGciOiJFUz" })",
.expected_response = {
.net_error = net::OK, .status_code = net::HTTP_OK, .body = [] {
VerifyInit::Response::SuccessBody body;
body.verification_token = "eyJhbGciOiJFUz";
return body;
}()}});
return kSuccess.get();
}
// clang-format off
// application/json errors:
// - HTTP 400:
// - { "code": null, "error": "Bad Request", "status": 400 }
// - { "code": 13001, "error": "too many pending verification requests for email", "status": 400 }
// - { "code": 13003, "error": "intent not allowed", "status": 400 }
// - { "code": 13004, "error": "account already exists", "status": 400 }
// - { "code": 13005, "error": "account does not exist", "status": 400 }
// - { "code": 13006, "error": "email domain is not supported", "status": 400 }
// - { "code": 13007, "error": "failed to send email due to invalid format", "status": 400 }
// - HTTP 401:
// - { "code": null, "error": "Unauthorized", "status": 401 }
// - HTTP 403:
// - { "code": 14007, "error": "invalid token audience", "status": 403 }
// - HTTP 5XX:
// - { "code": null, "error": "Internal Server Error", "status": <5xx> }
// clang-format on
const VerifyInitTestCase* ApplicationJsonErrorCodeIsNull() {
static const base::NoDestructor<VerifyInitTestCase>
kApplicationJsonErrorCodeIsNull(
{.test_name = "application_json_error_code_is_null",
.http_status_code = net::HTTP_BAD_REQUEST,
.raw_response_body =
R"({ "code": null,
"error": "Bad Request",
"status": 400 })",
.expected_response = {.net_error = net::OK,
.status_code = net::HTTP_BAD_REQUEST,
.body = base::unexpected([] {
VerifyInit::Response::ErrorBody body;
body.code = base::Value();
return body;
}())}});
return kApplicationJsonErrorCodeIsNull.get();
}
const VerifyInitTestCase* ApplicationJsonErrorCodeIsNotNull() {
static const base::NoDestructor<VerifyInitTestCase>
kApplicationJsonErrorCodeIsNotNull(
{.test_name = "application_json_error_code_is_not_null",
.http_status_code = net::HTTP_FORBIDDEN,
.raw_response_body =
R"({ "code": 14007,
"error": "invalid token audience",
"status": 403 })",
.expected_response = {.net_error = net::OK,
.status_code = net::HTTP_FORBIDDEN,
.body = base::unexpected([] {
VerifyInit::Response::ErrorBody body;
body.code = base::Value(14007);
return body;
}())}});
return kApplicationJsonErrorCodeIsNotNull.get();
}
// non-application/json errors:
// - HTTP 5XX:
// - plain text errors returned by AWS/load balancer
const VerifyInitTestCase* NonApplicationJsonError() {
static const base::NoDestructor<VerifyInitTestCase> kNonApplicationJsonError(
{.test_name = "non_application_json_error",
.http_status_code = net::HTTP_INTERNAL_SERVER_ERROR,
.raw_response_body = "non-application/json error",
.expected_response = {.net_error = net::OK,
.status_code = net::HTTP_INTERNAL_SERVER_ERROR,
.body = std::nullopt}});
return kNonApplicationJsonError.get();
}
using VerifyInitTest = EndpointTest<VerifyInit>;
} // namespace
TEST_P(VerifyInitTest, HandlesReplies) {
RunTestCase();
}
INSTANTIATE_TEST_SUITE_P(VerifyInitTestCases,
VerifyInitTest,
testing::Values(Success(),
ApplicationJsonErrorCodeIsNull(),
ApplicationJsonErrorCodeIsNotNull(),
NonApplicationJsonError()),
VerifyInitTest::kNameGenerator);
} // namespace brave_account::endpoints