125 lines
3.0 KiB
Go
125 lines
3.0 KiB
Go
package server
|
|
|
|
import (
|
|
"github.com/gofiber/fiber/v2"
|
|
"time"
|
|
|
|
"code.smartsheep.studio/hydrogen/passport/pkg/security"
|
|
"code.smartsheep.studio/hydrogen/passport/pkg/services"
|
|
"github.com/samber/lo"
|
|
)
|
|
|
|
func startChallenge(c *fiber.Ctx) error {
|
|
var data struct {
|
|
ID string `json:"id"`
|
|
}
|
|
|
|
if err := BindAndValidate(c, &data); err != nil {
|
|
return err
|
|
}
|
|
|
|
user, err := services.LookupAccount(data.ID)
|
|
if err != nil {
|
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
|
}
|
|
factors, err := services.LookupFactorsByUser(user.ID)
|
|
if err != nil {
|
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
|
}
|
|
|
|
challenge, err := security.NewChallenge(user, factors, c.IP(), c.Get(fiber.HeaderUserAgent))
|
|
if err != nil {
|
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
|
}
|
|
|
|
return c.JSON(fiber.Map{
|
|
"display_name": user.Nick,
|
|
"challenge": challenge,
|
|
"factors": factors,
|
|
})
|
|
}
|
|
|
|
func doChallenge(c *fiber.Ctx) error {
|
|
var data struct {
|
|
ChallengeID uint `json:"challenge_id"`
|
|
FactorID uint `json:"factor_id"`
|
|
Secret string `json:"secret"`
|
|
}
|
|
|
|
if err := BindAndValidate(c, &data); err != nil {
|
|
return err
|
|
}
|
|
|
|
challenge, err := services.LookupChallengeWithFingerprint(data.ChallengeID, c.IP(), c.Get(fiber.HeaderUserAgent))
|
|
if err != nil {
|
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
|
}
|
|
|
|
factor, err := services.LookupFactor(data.FactorID)
|
|
if err != nil {
|
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
|
}
|
|
|
|
if err := security.DoChallenge(challenge, factor, data.Secret); err != nil {
|
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
|
}
|
|
|
|
challenge, err = services.LookupChallenge(data.ChallengeID)
|
|
if err != nil {
|
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
|
} else if challenge.Progress >= challenge.Requirements {
|
|
session, err := security.GrantSession(challenge, []string{"*"}, nil, lo.ToPtr(time.Now()))
|
|
if err != nil {
|
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
|
}
|
|
|
|
return c.JSON(fiber.Map{
|
|
"is_finished": true,
|
|
"challenge": challenge,
|
|
"session": session,
|
|
})
|
|
}
|
|
|
|
return c.JSON(fiber.Map{
|
|
"is_finished": false,
|
|
"challenge": challenge,
|
|
"session": nil,
|
|
})
|
|
}
|
|
|
|
func exchangeToken(c *fiber.Ctx) error {
|
|
var data struct {
|
|
Token string `json:"token"`
|
|
GrantType string `json:"grant_type"`
|
|
}
|
|
|
|
if err := BindAndValidate(c, &data); err != nil {
|
|
return err
|
|
}
|
|
|
|
switch data.GrantType {
|
|
case "authorization_code":
|
|
access, refresh, err := security.ExchangeToken(data.Token)
|
|
if err != nil {
|
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
|
}
|
|
|
|
return c.JSON(fiber.Map{
|
|
"access_token": access,
|
|
"refresh_token": refresh,
|
|
})
|
|
case "refresh_token":
|
|
access, refresh, err := security.RefreshToken(data.Token)
|
|
if err != nil {
|
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
|
}
|
|
|
|
return c.JSON(fiber.Map{
|
|
"access_token": access,
|
|
"refresh_token": refresh,
|
|
})
|
|
default:
|
|
return fiber.NewError(fiber.StatusBadRequest, "Unsupported exchange token type.")
|
|
}
|
|
}
|