1
0
mirror of https://github.com/pocket-id/pocket-id.git synced 2026-03-22 18:30:09 +00:00

fix: one-time-access-token route should get user ID from URL only (#1358)

This commit is contained in:
Alessandro (Ale) Segala
2026-03-03 18:53:36 -08:00
committed by GitHub
parent e0fc4cc01b
commit 27ca713cd4
5 changed files with 60 additions and 10 deletions

View File

@@ -139,6 +139,20 @@ func (e *TooManyRequestsError) Error() string {
} }
func (e *TooManyRequestsError) HttpStatusCode() int { return http.StatusTooManyRequests } func (e *TooManyRequestsError) HttpStatusCode() int { return http.StatusTooManyRequests }
type UserIdNotProvidedError struct{}
func (e *UserIdNotProvidedError) Error() string {
return "User id not provided"
}
func (e *UserIdNotProvidedError) HttpStatusCode() int { return http.StatusBadRequest }
type UserNotFoundError struct{}
func (e *UserNotFoundError) Error() string {
return "User not found"
}
func (e *UserNotFoundError) HttpStatusCode() int { return http.StatusNotFound }
type ClientIdOrSecretNotProvidedError struct{} type ClientIdOrSecretNotProvidedError struct{}
func (e *ClientIdOrSecretNotProvidedError) Error() string { func (e *ClientIdOrSecretNotProvidedError) Error() string {

View File

@@ -4,6 +4,7 @@ import (
"net/http" "net/http"
"time" "time"
"github.com/pocket-id/pocket-id/backend/internal/common"
"github.com/pocket-id/pocket-id/backend/internal/utils/cookie" "github.com/pocket-id/pocket-id/backend/internal/utils/cookie"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
@@ -322,22 +323,34 @@ func (uc *UserController) updateCurrentUserProfilePictureHandler(c *gin.Context)
func (uc *UserController) createOneTimeAccessTokenHandler(c *gin.Context, own bool) { func (uc *UserController) createOneTimeAccessTokenHandler(c *gin.Context, own bool) {
var input dto.OneTimeAccessTokenCreateDto var input dto.OneTimeAccessTokenCreateDto
if err := c.ShouldBindJSON(&input); err != nil { err := c.ShouldBindJSON(&input)
if err != nil {
_ = c.Error(err) _ = c.Error(err)
return return
} }
var ttl time.Duration var (
userID string
ttl time.Duration
)
if own { if own {
input.UserID = c.GetString("userID") // Get user ID from context and force the default TTL
userID = c.GetString("userID")
ttl = defaultOneTimeAccessTokenDuration ttl = defaultOneTimeAccessTokenDuration
} else { } else {
// Get user ID from URL parameter, and optional TTL from body
userID = c.Param("id")
ttl = input.TTL.Duration ttl = input.TTL.Duration
if ttl <= 0 { if ttl <= 0 {
ttl = defaultOneTimeAccessTokenDuration ttl = defaultOneTimeAccessTokenDuration
} }
} }
token, err := uc.oneTimeAccessService.CreateOneTimeAccessToken(c.Request.Context(), input.UserID, ttl) if userID == "" {
_ = c.Error(&common.UserIdNotProvidedError{})
return
}
token, err := uc.oneTimeAccessService.CreateOneTimeAccessToken(c.Request.Context(), userID, ttl)
if err != nil { if err != nil {
_ = c.Error(err) _ = c.Error(err)
return return

View File

@@ -3,7 +3,6 @@ package dto
import "github.com/pocket-id/pocket-id/backend/internal/utils" import "github.com/pocket-id/pocket-id/backend/internal/utils"
type OneTimeAccessTokenCreateDto struct { type OneTimeAccessTokenCreateDto struct {
UserID string `json:"userId"`
TTL utils.JSONDuration `json:"ttl" binding:"ttl"` TTL utils.JSONDuration `json:"ttl" binding:"ttl"`
} }

View File

@@ -79,7 +79,7 @@ func (s *OneTimeAccessService) requestOneTimeAccessEmailInternal(ctx context.Con
tx.Rollback() tx.Rollback()
}() }()
user, err := s.userService.GetUser(ctx, userID) user, err := s.userService.getUserInternal(ctx, userID, tx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -131,8 +131,32 @@ func (s *OneTimeAccessService) requestOneTimeAccessEmailInternal(ctx context.Con
} }
func (s *OneTimeAccessService) CreateOneTimeAccessToken(ctx context.Context, userID string, ttl time.Duration) (token string, err error) { func (s *OneTimeAccessService) CreateOneTimeAccessToken(ctx context.Context, userID string, ttl time.Duration) (token string, err error) {
token, _, err = s.createOneTimeAccessTokenInternal(ctx, userID, ttl, false, s.db) tx := s.db.Begin()
return token, err defer func() {
tx.Rollback()
}()
// Load the user to ensure it exists
_, err = s.userService.getUserInternal(ctx, userID, tx)
if errors.Is(err, gorm.ErrRecordNotFound) {
return "", &common.UserNotFoundError{}
} else if err != nil {
return "", err
}
// Create the one-time access token
token, _, err = s.createOneTimeAccessTokenInternal(ctx, userID, ttl, false, tx)
if err != nil {
return "", err
}
// Commit
err = tx.Commit().Error
if err != nil {
return "", err
}
return token, nil
} }
func (s *OneTimeAccessService) createOneTimeAccessTokenInternal(ctx context.Context, userID string, ttl time.Duration, withDeviceToken bool, tx *gorm.DB) (token string, deviceToken *string, err error) { func (s *OneTimeAccessService) createOneTimeAccessTokenInternal(ctx context.Context, userID string, ttl time.Duration, withDeviceToken bool, tx *gorm.DB) (token string, deviceToken *string, err error) {

View File

@@ -72,7 +72,7 @@ export default class UserService extends APIService {
}; };
createOneTimeAccessToken = async (userId: string = 'me', ttl?: string | number) => { createOneTimeAccessToken = async (userId: string = 'me', ttl?: string | number) => {
const res = await this.api.post(`/users/${userId}/one-time-access-token`, { userId, ttl }); const res = await this.api.post(`/users/${userId}/one-time-access-token`, { ttl });
return res.data.token; return res.data.token;
}; };