2024-04-20 17:33:42 +00:00
|
|
|
package ui
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2024-06-17 14:21:34 +00:00
|
|
|
"git.solsynth.dev/hydrogen/passport/pkg/internal/models"
|
2024-06-22 05:04:21 +00:00
|
|
|
"git.solsynth.dev/hydrogen/passport/pkg/internal/server/exts"
|
2024-06-17 14:21:34 +00:00
|
|
|
"git.solsynth.dev/hydrogen/passport/pkg/internal/services"
|
2024-04-20 17:33:42 +00:00
|
|
|
"github.com/gofiber/fiber/v2"
|
|
|
|
"github.com/nicksnyder/go-i18n/v2/i18n"
|
|
|
|
"github.com/samber/lo"
|
|
|
|
"github.com/sujit-baniya/flash"
|
|
|
|
)
|
|
|
|
|
|
|
|
func mfaRequestPage(c *fiber.Ctx) error {
|
|
|
|
ticketId := c.QueryInt("ticket", 0)
|
|
|
|
|
|
|
|
ticket, err := services.GetTicket(uint(ticketId))
|
|
|
|
if err != nil {
|
|
|
|
return flash.WithInfo(c, fiber.Map{
|
|
|
|
"message": "you must provide ticket id to perform multi-factor authenticate",
|
|
|
|
}).Redirect("/sign-in")
|
|
|
|
}
|
|
|
|
user, err := services.GetAccount(ticket.AccountID)
|
|
|
|
if err != nil {
|
|
|
|
return flash.WithInfo(c, fiber.Map{
|
|
|
|
"message": "ticket related user just weirdly disappear",
|
|
|
|
}).Redirect("/sign-in")
|
|
|
|
}
|
|
|
|
factors, err := services.ListUserFactor(user.ID)
|
|
|
|
if err != nil {
|
|
|
|
return flash.WithInfo(c, fiber.Map{
|
|
|
|
"message": fmt.Sprintf("unable to get your factors: %v", err.Error()),
|
|
|
|
}).Redirect("/sign-in")
|
|
|
|
}
|
|
|
|
|
|
|
|
factors = lo.Filter(factors, func(item models.AuthFactor, index int) bool {
|
|
|
|
return item.Type != models.PasswordAuthFactor
|
|
|
|
})
|
|
|
|
|
|
|
|
localizer := c.Locals("localizer").(*i18n.Localizer)
|
|
|
|
|
|
|
|
next, _ := localizer.LocalizeMessage(&i18n.Message{ID: "next"})
|
|
|
|
title, _ := localizer.LocalizeMessage(&i18n.Message{ID: "mfaTitle"})
|
|
|
|
caption, _ := localizer.LocalizeMessage(&i18n.Message{ID: "mfaCaption"})
|
|
|
|
|
|
|
|
return c.Render("views/mfa", fiber.Map{
|
|
|
|
"info": flash.Get(c)["message"],
|
|
|
|
"redirect_uri": flash.Get(c)["redirect_uri"],
|
|
|
|
"ticket_id": ticket.ID,
|
|
|
|
"factors": lo.Map(factors, func(item models.AuthFactor, index int) fiber.Map {
|
|
|
|
return fiber.Map{
|
|
|
|
"name": services.GetFactorName(item.Type, localizer),
|
|
|
|
"id": item.ID,
|
|
|
|
}
|
|
|
|
}),
|
|
|
|
"i18n": fiber.Map{
|
|
|
|
"next": next,
|
|
|
|
"title": title,
|
|
|
|
"caption": caption,
|
|
|
|
},
|
|
|
|
}, "views/layouts/auth")
|
|
|
|
}
|
|
|
|
|
|
|
|
func mfaRequestAction(c *fiber.Ctx) error {
|
|
|
|
var data struct {
|
|
|
|
TicketID uint `form:"ticket_id" validate:"required"`
|
|
|
|
FactorID uint `form:"factor_id" validate:"required"`
|
|
|
|
}
|
|
|
|
|
|
|
|
redirectBackUri := "/sign-in"
|
2024-06-22 05:04:21 +00:00
|
|
|
err := exts.BindAndValidate(c, &data)
|
2024-04-20 17:33:42 +00:00
|
|
|
|
|
|
|
if data.TicketID > 0 {
|
|
|
|
redirectBackUri = fmt.Sprintf("/mfa?ticket=%d", data.TicketID)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return flash.WithInfo(c, fiber.Map{
|
|
|
|
"message": err.Error(),
|
|
|
|
}).Redirect(redirectBackUri)
|
|
|
|
}
|
|
|
|
|
|
|
|
factor, err := services.GetFactor(data.FactorID)
|
|
|
|
if err != nil {
|
|
|
|
return flash.WithInfo(c, fiber.Map{
|
|
|
|
"message": fmt.Sprintf("factor was not found: %v", err.Error()),
|
|
|
|
}).Redirect(redirectBackUri)
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = services.GetFactorCode(factor)
|
|
|
|
if err != nil {
|
|
|
|
return flash.WithInfo(c, fiber.Map{
|
|
|
|
"message": fmt.Sprintf("unable to get factor code: %v", err.Error()),
|
|
|
|
}).Redirect(redirectBackUri)
|
|
|
|
}
|
|
|
|
|
|
|
|
return flash.WithData(c, fiber.Map{
|
2024-06-22 05:04:21 +00:00
|
|
|
"redirect_uri": exts.GetRedirectUri(c),
|
2024-04-20 17:33:42 +00:00
|
|
|
}).Redirect(fmt.Sprintf("/mfa/apply?ticket=%d&factor=%d", data.TicketID, factor.ID))
|
|
|
|
}
|
|
|
|
|
|
|
|
func mfaApplyPage(c *fiber.Ctx) error {
|
|
|
|
ticketId := c.QueryInt("ticket", 0)
|
|
|
|
factorId := c.QueryInt("factor", 0)
|
|
|
|
|
|
|
|
ticket, err := services.GetTicket(uint(ticketId))
|
|
|
|
if err != nil {
|
|
|
|
return flash.WithInfo(c, fiber.Map{
|
|
|
|
"message": fmt.Sprintf("unable to find your ticket: %v", err.Error()),
|
|
|
|
}).Redirect("/sign-in")
|
|
|
|
}
|
|
|
|
factor, err := services.GetFactor(uint(factorId))
|
|
|
|
if err != nil {
|
|
|
|
return flash.WithInfo(c, fiber.Map{
|
|
|
|
"message": fmt.Sprintf("unable to find your factors: %v", err.Error()),
|
|
|
|
}).Redirect("/sign-in")
|
|
|
|
}
|
|
|
|
|
|
|
|
localizer := c.Locals("localizer").(*i18n.Localizer)
|
|
|
|
|
|
|
|
next, _ := localizer.LocalizeMessage(&i18n.Message{ID: "next"})
|
|
|
|
password, _ := localizer.LocalizeMessage(&i18n.Message{ID: "password"})
|
|
|
|
title, _ := localizer.LocalizeMessage(&i18n.Message{ID: "mfaTitle"})
|
|
|
|
caption, _ := localizer.LocalizeMessage(&i18n.Message{ID: "mfaCaption"})
|
|
|
|
|
|
|
|
return c.Render("views/mfa-apply", fiber.Map{
|
|
|
|
"info": flash.Get(c)["message"],
|
|
|
|
"label": services.GetFactorName(factor.Type, localizer),
|
|
|
|
"ticket_id": ticket.ID,
|
|
|
|
"factor_id": factor.ID,
|
|
|
|
"i18n": fiber.Map{
|
|
|
|
"next": next,
|
|
|
|
"password": password,
|
|
|
|
"title": title,
|
|
|
|
"caption": caption,
|
|
|
|
},
|
|
|
|
}, "views/layouts/auth")
|
|
|
|
}
|
|
|
|
|
|
|
|
func mfaApplyAction(c *fiber.Ctx) error {
|
|
|
|
var data struct {
|
|
|
|
TicketID uint `form:"ticket_id" validate:"required"`
|
|
|
|
FactorID uint `form:"factor_id" validate:"required"`
|
|
|
|
Code string `form:"code" validate:"required"`
|
|
|
|
}
|
|
|
|
|
|
|
|
redirectBackUri := "/sign-in"
|
2024-06-22 05:04:21 +00:00
|
|
|
err := exts.BindAndValidate(c, &data)
|
2024-04-20 17:33:42 +00:00
|
|
|
|
|
|
|
if data.TicketID > 0 {
|
|
|
|
redirectBackUri = fmt.Sprintf("/mfa/apply?ticket=%d&factor=%d", data.TicketID, data.FactorID)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return flash.WithInfo(c, fiber.Map{
|
|
|
|
"message": err.Error(),
|
|
|
|
}).Redirect(redirectBackUri)
|
|
|
|
}
|
|
|
|
|
|
|
|
ticket, err := services.GetTicket(data.TicketID)
|
|
|
|
if err != nil {
|
|
|
|
return flash.WithInfo(c, fiber.Map{
|
|
|
|
"message": fmt.Sprintf("unable to find your ticket: %v", err.Error()),
|
|
|
|
}).Redirect("/sign-in")
|
|
|
|
}
|
|
|
|
factor, err := services.GetFactor(data.FactorID)
|
|
|
|
if err != nil {
|
|
|
|
return flash.WithInfo(c, fiber.Map{
|
|
|
|
"message": fmt.Sprintf("factor was not found: %v", err.Error()),
|
|
|
|
}).Redirect(redirectBackUri)
|
|
|
|
}
|
|
|
|
|
|
|
|
ticket, err = services.ActiveTicketWithMFA(ticket, factor, data.Code)
|
|
|
|
if err != nil {
|
|
|
|
return flash.WithInfo(c, fiber.Map{
|
|
|
|
"message": fmt.Sprintf("invalid multi-factor authenticate code: %v", err.Error()),
|
|
|
|
}).Redirect(redirectBackUri)
|
|
|
|
} else if ticket.IsAvailable() != nil {
|
|
|
|
return flash.WithInfo(c, fiber.Map{
|
|
|
|
"message": "ticket weirdly still unavailable after multi-factor authenticate",
|
|
|
|
}).Redirect("/sign-in")
|
|
|
|
}
|
|
|
|
|
|
|
|
access, refresh, err := services.ExchangeToken(*ticket.GrantToken)
|
|
|
|
if err != nil {
|
|
|
|
return flash.WithInfo(c, fiber.Map{
|
|
|
|
"message": fmt.Sprintf("failed to exchange token: %v", err.Error()),
|
|
|
|
}).Redirect("/sign-in")
|
|
|
|
} else {
|
2024-06-22 05:04:21 +00:00
|
|
|
exts.SetAuthCookies(c, access, refresh)
|
2024-04-20 17:33:42 +00:00
|
|
|
}
|
|
|
|
|
2024-06-22 05:04:21 +00:00
|
|
|
return c.Redirect(lo.FromPtr(exts.GetRedirectUri(c, "/users/me")))
|
2024-04-20 17:33:42 +00:00
|
|
|
}
|