Rename register resend endpoint to send (#622)

This commit is contained in:
shenlan 2025-11-01 20:25:37 +08:00 committed by GitHub
parent a898b95248
commit 32784d74b6
7 changed files with 31 additions and 19 deletions

View File

@ -187,7 +187,7 @@ func RegisterRoutes(r *gin.Engine, opts ...Option) {
auth.POST("/register", h.register)
auth.POST("/register/verify", h.verifyEmail)
auth.POST("/register/resend", h.resendEmailVerification)
auth.POST("/register/send", h.sendEmailVerification)
auth.POST("/login", h.login)
@ -229,7 +229,7 @@ type verificationCodeRequest struct {
Code string `json:"code"`
}
type verificationResendRequest struct {
type verificationSendRequest struct {
Email string `json:"email"`
}
@ -429,13 +429,13 @@ func (h *handler) verifyEmail(c *gin.Context) {
})
}
func (h *handler) resendEmailVerification(c *gin.Context) {
func (h *handler) sendEmailVerification(c *gin.Context) {
if hasQueryParameter(c, "email") {
respondError(c, http.StatusBadRequest, "email_in_query", "email must be sent in the request body")
return
}
var req verificationResendRequest
var req verificationSendRequest
if err := c.ShouldBindJSON(&req); err != nil {
respondError(c, http.StatusBadRequest, "invalid_request", "invalid request payload")
return
@ -468,12 +468,12 @@ func (h *handler) resendEmailVerification(c *gin.Context) {
}
if err := h.enqueueEmailVerification(c.Request.Context(), user); err != nil {
slog.Error("failed to resend verification email", "err", err, "email", user.Email)
slog.Error("failed to send verification email", "err", err, "email", user.Email)
respondError(c, http.StatusInternalServerError, "verification_failed", "verification email could not be sent")
return
}
c.JSON(http.StatusOK, gin.H{"message": "verification email resent"})
c.JSON(http.StatusOK, gin.H{"message": "verification email sent"})
}
func (h *handler) requestPasswordReset(c *gin.Context) {

View File

@ -290,7 +290,7 @@ func TestResendVerificationEndpoint(t *testing.T) {
t.Fatalf("failed to marshal resend payload: %v", err)
}
req = httptest.NewRequest(http.MethodPost, "/api/auth/register/resend", bytes.NewReader(resendBody))
req = httptest.NewRequest(http.MethodPost, "/api/auth/register/send", bytes.NewReader(resendBody))
req.Header.Set("Content-Type", "application/json")
rr = httptest.NewRecorder()
router.ServeHTTP(rr, req)
@ -382,7 +382,7 @@ func TestResendVerificationEndpointErrors(t *testing.T) {
t.Fatalf("failed to marshal resend payload: %v", err)
}
req = httptest.NewRequest(http.MethodPost, "/api/auth/register/resend", bytes.NewReader(resendBody))
req = httptest.NewRequest(http.MethodPost, "/api/auth/register/send", bytes.NewReader(resendBody))
req.Header.Set("Content-Type", "application/json")
rr = httptest.NewRecorder()
router.ServeHTTP(rr, req)
@ -396,7 +396,7 @@ func TestResendVerificationEndpointErrors(t *testing.T) {
t.Fatalf("failed to marshal unknown payload: %v", err)
}
req = httptest.NewRequest(http.MethodPost, "/api/auth/register/resend", bytes.NewReader(unknownBody))
req = httptest.NewRequest(http.MethodPost, "/api/auth/register/send", bytes.NewReader(unknownBody))
req.Header.Set("Content-Type", "application/json")
rr = httptest.NewRecorder()
router.ServeHTTP(rr, req)

View File

@ -483,7 +483,7 @@ export default function RegisterContent() {
}
try {
const resendResponse = await fetch('/api/auth/register/resend', {
const resendResponse = await fetch('/api/auth/register/send', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
@ -705,7 +705,7 @@ export default function RegisterContent() {
setIsResending(true)
try {
const response = await fetch('/api/auth/register/resend', {
const response = await fetch('/api/auth/register/send', {
method: 'POST',
headers: {
'Content-Type': 'application/json',

View File

@ -1 +0,0 @@
export { POST, GET } from '../../verify-email/resend/route'

View File

@ -0,0 +1 @@
export { POST, GET } from '../../verify-email/send/route'

View File

@ -5,7 +5,7 @@ import { getAccountServiceBaseUrl } from '@lib/serviceConfig'
const ACCOUNT_SERVICE_URL = getAccountServiceBaseUrl()
const ACCOUNT_API_BASE = `${ACCOUNT_SERVICE_URL}/api/auth`
type ResendPayload = {
type SendPayload = {
email?: string
}
@ -14,11 +14,11 @@ function normalizeEmail(value: unknown) {
}
export async function POST(request: NextRequest) {
let payload: ResendPayload
let payload: SendPayload
try {
payload = (await request.json()) as ResendPayload
payload = (await request.json()) as SendPayload
} catch (error) {
console.error('Failed to decode verification resend payload', error)
console.error('Failed to decode verification send payload', error)
return NextResponse.json({ success: false, error: 'invalid_request', needMfa: false }, { status: 400 })
}
@ -28,7 +28,7 @@ export async function POST(request: NextRequest) {
}
try {
const response = await fetch(`${ACCOUNT_API_BASE}/register/resend`, {
const response = await fetch(`${ACCOUNT_API_BASE}/register/send`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
@ -45,7 +45,7 @@ export async function POST(request: NextRequest) {
return NextResponse.json({ success: true, error: null, needMfa: false })
} catch (error) {
console.error('Account service verification resend proxy failed', error)
console.error('Account service verification send proxy failed', error)
return NextResponse.json(
{ success: false, error: 'account_service_unreachable', needMfa: false },
{ status: 502 },

View File

@ -4,7 +4,7 @@ This document describes the HTTP endpoints provided by the XControl platform. Ea
## Authentication Gateway (Next.js)
The XControl web frontend exposes authentication APIs under `dashboard/app/api/auth`. These endpoints act as a secure gateway that proxies requests to the shared Account Service (`/api/auth/register`, `/api/auth/register/verify`, `/api/auth/login`, `/api/auth/mfa/setup`, `/api/auth/mfa/verify`). Responses always include `{ "success": boolean, "error": string | null, "needMfa": boolean }` so that multiple frontends can share the same Account Service behaviour.
The XControl web frontend exposes authentication APIs under `dashboard/app/api/auth`. These endpoints act as a secure gateway that proxies requests to the shared Account Service (`/api/auth/register`, `/api/auth/register/send`, `/api/auth/register/verify`, `/api/auth/login`, `/api/auth/mfa/setup`, `/api/auth/mfa/verify`). Responses always include `{ "success": boolean, "error": string | null, "needMfa": boolean }` so that multiple frontends can share the same Account Service behaviour.
Gateway-managed session cookies (`xc_session`) and MFA challenge cookies (`xc_mfa_challenge`) are issued with `HttpOnly`, `Secure`, and `SameSite=Strict` attributes. Cookies are HTTPS-only and never expose raw secrets to JavaScript.
@ -22,6 +22,18 @@ Gateway-managed session cookies (`xc_session`) and MFA challenge cookies (`xc_mf
-d '{"name":"demo","email":"demo@example.com","password":"Secret123","confirmPassword":"Secret123"}'
```
### POST /api/auth/register/send
- **Description:** Trigger a verification email for an existing pending registration. This endpoint may be used to send the initial code when the frontend wants to separate registration from verification, or to resend a code if the user did not receive the previous email.
- **Body Parameters (JSON):**
- `email` The pending account email address.
- **Response:** `{ "success": true, "error": null, "needMfa": false }` on success. On failure `error` contains the Account Service error code.
- **Test:**
```bash
curl -X POST http://localhost:3000/api/auth/register/send \
-H "Content-Type: application/json" \
-d '{"email":"demo@example.com"}'
```
### POST /api/auth/verify-email
- **Description:** Confirm the 6-digit email verification code issued during registration. Activates the account when the code matches and has not expired.
- **Body Parameters (JSON):**