Skip to content

feat(auth): Add email verification to user registration to verify email ownership. - #7978

Draft
Neilk1021 wants to merge 4 commits into
apache:mainfrom
Neilk1021:task/VerifyEmailWithCodeLite
Draft

feat(auth): Add email verification to user registration to verify email ownership.#7978
Neilk1021 wants to merge 4 commits into
apache:mainfrom
Neilk1021:task/VerifyEmailWithCodeLite

Conversation

@Neilk1021

Copy link
Copy Markdown
Contributor

What changes were proposed in this PR?

An email address a user types is now proved by a code mailed to it before it is accepted at registration, and when a signed-in account with no address on file supplies one. An address a provider already asserts as verified (Google's email_verified) is trusted and never re-checked.

The hole this closes: a dataset contributor placeholder is a real account that contributor rows already point at, and until now anyone could claim it by typing a colleague's address.

Codes are derived, not stored. EmailCodeVerifier computes a 6-digit code as a truncated HMAC (RFC 4226 style) over purpose | scope | address | time-step, keyed from the JWT secret, and checks it by recomputing for the current and previous step:

register(user, email, pw) ──> derive code ──> mail it ──> 200, no account yet
                                                        │
                                    registerVerify(user, email, pw, code)
                                                        │
                                    recompute & compare ──> account + token

Consequences of that choice:

No email_verification table no migration, no DDL, no changelog entry in this PR
Nothing pending is written down no password or hash waits anywhere while a signup is in flight
A restart loses nothing the code is a function of the secret and the clock
The client re-submits its fields which is why registerVerify takes the password again

A code is valid 5–10 minutes (one step plus the previous). Resend cooldown, a per-scope
attempt cap, and eviction of stale trackers live in an in-memory map; /auth/register is
unauthenticated, so that map is bounded deliberately rather than incidentally.

Defaults. user-sys.email-verification ships on — an unproven address is the weaker
position, so a deployment opts out of checking rather than into it. It must agree with
user-sys.google.smtp.gmail: on with no sender configured is a misconfiguration, not a
degraded mode, and both entry points refuse with a 503 naming the two settings rather than
logging the code where anyone with log access could spend it. bin/local-dev.sh sets
USER_SYS_EMAIL_VERIFICATION=false, since a local stack has no sender; an explicit export
still wins.

New endpoints: POST /auth/register/verify and POST /auth/email/code. POST /auth/register
now answers with RegistrationResponse, whose null accessToken is the "a code was mailed"
signal — it is not reported separately, so the two cannot drift apart.

Any related issues, documentation, discussions?

Closes #7977
Discussion #7818

How was this PR tested?

Automated, all run on this branch:

# backend — the verifier and both endpoint flows
sbt "WorkflowExecutionService/testOnly org.apache.texera.web.resource.auth.AuthResourceSpec org.apache.texera.web.resource.auth.EmailCodeVerifierSpec"
#   => Tests: succeeded 75, failed 0

# the config flag's default and its exposure to the anonymous config payload
sbt "Config/testOnly org.apache.texera.common.config.UserSystemConfigSpec"
#   => Tests: succeeded 3, failed 0
sbt "ConfigService/testOnly org.apache.texera.service.resource.ConfigResourceSpec"
#   => Tests: succeeded 33, failed 0

# frontend, full suite
cd frontend && yarn test --watch=false
#   => Test Files 201 passed | Tests 5067 passed, 1 skipped

EmailCodeVerifierSpec is new (20 cases): code derivation and its independence from any
stored state, acceptance across the step boundary, rejection of a code minted for a different
address / scope / purpose / secret, the resend cooldown, the attempt cap, and the refusal when
SMTP is unconfigured. AuthResourceSpec gains 12 cases covering both endpoint flows with the
verification seam pinned on.

Note for anyone running these locally: AuthResource.emailVerificationRequired reads
UserSystemConfig, an object val resolved once per JVM. Both suites pin the flag explicitly
rather than inheriting it, so they pass whether or not USER_SYS_EMAIL_VERIFICATION is
exported in your shell — verified both ways.

Was this PR authored or co-authored using generative AI tooling?

Generated-by: Claude Code (Claude Opus 5)

@github-actions github-actions Bot added engine frontend Changes related to the frontend GUI docs Changes related to documentations infra common platform Non-amber Scala service paths labels Aug 25, 2026
@Neilk1021
Neilk1021 marked this pull request as draft August 25, 2026 21:19
@github-actions

Copy link
Copy Markdown
Contributor

Automated Reviewer Suggestions

Based on the git blame history of the changed files, we recommend the following reviewers:

  • Contributors with relevant context: @Ma77Ball, @aglinxinyuan, @zyratlo
    You can notify them by mentioning @Ma77Ball, @aglinxinyuan, @zyratlo in a comment.

@codecov-commenter

codecov-commenter commented Aug 25, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 83.51648% with 30 lines in your changes missing coverage. Please review.
✅ Project coverage is 92.82%. Comparing base (2c2c0e5) to head (8989ed3).
⚠️ Report is 1 commits behind head on main.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
...apache/texera/web/resource/auth/AuthResource.scala 78.12% 3 Missing and 4 partials ⚠️
...e/texera/web/resource/auth/EmailCodeVerifier.scala 91.02% 2 Missing and 5 partials ⚠️
...pp/hub/component/login/texera-login.component.html 0.00% 4 Missing ⚠️
.../app/hub/component/login/texera-login.component.ts 85.00% 0 Missing and 3 partials ⚠️
...el/http/request/auth/UserRegistrationRequest.scala 80.00% 1 Missing ⚠️
...web/model/http/response/RegistrationResponse.scala 0.00% 1 Missing ⚠️
...org/apache/texera/web/resource/EmailTemplate.scala 83.33% 0 Missing and 1 partial ⚠️
...ontend/src/app/common/service/user/auth.service.ts 94.44% 0 Missing and 1 partial ⚠️
...l-request-modal/email-request-modal.component.html 75.00% 1 Missing ⚠️
...ail-request-modal/email-request-modal.component.ts 66.66% 0 Missing and 1 partial ⚠️
... and 3 more
Additional details and impacted files
@@             Coverage Diff              @@
##               main    #7978      +/-   ##
============================================
- Coverage     92.86%   92.82%   -0.04%     
- Complexity     4631     4659      +28     
============================================
  Files          1174     1176       +2     
  Lines         47442    47612     +170     
  Branches       5304     5334      +30     
============================================
+ Hits          44056    44196     +140     
- Misses         1877     1890      +13     
- Partials       1509     1526      +17     
Flag Coverage Δ *Carryforward flag
access-control-service 81.00% <ø> (ø)
agent-service 99.32% <ø> (ø) Carriedforward from 2c2c0e5
amber 89.15% <86.17%> (-0.03%) ⬇️
computing-unit-managing-service 73.67% <ø> (ø)
config-service 86.86% <100.00%> (+0.13%) ⬆️
file-service 84.48% <ø> (ø)
frontend 94.99% <77.19%> (-0.05%) ⬇️
notebook-migration-service 79.13% <ø> (ø)
pyamber 97.57% <ø> (ø) Carriedforward from 2c2c0e5
workflow-compiling-service 77.19% <ø> (ø)

*This pull request uses carry forward flags. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Benchmark changes need a look

🟢 4 better · 🔴 2 worse · ⚪ 9 noise (<±5%) · 0 without baseline

Compared against main 0af18e0 benchmarked on this same runner, so the delta is largely free of cross-runner hardware noise. The "7d avg" column still reflects the gh-pages dashboard. Treat <±5% as noise unless repeated.

Dashboard · Run

config throughput MB/s latency max Δ latest / 7d
🔴 bs=10 sw=10 sl=64 441 0.269 21,821/27,042/27,042 us 🔴 -7.6% / 🔴 +100.1%
🟢 bs=100 sw=10 sl=64 939 0.573 104,946/120,335/120,335 us 🟢 -9.3% / 🔴 +31.4%
🟢 bs=1000 sw=10 sl=64 1,088 0.664 916,357/952,788/952,788 us 🟢 -9.1% / 🔴 -10.9%
Baseline details

Latest main 0af18e0 from same runner

config metric PR latest main 7d avg Δ latest Δ 7d
bs=10 sw=10 sl=64 throughput 441 tuples/sec 477 tuples/sec 908.02 tuples/sec -7.5% -51.4%
bs=10 sw=10 sl=64 MB/s 0.269 MB/s 0.291 MB/s 0.554 MB/s -7.6% -51.5%
bs=10 sw=10 sl=64 p50 21,821 us 21,439 us 10,907 us +1.8% +100.1%
bs=10 sw=10 sl=64 p95 27,042 us 28,042 us 13,604 us -3.6% +98.8%
bs=10 sw=10 sl=64 p99 27,042 us 28,042 us 16,697 us -3.6% +62.0%
bs=100 sw=10 sl=64 throughput 939 tuples/sec 950 tuples/sec 1,188 tuples/sec -1.2% -20.9%
bs=100 sw=10 sl=64 MB/s 0.573 MB/s 0.58 MB/s 0.725 MB/s -1.2% -20.9%
bs=100 sw=10 sl=64 p50 104,946 us 100,595 us 85,003 us +4.3% +23.5%
bs=100 sw=10 sl=64 p95 120,335 us 132,654 us 91,599 us -9.3% +31.4%
bs=100 sw=10 sl=64 p99 120,335 us 132,654 us 99,202 us -9.3% +21.3%
bs=1000 sw=10 sl=64 throughput 1,088 tuples/sec 1,080 tuples/sec 1,221 tuples/sec +0.7% -10.9%
bs=1000 sw=10 sl=64 MB/s 0.664 MB/s 0.659 MB/s 0.745 MB/s +0.8% -10.9%
bs=1000 sw=10 sl=64 p50 916,357 us 919,073 us 837,593 us -0.3% +9.4%
bs=1000 sw=10 sl=64 p95 952,788 us 1,047,729 us 881,015 us -9.1% +8.1%
bs=1000 sw=10 sl=64 p99 952,788 us 1,047,729 us 915,550 us -9.1% +4.1%
Raw CSV
config_idx,batch_size,schema_width,string_len,num_batches,total_ms,total_tuples,total_bytes,tuples_per_sec,mb_per_sec,lat_p50_us,lat_p95_us,lat_p99_us
0,10,10,64,20,453.49,200,128000,441,0.269,21820.94,27042.29,27042.29
1,100,10,64,20,2129.54,2000,1280000,939,0.573,104946.01,120335.42,120335.42
2,1000,10,64,20,18389.98,20000,12800000,1088,0.664,916357.14,952787.80,952787.80

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

common docs Changes related to documentations engine frontend Changes related to the frontend GUI infra platform Non-amber Scala service paths

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add email verification for local login and non-verified account providers.

2 participants