Add OTP resend functionality for user verification

- Implemented GinSendOTP function to handle resending OTP verification codes for user entry.
- Added EntrySendOTPResponse type to structure the response for OTP requests, including OTP ID and expiration time.
- Updated user routing to include a new endpoint for OTP requests, enhancing the user authentication flow.
This commit is contained in:
Max 2025-10-16 14:35:48 +08:00
parent 48f531cf82
commit a8159a0e90
3 changed files with 81 additions and 0 deletions

View file

@ -561,6 +561,80 @@ func validatePassword(password string) error {
return nil
}
// GinSendOTP handles resending OTP verification code
func GinSendOTP(c *gin.Context) {
// Get authorized info from the temporary token
authInfo := oauth.GetAuthorizedInfo(c)
if authInfo == nil || authInfo.Scope != ScopeEntryVerification {
errorResp := &response.ErrorResponse{
Code: response.ErrAccessDenied.Code,
ErrorDescription: "Invalid or missing entry verification token",
}
response.RespondWithError(c, response.StatusUnauthorized, errorResp)
return
}
// Get username and username_type from token claims
username, _ := c.Get("__username")
usernameType, _ := c.Get("__username_type")
usernameStr, ok := username.(string)
if !ok || usernameStr == "" {
errorResp := &response.ErrorResponse{
Code: response.ErrInvalidRequest.Code,
ErrorDescription: "Username not found in token",
}
response.RespondWithError(c, response.StatusBadRequest, errorResp)
return
}
usernameTypeStr, ok := usernameType.(string)
if !ok || usernameTypeStr == "" {
errorResp := &response.ErrorResponse{
Code: response.ErrInvalidRequest.Code,
ErrorDescription: "Username type not found in token",
}
response.RespondWithError(c, response.StatusBadRequest, errorResp)
return
}
// Get locale from query parameter
locale := c.Query("locale")
// Get entry configuration
config := GetEntryConfig(locale)
if config == nil {
errorResp := &response.ErrorResponse{
Code: response.ErrInvalidRequest.Code,
ErrorDescription: "Entry configuration not found",
}
response.RespondWithError(c, response.StatusNotFound, errorResp)
return
}
// Generate new OTP
otpID, verificationCode := generateEntryOTP()
// Send verification message asynchronously
go func() {
ctx := context.Background()
err := sendVerificationMessage(ctx, config, usernameTypeStr, usernameStr, verificationCode, locale)
if err != nil {
log.Error("Failed to resend verification code to %s: %v", usernameStr, err)
return
}
log.Info("Verification code resent to %s (OTP ID: %s)", usernameStr, otpID)
}()
// Prepare response
otpResponse := EntrySendOTPResponse{
OtpID: otpID,
ExpiresIn: 600, // 10 minutes
}
response.RespondWithSuccess(c, response.StatusOK, otpResponse)
}
// GinEntryRegister handles user registration
func GinEntryRegister(c *gin.Context) {
// Get authorized info from the temporary token

View file

@ -290,6 +290,12 @@ type EntryLoginRequest struct {
Locale string `json:"locale,omitempty"`
}
// EntrySendOTPResponse represents the response for sending OTP verification code
type EntrySendOTPResponse struct {
OtpID string `json:"otp_id"` // OTP ID for verification
ExpiresIn int `json:"expires_in,omitempty"` // OTP expiration in seconds
}
// Built-in preset mapping types
const (
MappingGoogle = "google"

View file

@ -35,6 +35,7 @@ func Attach(group *gin.RouterGroup, oauth types.OAuth) {
// Register a new user
group.POST("/entry/register", oauth.Guard, GinEntryRegister) // Register a new user
group.POST("/entry/login", oauth.Guard, GinEntryLogin) // Login a user
group.POST("/entry/otp", oauth.Guard, GinSendOTP) // Send OTP
group.POST("/logout", oauth.Guard, placeholder) // User logout
// Logined User Settings