mirror of
https://github.com/pocket-id/pocket-id.git
synced 2026-08-22 04:46:39 +00:00
133 lines
3.9 KiB
Go
133 lines
3.9 KiB
Go
package onetimeaccess
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/pocket-id/pocket-id/backend/internal/common"
|
|
"github.com/pocket-id/pocket-id/backend/internal/dto"
|
|
"github.com/pocket-id/pocket-id/backend/internal/utils/cookie"
|
|
httpapi "github.com/pocket-id/pocket-id/backend/internal/utils/huma"
|
|
)
|
|
|
|
const defaultTokenDuration = 15 * time.Minute
|
|
|
|
type tokenCreateInput struct {
|
|
ID string `path:"id"`
|
|
Body tokenCreateDto
|
|
}
|
|
|
|
type ownTokenCreateInput struct {
|
|
Body tokenCreateDto
|
|
}
|
|
|
|
type emailAsUnauthenticatedUserInput struct {
|
|
Body emailAsUnauthenticatedUserDto
|
|
}
|
|
|
|
type emailAsAdminInput struct {
|
|
ID string `path:"id"`
|
|
Body emailAsAdminDto
|
|
}
|
|
|
|
type tokenExchangeInput struct {
|
|
Token string `path:"token"`
|
|
}
|
|
|
|
type cookieOutput struct {
|
|
SetCookie []http.Cookie `header:"Set-Cookie"`
|
|
}
|
|
|
|
type userOutput struct {
|
|
SetCookie []http.Cookie `header:"Set-Cookie"`
|
|
Body dto.UserDto
|
|
}
|
|
|
|
type handler struct {
|
|
service *Service
|
|
appConfig AppConfigResolver
|
|
}
|
|
|
|
func newHandler(service *Service, appConfig AppConfigResolver) *handler {
|
|
return &handler{service: service, appConfig: appConfig}
|
|
}
|
|
|
|
func (h *handler) createOwnToken(ctx context.Context, _ *ownTokenCreateInput) (*httpapi.BodyOutput[map[string]string], error) {
|
|
return h.createToken(ctx, httpapi.UserID(ctx), defaultTokenDuration)
|
|
}
|
|
|
|
func (h *handler) createTokenForUser(ctx context.Context, input *tokenCreateInput) (*httpapi.BodyOutput[map[string]string], error) {
|
|
ttl := input.Body.TTL.Duration
|
|
if ttl <= 0 {
|
|
ttl = defaultTokenDuration
|
|
}
|
|
return h.createToken(ctx, input.ID, ttl)
|
|
}
|
|
|
|
func (h *handler) createToken(ctx context.Context, userID string, ttl time.Duration) (*httpapi.BodyOutput[map[string]string], error) {
|
|
if userID == "" {
|
|
return nil, &common.UserIdNotProvidedError{}
|
|
}
|
|
token, err := h.service.CreateToken(ctx, userID, ttl)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &httpapi.BodyOutput[map[string]string]{Body: map[string]string{"token": token}}, nil
|
|
}
|
|
|
|
func (h *handler) requestEmailAsUnauthenticatedUser(ctx context.Context, input *emailAsUnauthenticatedUserInput) (*cookieOutput, error) {
|
|
dbConfig, err := h.appConfig.GetConfig(ctx)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error loading app configuration: %w", err)
|
|
}
|
|
|
|
deviceToken, err := h.service.RequestOneTimeAccessEmailAsUnauthenticatedUser(ctx, dbConfig, input.Body.Email, input.Body.RedirectPath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &cookieOutput{SetCookie: []http.Cookie{*cookie.NewDeviceTokenCookie(deviceToken)}}, nil
|
|
}
|
|
|
|
func (h *handler) requestEmailAsAdmin(ctx context.Context, input *emailAsAdminInput) (*httpapi.EmptyOutput, error) {
|
|
dbConfig, err := h.appConfig.GetConfig(ctx)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error loading app configuration: %w", err)
|
|
}
|
|
|
|
ttl := input.Body.TTL.Duration
|
|
if ttl <= 0 {
|
|
ttl = defaultTokenDuration
|
|
}
|
|
if err := h.service.RequestOneTimeAccessEmailAsAdmin(ctx, dbConfig, input.ID, ttl); err != nil {
|
|
return nil, err
|
|
}
|
|
return &httpapi.EmptyOutput{}, nil
|
|
}
|
|
|
|
func (h *handler) exchangeToken(ctx context.Context, input *tokenExchangeInput) (*userOutput, error) {
|
|
dbConfig, err := h.appConfig.GetConfig(ctx)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error loading app configuration: %w", err)
|
|
}
|
|
|
|
if len(input.Token) != 6 && len(input.Token) != 16 {
|
|
return nil, &common.TokenInvalidOrExpiredError{}
|
|
}
|
|
deviceToken := ""
|
|
if requestCookie, err := httpapi.Cookie(ctx, cookie.DeviceTokenCookieName); err == nil {
|
|
deviceToken = requestCookie.Value
|
|
}
|
|
user, token, err := h.service.ExchangeToken(ctx, dbConfig, input.Token, deviceToken, httpapi.ClientIP(ctx), httpapi.UserAgent(ctx))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var output dto.UserDto
|
|
if err := dto.MapStruct(user, &output); err != nil {
|
|
return nil, err
|
|
}
|
|
maxAge := int(dbConfig.SessionDuration.AsDurationMinutes().Seconds())
|
|
return &userOutput{SetCookie: []http.Cookie{*cookie.NewAccessTokenCookie(maxAge, token)}, Body: output}, nil
|
|
}
|