mirror of
https://github.com/makayabou/asg-server.git
synced 2026-05-02 17:43:36 +02:00
[mobile] add registration by one-time code
This commit is contained in:
parent
8127d9d824
commit
a8b23f4dc1
@ -7,6 +7,11 @@ import (
|
|||||||
// @securitydefinitions.basic ApiAuth
|
// @securitydefinitions.basic ApiAuth
|
||||||
// @description User authentication
|
// @description User authentication
|
||||||
|
|
||||||
|
// @securitydefinitions.apikey UserCode
|
||||||
|
// @in header
|
||||||
|
// @name Authorization
|
||||||
|
// @description User one-time code authentication
|
||||||
|
|
||||||
// @securitydefinitions.apikey MobileToken
|
// @securitydefinitions.apikey MobileToken
|
||||||
// @in header
|
// @in header
|
||||||
// @name Authorization
|
// @name Authorization
|
||||||
|
|||||||
4
go.mod
4
go.mod
@ -6,9 +6,9 @@ toolchain go1.23.2
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
firebase.google.com/go/v4 v4.12.1
|
firebase.google.com/go/v4 v4.12.1
|
||||||
github.com/android-sms-gateway/client-go v1.5.4
|
github.com/android-sms-gateway/client-go v1.5.6
|
||||||
github.com/ansrivas/fiberprometheus/v2 v2.6.1
|
github.com/ansrivas/fiberprometheus/v2 v2.6.1
|
||||||
github.com/capcom6/go-helpers v0.1.1
|
github.com/capcom6/go-helpers v0.2.0
|
||||||
github.com/capcom6/go-infra-fx v0.2.1
|
github.com/capcom6/go-infra-fx v0.2.1
|
||||||
github.com/go-playground/assert/v2 v2.2.0
|
github.com/go-playground/assert/v2 v2.2.0
|
||||||
github.com/go-playground/validator/v10 v10.16.0
|
github.com/go-playground/validator/v10 v10.16.0
|
||||||
|
|||||||
8
go.sum
8
go.sum
@ -28,6 +28,10 @@ github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEV
|
|||||||
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk=
|
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk=
|
||||||
github.com/android-sms-gateway/client-go v1.5.4 h1:sFMqu+Lc+YtkasesmerckVV8KKL8Qcx/VPEUWvcfbyA=
|
github.com/android-sms-gateway/client-go v1.5.4 h1:sFMqu+Lc+YtkasesmerckVV8KKL8Qcx/VPEUWvcfbyA=
|
||||||
github.com/android-sms-gateway/client-go v1.5.4/go.mod h1:DQsReciU1xcaVW3T5Z2bqslNdsAwCFCtghawmA6g6L4=
|
github.com/android-sms-gateway/client-go v1.5.4/go.mod h1:DQsReciU1xcaVW3T5Z2bqslNdsAwCFCtghawmA6g6L4=
|
||||||
|
github.com/android-sms-gateway/client-go v1.5.6-0.20250326025946-2625b20dcccd h1:VuSsDc7HeRllPmVrFCmMi0ksFDWEDoUEHFuua4ccJ0s=
|
||||||
|
github.com/android-sms-gateway/client-go v1.5.6-0.20250326025946-2625b20dcccd/go.mod h1:DQsReciU1xcaVW3T5Z2bqslNdsAwCFCtghawmA6g6L4=
|
||||||
|
github.com/android-sms-gateway/client-go v1.5.6 h1:sCiBT1Fn7QBaTlX7Z3eBGDcG+u+3sADmp+rxb0HWuaA=
|
||||||
|
github.com/android-sms-gateway/client-go v1.5.6/go.mod h1:DQsReciU1xcaVW3T5Z2bqslNdsAwCFCtghawmA6g6L4=
|
||||||
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
|
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
|
||||||
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
|
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
|
||||||
github.com/ansrivas/fiberprometheus/v2 v2.6.1 h1:wac3pXaE6BYYTF04AC6K0ktk6vCD+MnDOJZ3SK66kXM=
|
github.com/ansrivas/fiberprometheus/v2 v2.6.1 h1:wac3pXaE6BYYTF04AC6K0ktk6vCD+MnDOJZ3SK66kXM=
|
||||||
@ -39,6 +43,10 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
|||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
github.com/capcom6/go-helpers v0.1.1 h1:kcpK1+VUwo94MZlZX+0Gab4gf78egHTPzW9sOQXLfFE=
|
github.com/capcom6/go-helpers v0.1.1 h1:kcpK1+VUwo94MZlZX+0Gab4gf78egHTPzW9sOQXLfFE=
|
||||||
github.com/capcom6/go-helpers v0.1.1/go.mod h1:WDqc7HZNqHxUTisArkYIBZtqUfJBVyPWeQI+FMwEzAw=
|
github.com/capcom6/go-helpers v0.1.1/go.mod h1:WDqc7HZNqHxUTisArkYIBZtqUfJBVyPWeQI+FMwEzAw=
|
||||||
|
github.com/capcom6/go-helpers v0.1.2-0.20250326030929-5d1f34b4936b h1:grbupORuCS6EJV/IMdEijRwmW7M91bpgqqex/TRKg38=
|
||||||
|
github.com/capcom6/go-helpers v0.1.2-0.20250326030929-5d1f34b4936b/go.mod h1:WDqc7HZNqHxUTisArkYIBZtqUfJBVyPWeQI+FMwEzAw=
|
||||||
|
github.com/capcom6/go-helpers v0.2.0 h1:OUcUnVbjBiwaTzvyaxkxqRKtrOXv1ifYalQ1NXzFBNM=
|
||||||
|
github.com/capcom6/go-helpers v0.2.0/go.mod h1:WDqc7HZNqHxUTisArkYIBZtqUfJBVyPWeQI+FMwEzAw=
|
||||||
github.com/capcom6/go-infra-fx v0.2.1 h1:8rqr2ZV+YC2R07amHMdlE1XKLUhMe5yO+ffCJ/xXlNY=
|
github.com/capcom6/go-infra-fx v0.2.1 h1:8rqr2ZV+YC2R07amHMdlE1XKLUhMe5yO+ffCJ/xXlNY=
|
||||||
github.com/capcom6/go-infra-fx v0.2.1/go.mod h1:klScvB8QAKgJ19FfJOnUKK5tI0o9b79Aj2RmCJHfbN0=
|
github.com/capcom6/go-infra-fx v0.2.1/go.mod h1:klScvB8QAKgJ19FfJOnUKK5tI0o9b79Aj2RmCJHfbN0=
|
||||||
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
|
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
|
||||||
|
|||||||
@ -47,7 +47,7 @@ func (h *thirdPartyHandler) Register(router fiber.Router) {
|
|||||||
h.healthHandler.Register(router)
|
h.healthHandler.Register(router)
|
||||||
|
|
||||||
router.Use(
|
router.Use(
|
||||||
userauth.New(h.authSvc),
|
userauth.NewBasic(h.authSvc),
|
||||||
userauth.UserRequired(),
|
userauth.UserRequired(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -12,14 +12,15 @@ import (
|
|||||||
|
|
||||||
const localsUser = "user"
|
const localsUser = "user"
|
||||||
|
|
||||||
// New returns a middleware that checks for a valid "Authorization" header
|
// NewBasic returns a middleware that will check if the request contains a valid
|
||||||
// in the form of "Basic <base64-encoded credentials>" and authorizes the user.
|
// "Authorization" header in the form of "Basic <base64 encoded username:password>".
|
||||||
func New(authSvc *auth.Service) fiber.Handler {
|
// If the header is valid, the middleware will authorize the user and store the
|
||||||
|
// user in the request's Locals under the key LocalsUser. If the header is invalid,
|
||||||
|
// the middleware will call c.Next() and continue with the request.
|
||||||
|
func NewBasic(authSvc *auth.Service) fiber.Handler {
|
||||||
return func(c *fiber.Ctx) error {
|
return func(c *fiber.Ctx) error {
|
||||||
// Get authorization header
|
|
||||||
auth := c.Get(fiber.HeaderAuthorization)
|
auth := c.Get(fiber.HeaderAuthorization)
|
||||||
|
|
||||||
// Check if the header contains content besides "basic".
|
|
||||||
if len(auth) <= 6 || !strings.EqualFold(auth[:6], "basic ") {
|
if len(auth) <= 6 || !strings.EqualFold(auth[:6], "basic ") {
|
||||||
return c.Next()
|
return c.Next()
|
||||||
}
|
}
|
||||||
@ -55,6 +56,33 @@ func New(authSvc *auth.Service) fiber.Handler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewCode returns a middleware that will check if the request contains a valid
|
||||||
|
// "Authorization" header in the form of "Code <one-time user authorization code>".
|
||||||
|
// If the header is valid, the middleware will authorize the user and store the
|
||||||
|
// user in the request's Locals under the key LocalsUser. If the header is invalid,
|
||||||
|
// the middleware will call c.Next() and continue with the request.
|
||||||
|
func NewCode(authSvc *auth.Service) fiber.Handler {
|
||||||
|
return func(c *fiber.Ctx) error {
|
||||||
|
auth := c.Get(fiber.HeaderAuthorization)
|
||||||
|
|
||||||
|
if len(auth) <= 5 || !strings.EqualFold(auth[:5], "code ") {
|
||||||
|
return c.Next()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the code
|
||||||
|
code := auth[5:]
|
||||||
|
|
||||||
|
user, err := authSvc.AuthorizeUserByCode(code)
|
||||||
|
if err != nil {
|
||||||
|
return fiber.ErrUnauthorized
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Locals(localsUser, user)
|
||||||
|
|
||||||
|
return c.Next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// HasUser checks if a user is present in the Locals of the given context.
|
// HasUser checks if a user is present in the Locals of the given context.
|
||||||
// It returns true if the Locals contain a user under the key LocalsUser,
|
// It returns true if the Locals contain a user under the key LocalsUser,
|
||||||
// otherwise returns false.
|
// otherwise returns false.
|
||||||
|
|||||||
@ -60,6 +60,7 @@ func (h *mobileHandler) getDevice(device models.Device, c *fiber.Ctx) error {
|
|||||||
// @Summary Register device
|
// @Summary Register device
|
||||||
// @Description Registers new device for new or existing user. Returns user credentials only for new users
|
// @Description Registers new device for new or existing user. Returns user credentials only for new users
|
||||||
// @Security ApiAuth
|
// @Security ApiAuth
|
||||||
|
// @Security UserCode
|
||||||
// @Security ServerKey
|
// @Security ServerKey
|
||||||
// @Tags Device
|
// @Tags Device
|
||||||
// @Accept json
|
// @Accept json
|
||||||
@ -198,6 +199,29 @@ func (h *mobileHandler) patchMessage(device models.Device, c *fiber.Ctx) error {
|
|||||||
return c.SendStatus(fiber.StatusNoContent)
|
return c.SendStatus(fiber.StatusNoContent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Summary Get one-time code for device registration
|
||||||
|
// @Description Returns one-time code for device registration
|
||||||
|
// @Security ApiAuth
|
||||||
|
// @Tags Device
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 {object} smsgateway.MobileUserCodeResponse "User code"
|
||||||
|
// @Failure 500 {object} smsgateway.ErrorResponse "Internal server error"
|
||||||
|
// @Router /mobile/v1/user/code [get]
|
||||||
|
//
|
||||||
|
// Get user code
|
||||||
|
func (h *mobileHandler) getUserCode(user models.User, c *fiber.Ctx) error {
|
||||||
|
code, err := h.authSvc.GenerateUserCode(user.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(smsgateway.MobileUserCodeResponse{
|
||||||
|
Code: code.Code,
|
||||||
|
ValidUntil: code.ValidUntil,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// @Summary Change password
|
// @Summary Change password
|
||||||
// @Description Changes the user's password
|
// @Description Changes the user's password
|
||||||
// @Security MobileToken
|
// @Security MobileToken
|
||||||
@ -231,7 +255,8 @@ func (h *mobileHandler) Register(router fiber.Router) {
|
|||||||
router = router.Group("/mobile/v1")
|
router = router.Group("/mobile/v1")
|
||||||
|
|
||||||
router.Post("/device",
|
router.Post("/device",
|
||||||
userauth.New(h.authSvc),
|
userauth.NewBasic(h.authSvc),
|
||||||
|
userauth.NewCode(h.authSvc),
|
||||||
keyauth.New(keyauth.Config{
|
keyauth.New(keyauth.Config{
|
||||||
Next: func(c *fiber.Ctx) bool {
|
Next: func(c *fiber.Ctx) bool {
|
||||||
// Skip server key authorization in the following cases:
|
// Skip server key authorization in the following cases:
|
||||||
@ -247,6 +272,12 @@ func (h *mobileHandler) Register(router fiber.Router) {
|
|||||||
h.postDevice,
|
h.postDevice,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
router.Get("/user/code",
|
||||||
|
userauth.NewBasic(h.authSvc),
|
||||||
|
userauth.UserRequired(),
|
||||||
|
userauth.WithUser(h.getUserCode),
|
||||||
|
)
|
||||||
|
|
||||||
router.Use(
|
router.Use(
|
||||||
deviceauth.New(h.authSvc),
|
deviceauth.New(h.authSvc),
|
||||||
)
|
)
|
||||||
@ -260,6 +291,7 @@ func (h *mobileHandler) Register(router fiber.Router) {
|
|||||||
router.Get("/message", deviceauth.WithDevice(h.getMessage))
|
router.Get("/message", deviceauth.WithDevice(h.getMessage))
|
||||||
router.Patch("/message", deviceauth.WithDevice(h.patchMessage))
|
router.Patch("/message", deviceauth.WithDevice(h.patchMessage))
|
||||||
|
|
||||||
|
// Should be under `userauth.NewBasic` protection instead of `deviceauth`
|
||||||
router.Patch("/user/password", deviceauth.WithDevice(h.changePassword))
|
router.Patch("/user/password", deviceauth.WithDevice(h.changePassword))
|
||||||
|
|
||||||
h.webhooksCtrl.Register(router.Group("/webhooks"))
|
h.webhooksCtrl.Register(router.Group("/webhooks"))
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
package auth
|
package auth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
"go.uber.org/fx"
|
"go.uber.org/fx"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
@ -12,4 +14,17 @@ var Module = fx.Module(
|
|||||||
}),
|
}),
|
||||||
fx.Provide(New),
|
fx.Provide(New),
|
||||||
fx.Provide(newRepository, fx.Private),
|
fx.Provide(newRepository, fx.Private),
|
||||||
|
fx.Invoke(func(lc fx.Lifecycle, svc *Service) {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
lc.Append(fx.Hook{
|
||||||
|
OnStart: func(_ context.Context) error {
|
||||||
|
go svc.Run(ctx)
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
OnStop: func(_ context.Context) error {
|
||||||
|
cancel()
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}),
|
||||||
)
|
)
|
||||||
|
|||||||
@ -15,6 +15,13 @@ func newRepository(db *gorm.DB) *repository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetByID returns a user by their ID.
|
||||||
|
func (r *repository) GetByID(id string) (models.User, error) {
|
||||||
|
user := models.User{}
|
||||||
|
|
||||||
|
return user, r.db.Where("id = ?", id).Take(&user).Error
|
||||||
|
}
|
||||||
|
|
||||||
func (r *repository) GetByLogin(login string) (models.User, error) {
|
func (r *repository) GetByLogin(login string) (models.User, error) {
|
||||||
user := models.User{}
|
user := models.User{}
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
package auth
|
package auth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/rand"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"crypto/subtle"
|
"crypto/subtle"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
@ -36,6 +38,7 @@ type Service struct {
|
|||||||
config Config
|
config Config
|
||||||
|
|
||||||
users *repository
|
users *repository
|
||||||
|
codesCache *cache.Cache[string]
|
||||||
usersCache *cache.Cache[models.User]
|
usersCache *cache.Cache[models.User]
|
||||||
|
|
||||||
devicesSvc *devices.Service
|
devicesSvc *devices.Service
|
||||||
@ -55,10 +58,39 @@ func New(params Params) *Service {
|
|||||||
logger: params.Logger.Named("Service"),
|
logger: params.Logger.Named("Service"),
|
||||||
idgen: idgen,
|
idgen: idgen,
|
||||||
|
|
||||||
|
codesCache: cache.New[string](cache.Config{}),
|
||||||
usersCache: cache.New[models.User](cache.Config{TTL: 1 * time.Hour}),
|
usersCache: cache.New[models.User](cache.Config{TTL: 1 * time.Hour}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GenerateUserCode generates a unique one-time user authorization code
|
||||||
|
func (s *Service) GenerateUserCode(userID string) (AuthCode, error) {
|
||||||
|
var code string
|
||||||
|
var err error
|
||||||
|
|
||||||
|
b := make([]byte, 3)
|
||||||
|
validUntil := time.Now().Add(codeTTL)
|
||||||
|
for range 3 {
|
||||||
|
if _, err = rand.Read(b); err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
num := (int(b[0]) << 16) | (int(b[1]) << 8) | int(b[2])
|
||||||
|
code = fmt.Sprintf("%06d", num%1000000)
|
||||||
|
|
||||||
|
if err = s.codesCache.SetOrFail(code, userID, cache.WithValidUntil(validUntil)); err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return AuthCode{}, fmt.Errorf("can't generate code: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return AuthCode{Code: code, ValidUntil: validUntil}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Service) RegisterUser(login, password string) (models.User, error) {
|
func (s *Service) RegisterUser(login, password string) (models.User, error) {
|
||||||
user := models.User{
|
user := models.User{
|
||||||
ID: login,
|
ID: login,
|
||||||
@ -143,6 +175,21 @@ func (s *Service) AuthorizeUser(username, password string) (models.User, error)
|
|||||||
return user, nil
|
return user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AuthorizeUserByCode authorizes a user by one-time code.
|
||||||
|
func (s *Service) AuthorizeUserByCode(code string) (models.User, error) {
|
||||||
|
userID, err := s.codesCache.GetAndDelete(code)
|
||||||
|
if err != nil {
|
||||||
|
return models.User{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
user, err := s.users.GetByID(userID)
|
||||||
|
if err != nil {
|
||||||
|
return models.User{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return user, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Service) ChangePassword(userID string, currentPassword string, newPassword string) error {
|
func (s *Service) ChangePassword(userID string, currentPassword string, newPassword string) error {
|
||||||
user, err := s.users.GetByLogin(userID)
|
user, err := s.users.GetByLogin(userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -171,3 +218,24 @@ func (s *Service) ChangePassword(userID string, currentPassword string, newPassw
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Run starts a ticker that triggers the clean function every hour.
|
||||||
|
// It runs indefinitely until the provided context is canceled.
|
||||||
|
func (s *Service) Run(ctx context.Context) {
|
||||||
|
ticker := time.NewTicker(1 * time.Hour)
|
||||||
|
defer ticker.Stop()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
case <-ticker.C:
|
||||||
|
s.clean(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) clean(_ context.Context) {
|
||||||
|
s.codesCache.Cleanup()
|
||||||
|
s.usersCache.Cleanup()
|
||||||
|
}
|
||||||
|
|||||||
@ -1,8 +1,18 @@
|
|||||||
package auth
|
package auth
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
const codeTTL = 5 * time.Minute
|
||||||
|
|
||||||
type Mode string
|
type Mode string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ModePublic Mode = "public"
|
ModePublic Mode = "public"
|
||||||
ModePrivate Mode = "private"
|
ModePrivate Mode = "private"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// AuthCode is a one-time user authorization code
|
||||||
|
type AuthCode struct {
|
||||||
|
Code string
|
||||||
|
ValidUntil time.Time
|
||||||
|
}
|
||||||
|
|||||||
@ -7,10 +7,15 @@
|
|||||||
GET {{baseUrl}}/device HTTP/1.1
|
GET {{baseUrl}}/device HTTP/1.1
|
||||||
Authorization: Bearer {{mobileToken}}
|
Authorization: Bearer {{mobileToken}}
|
||||||
|
|
||||||
|
###
|
||||||
|
GET {{baseUrl}}/user/code HTTP/1.1
|
||||||
|
Authorization: Bearer {{mobileToken}}
|
||||||
|
|
||||||
###
|
###
|
||||||
POST {{baseUrl}}/device HTTP/1.1
|
POST {{baseUrl}}/device HTTP/1.1
|
||||||
# Authorization: Bearer 123456789
|
# Authorization: Bearer 123456789
|
||||||
Authorization: Basic {{credentials}}
|
# Authorization: Basic {{credentials}}
|
||||||
|
# Authorization: Code 065379
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|||||||
@ -567,6 +567,9 @@
|
|||||||
{
|
{
|
||||||
"ApiAuth": []
|
"ApiAuth": []
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"UserCode": []
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ServerKey": []
|
"ServerKey": []
|
||||||
}
|
}
|
||||||
@ -764,6 +767,40 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/mobile/v1/user/code": {
|
||||||
|
"get": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Returns one-time code for device registration",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Device"
|
||||||
|
],
|
||||||
|
"summary": "Get one-time code for device registration",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "User code",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/smsgateway.MobileUserCodeResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal server error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/smsgateway.ErrorResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/mobile/v1/user/password": {
|
"/mobile/v1/user/password": {
|
||||||
"patch": {
|
"patch": {
|
||||||
"security": [
|
"security": [
|
||||||
@ -1319,6 +1356,19 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"smsgateway.MobileUserCodeResponse": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"code": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "123456"
|
||||||
|
},
|
||||||
|
"validUntil": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "2020-01-01T00:00:00Z"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"smsgateway.ProcessingState": {
|
"smsgateway.ProcessingState": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
@ -1483,6 +1533,12 @@
|
|||||||
"type": "apiKey",
|
"type": "apiKey",
|
||||||
"name": "Authorization",
|
"name": "Authorization",
|
||||||
"in": "header"
|
"in": "header"
|
||||||
|
},
|
||||||
|
"UserCode": {
|
||||||
|
"description": "User one-time code authentication",
|
||||||
|
"type": "apiKey",
|
||||||
|
"name": "Authorization",
|
||||||
|
"in": "header"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -301,6 +301,15 @@ definitions:
|
|||||||
maxLength: 256
|
maxLength: 256
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
|
smsgateway.MobileUserCodeResponse:
|
||||||
|
properties:
|
||||||
|
code:
|
||||||
|
example: "123456"
|
||||||
|
type: string
|
||||||
|
validUntil:
|
||||||
|
example: "2020-01-01T00:00:00Z"
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
smsgateway.ProcessingState:
|
smsgateway.ProcessingState:
|
||||||
enum:
|
enum:
|
||||||
- Pending
|
- Pending
|
||||||
@ -848,6 +857,7 @@ paths:
|
|||||||
$ref: '#/definitions/smsgateway.ErrorResponse'
|
$ref: '#/definitions/smsgateway.ErrorResponse'
|
||||||
security:
|
security:
|
||||||
- ApiAuth: []
|
- ApiAuth: []
|
||||||
|
- UserCode: []
|
||||||
- ServerKey: []
|
- ServerKey: []
|
||||||
summary: Register device
|
summary: Register device
|
||||||
tags:
|
tags:
|
||||||
@ -908,6 +918,27 @@ paths:
|
|||||||
tags:
|
tags:
|
||||||
- Device
|
- Device
|
||||||
- Messages
|
- Messages
|
||||||
|
/mobile/v1/user/code:
|
||||||
|
get:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: Returns one-time code for device registration
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: User code
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/smsgateway.MobileUserCodeResponse'
|
||||||
|
"500":
|
||||||
|
description: Internal server error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/smsgateway.ErrorResponse'
|
||||||
|
security:
|
||||||
|
- ApiAuth: []
|
||||||
|
summary: Get one-time code for device registration
|
||||||
|
tags:
|
||||||
|
- Device
|
||||||
/mobile/v1/user/password:
|
/mobile/v1/user/password:
|
||||||
patch:
|
patch:
|
||||||
consumes:
|
consumes:
|
||||||
@ -1017,4 +1048,9 @@ securityDefinitions:
|
|||||||
in: header
|
in: header
|
||||||
name: Authorization
|
name: Authorization
|
||||||
type: apiKey
|
type: apiKey
|
||||||
|
UserCode:
|
||||||
|
description: User one-time code authentication
|
||||||
|
in: header
|
||||||
|
name: Authorization
|
||||||
|
type: apiKey
|
||||||
swagger: "2.0"
|
swagger: "2.0"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user