♻️ Improve code structure and much easier to read

🐛 Fix auth middleware
This commit is contained in:
LittleSheep 2024-06-22 13:04:21 +08:00
parent c37a55b88b
commit 7007cda8f2
34 changed files with 451 additions and 337 deletions

95
.idea/workspace.xml generated
View File

@ -4,13 +4,40 @@
<option name="autoReloadType" value="ALL" />
</component>
<component name="ChangeListManager">
<list default="true" id="3fefb2c4-b6f9-466b-a523-53352e8d6f95" name="更改" comment=":sparkles: Drop direct connection and uses consul">
<change afterPath="$PROJECT_DIR$/pkg/hyper/auth.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/pkg/hyper/auth_adaptor.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/pkg/hyper/conn.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/dataSources.local.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/dataSources.local.xml" afterDir="false" />
<list default="true" id="3fefb2c4-b6f9-466b-a523-53352e8d6f95" name="更改" comment=":technologist: Add the server side Hyper SDK">
<change afterPath="$PROJECT_DIR$/pkg/internal/server/api/index.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/pkg/internal/server/exts/cookies.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/pkg/internal/gap/server.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/gap/server.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/pkg/hyper/auth_adaptor.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/hyper/auth_adaptor.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/pkg/internal/embed.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/embed.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/pkg/internal/server/accounts_api.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/api/accounts_api.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/pkg/internal/server/admin/badges_api.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/admin/badges_api.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/pkg/internal/server/admin/index.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/admin/index.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/pkg/internal/server/auth_api.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/api/auth_api.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/pkg/internal/server/auth_middleware.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/exts/auth.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/pkg/internal/server/avatar_api.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/api/avatar_api.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/pkg/internal/server/factors_api.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/api/factors_api.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/pkg/internal/server/friendships_api.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/api/friendships_api.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/pkg/internal/server/notifications_api.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/api/notifications_api.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/pkg/internal/server/notify_api.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/api/notify_api.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/pkg/internal/server/page_api.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/api/page_api.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/pkg/internal/server/realm_members_api.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/api/realm_members_api.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/pkg/internal/server/realms_api.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/api/realms_api.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/pkg/internal/server/security_api.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/api/security_api.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/pkg/internal/server/server.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/server.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/pkg/internal/server/ui/accounts.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/ui/accounts.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/pkg/internal/server/ui/index.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/ui/index.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/pkg/internal/server/ui/mfa.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/ui/mfa.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/pkg/internal/server/ui/oauth.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/ui/oauth.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/pkg/internal/server/ui/signin.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/ui/signin.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/pkg/internal/server/ui/signup.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/ui/signup.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/pkg/internal/server/userinfo_api.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/api/userinfo_api.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/pkg/internal/server/well_known_api.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/api/well_known_api.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/pkg/internal/server/ws.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/api/ws.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/pkg/internal/services/auth.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/services/auth.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/pkg/internal/services/jwt.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/services/jwt.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/pkg/internal/utils/auth.go" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/pkg/internal/utils/request.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/exts/request.go" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
@ -45,33 +72,33 @@
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent">{
&quot;keyToString&quot;: {
&quot;DefaultGoTemplateProperty&quot;: &quot;Go File&quot;,
&quot;Go Build.Backend.executor&quot;: &quot;Run&quot;,
&quot;Go 构建.Backend.executor&quot;: &quot;Run&quot;,
&quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
&quot;RunOnceActivity.go.formatter.settings.were.checked&quot;: &quot;true&quot;,
&quot;RunOnceActivity.go.migrated.go.modules.settings&quot;: &quot;true&quot;,
&quot;RunOnceActivity.go.modules.automatic.dependencies.download&quot;: &quot;true&quot;,
&quot;RunOnceActivity.go.modules.go.list.on.any.changes.was.set&quot;: &quot;true&quot;,
&quot;git-widget-placeholder&quot;: &quot;features/consul&quot;,
&quot;go.import.settings.migrated&quot;: &quot;true&quot;,
&quot;go.sdk.automatically.set&quot;: &quot;true&quot;,
&quot;last_opened_file_path&quot;: &quot;/Users/littlesheep&quot;,
&quot;node.js.detected.package.eslint&quot;: &quot;true&quot;,
&quot;node.js.selected.package.eslint&quot;: &quot;(autodetect)&quot;,
&quot;nodejs_package_manager_path&quot;: &quot;npm&quot;,
&quot;run.code.analysis.last.selected.profile&quot;: &quot;pProject Default&quot;,
&quot;settings.editor.selected.configurable&quot;: &quot;preferences.pluginManager&quot;,
&quot;vue.rearranger.settings.migration&quot;: &quot;true&quot;
<component name="PropertiesComponent"><![CDATA[{
"keyToString": {
"DefaultGoTemplateProperty": "Go File",
"Go Build.Backend.executor": "Debug",
"Go 构建.Backend.executor": "Run",
"RunOnceActivity.ShowReadmeOnStart": "true",
"RunOnceActivity.go.formatter.settings.were.checked": "true",
"RunOnceActivity.go.migrated.go.modules.settings": "true",
"RunOnceActivity.go.modules.automatic.dependencies.download": "true",
"RunOnceActivity.go.modules.go.list.on.any.changes.was.set": "true",
"git-widget-placeholder": "master",
"go.import.settings.migrated": "true",
"go.sdk.automatically.set": "true",
"last_opened_file_path": "/Users/littlesheep",
"node.js.detected.package.eslint": "true",
"node.js.selected.package.eslint": "(autodetect)",
"nodejs_package_manager_path": "npm",
"run.code.analysis.last.selected.profile": "pProject Default",
"settings.editor.selected.configurable": "preferences.pluginManager",
"vue.rearranger.settings.migration": "true"
},
&quot;keyToStringList&quot;: {
&quot;DatabaseDriversLRU&quot;: [
&quot;postgresql&quot;
"keyToStringList": {
"DatabaseDriversLRU": [
"postgresql"
]
}
}</component>
}]]></component>
<component name="RecentsManager">
<key name="CopyFile.RECENT_KEYS">
<recent name="$PROJECT_DIR$/pkg/services" />
@ -81,11 +108,11 @@
<recent name="$PROJECT_DIR$/pkg" />
</key>
<key name="MoveFile.RECENT_KEYS">
<recent name="$PROJECT_DIR$/pkg/internal/server/exts" />
<recent name="$PROJECT_DIR$/pkg/internal/server/api" />
<recent name="$PROJECT_DIR$/pkg/internal" />
<recent name="$PROJECT_DIR$/pkg" />
<recent name="$PROJECT_DIR$/pkg/views/users/directory" />
<recent name="$PROJECT_DIR$/pkg/views/users" />
<recent name="$PROJECT_DIR$/pkg/utils" />
</key>
</component>
<component name="RunAnythingCache">
@ -147,7 +174,6 @@
</option>
</component>
<component name="VcsManagerConfiguration">
<MESSAGE value=":recycle: Improved the notification subscriber API" />
<MESSAGE value=":sparkles: E2EE Key Exchange" />
<MESSAGE value=":bug: Bug fixes on E2EE" />
<MESSAGE value=":bug: Fix key exchange cause echo" />
@ -172,7 +198,8 @@
<MESSAGE value=":sparkles: Consul registration" />
<MESSAGE value=":wastebasket: Remove HTTP provision to consul" />
<MESSAGE value=":sparkles: Drop direct connection and uses consul" />
<option name="LAST_COMMIT_MESSAGE" value=":sparkles: Drop direct connection and uses consul" />
<MESSAGE value=":technologist: Add the server side Hyper SDK" />
<option name="LAST_COMMIT_MESSAGE" value=":technologist: Add the server side Hyper SDK" />
</component>
<component name="VgoProject">
<settings-migrated>true</settings-migrated>

View File

@ -12,7 +12,6 @@ const CookieAtk = "__hydrogen_atk"
const CookieRtk = "__hydrogen_rtk"
func (v *HyperConn) AuthMiddleware(c *fiber.Ctx) error {
// Detect token
var atk string
if cookie := c.Cookies(CookieAtk); len(cookie) > 0 {
atk = cookie
@ -42,7 +41,6 @@ func (v *HyperConn) AuthMiddleware(c *fiber.Ctx) error {
})
}
c.Locals("p_user", user)
return nil
}
return c.Next()

View File

@ -2,5 +2,5 @@ package pkg
import "embed"
//go:embed views/*
//go:embed all:views/*
var FS embed.FS

View File

@ -2,16 +2,16 @@ package admin
import (
"fmt"
"git.solsynth.dev/hydrogen/passport/pkg/internal/server/exts"
"git.solsynth.dev/hydrogen/passport/pkg/internal/database"
"git.solsynth.dev/hydrogen/passport/pkg/internal/models"
"git.solsynth.dev/hydrogen/passport/pkg/internal/services"
"git.solsynth.dev/hydrogen/passport/pkg/internal/utils"
"github.com/gofiber/fiber/v2"
)
func grantBadge(c *fiber.Ctx) error {
if err := utils.CheckPermissions(c, "AdminGrantBadges", true); err != nil {
if err := exts.EnsureGrantedPerm(c, "AdminGrantBadges", true); err != nil {
return err
}
@ -21,7 +21,7 @@ func grantBadge(c *fiber.Ctx) error {
AccountID uint `json:"account_id"`
}
if err := utils.BindAndValidate(c, &data); err != nil {
if err := exts.BindAndValidate(c, &data); err != nil {
return err
}
@ -44,7 +44,7 @@ func grantBadge(c *fiber.Ctx) error {
}
func revokeBadge(c *fiber.Ctx) error {
if err := utils.CheckPermissions(c, "AdminRevokeBadges", true); err != nil {
if err := exts.EnsureGrantedPerm(c, "AdminRevokeBadges", true); err != nil {
return err
}

View File

@ -4,8 +4,8 @@ import (
"github.com/gofiber/fiber/v2"
)
func MapAdminEndpoints(A *fiber.App, authMiddleware fiber.Handler) {
admin := A.Group("/api/admin").Use(authMiddleware)
func MapAdminEndpoints(A *fiber.App) {
admin := A.Group("/api/admin")
{
admin.Post("/badges", grantBadge)
admin.Delete("/badges/:badgeId", revokeBadge)

View File

@ -1,12 +1,11 @@
package server
package api
import (
"fmt"
"git.solsynth.dev/hydrogen/passport/pkg/internal/server/exts"
"strconv"
"time"
"git.solsynth.dev/hydrogen/passport/pkg/internal/utils"
"git.solsynth.dev/hydrogen/passport/pkg/internal/database"
"git.solsynth.dev/hydrogen/passport/pkg/internal/models"
"git.solsynth.dev/hydrogen/passport/pkg/internal/services"
@ -16,7 +15,10 @@ import (
)
func getUserinfo(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
user := c.Locals("user").(models.Account)
var data models.Account
if err := database.C.
@ -47,7 +49,10 @@ func getUserinfo(c *fiber.Ctx) error {
}
func getEvents(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
user := c.Locals("user").(models.Account)
take := c.QueryInt("take", 0)
offset := c.QueryInt("offset", 0)
@ -76,7 +81,10 @@ func getEvents(c *fiber.Ctx) error {
}
func editUserinfo(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
user := c.Locals("user").(models.Account)
var data struct {
Nick string `json:"nick" validate:"required,min=4,max=24"`
@ -86,7 +94,7 @@ func editUserinfo(c *fiber.Ctx) error {
Birthday time.Time `json:"birthday"`
}
if err := utils.BindAndValidate(c, &data); err != nil {
if err := exts.BindAndValidate(c, &data); err != nil {
return err
}
@ -116,7 +124,10 @@ func editUserinfo(c *fiber.Ctx) error {
}
func killSession(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
user := c.Locals("user").(models.Account)
id, _ := c.ParamsInt("ticketId", 0)
if err := database.C.Delete(&models.AuthTicket{}, &models.AuthTicket{
@ -138,7 +149,7 @@ func doRegister(c *fiber.Ctx) error {
MagicToken string `json:"magic_token"`
}
if err := utils.BindAndValidate(c, &data); err != nil {
if err := exts.BindAndValidate(c, &data); err != nil {
return err
} else if viper.GetBool("use_registration_magic_token") && len(data.MagicToken) <= 0 {
return fmt.Errorf("missing magic token in request")
@ -167,7 +178,7 @@ func doRegisterConfirm(c *fiber.Ctx) error {
Code string `json:"code" validate:"required"`
}
if err := utils.BindAndValidate(c, &data); err != nil {
if err := exts.BindAndValidate(c, &data); err != nil {
return err
}

View File

@ -1,8 +1,8 @@
package server
package api
import (
"fmt"
"git.solsynth.dev/hydrogen/passport/pkg/internal/utils"
"git.solsynth.dev/hydrogen/passport/pkg/internal/server/exts"
"time"
"github.com/gofiber/fiber/v2"
@ -16,7 +16,7 @@ func doAuthenticate(c *fiber.Ctx) error {
Password string `json:"password" validate:"required"`
}
if err := utils.BindAndValidate(c, &data); err != nil {
if err := exts.BindAndValidate(c, &data); err != nil {
return err
}
@ -48,7 +48,7 @@ func doMultiFactorAuthenticate(c *fiber.Ctx) error {
Code string `json:"code" validate:"required"`
}
if err := utils.BindAndValidate(c, &data); err != nil {
if err := exts.BindAndValidate(c, &data); err != nil {
return err
}
@ -85,7 +85,7 @@ func getToken(c *fiber.Ctx) error {
GrantType string `json:"grant_type" form:"grant_type"`
}
if err := utils.BindAndValidate(c, &data); err != nil {
if err := exts.BindAndValidate(c, &data); err != nil {
return err
}
@ -134,7 +134,7 @@ func getToken(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusBadRequest, "unsupported exchange token type")
}
services.SetJwtCookieSet(c, access, refresh)
exts.SetAuthCookies(c, access, refresh)
return c.JSON(fiber.Map{
"id_token": access,

View File

@ -1,4 +1,4 @@
package server
package api
import (
"context"
@ -7,20 +7,27 @@ import (
"git.solsynth.dev/hydrogen/passport/pkg/internal/database"
"git.solsynth.dev/hydrogen/passport/pkg/internal/gap"
"git.solsynth.dev/hydrogen/passport/pkg/internal/models"
"git.solsynth.dev/hydrogen/passport/pkg/internal/server/exts"
"git.solsynth.dev/hydrogen/passport/pkg/internal/services"
"git.solsynth.dev/hydrogen/passport/pkg/internal/utils"
"github.com/gofiber/fiber/v2"
"github.com/samber/lo"
)
func setAvatar(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
user := c.Locals("user").(models.Account)
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
var data struct {
AttachmentID uint `json:"attachment" validate:"required"`
}
if err := utils.BindAndValidate(c, &data); err != nil {
if err := exts.BindAndValidate(c, &data); err != nil {
return err
}
@ -47,13 +54,16 @@ func setAvatar(c *fiber.Ctx) error {
}
func setBanner(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
user := c.Locals("user").(models.Account)
var data struct {
AttachmentID uint `json:"attachment" validate:"required"`
}
if err := utils.BindAndValidate(c, &data); err != nil {
if err := exts.BindAndValidate(c, &data); err != nil {
return err
}

View File

@ -1,4 +1,4 @@
package server
package api
import (
"git.solsynth.dev/hydrogen/passport/pkg/internal/services"

View File

@ -1,14 +1,17 @@
package server
package api
import (
"git.solsynth.dev/hydrogen/passport/pkg/internal/models"
"git.solsynth.dev/hydrogen/passport/pkg/internal/server/exts"
"git.solsynth.dev/hydrogen/passport/pkg/internal/services"
"git.solsynth.dev/hydrogen/passport/pkg/internal/utils"
"github.com/gofiber/fiber/v2"
)
func listFriendship(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
user := c.Locals("user").(models.Account)
status := c.QueryInt("status", -1)
var err error
@ -27,7 +30,10 @@ func listFriendship(c *fiber.Ctx) error {
}
func getFriendship(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
user := c.Locals("user").(models.Account)
relatedId, _ := c.ParamsInt("relatedId", 0)
related, err := services.GetAccount(uint(relatedId))
@ -43,7 +49,10 @@ func getFriendship(c *fiber.Ctx) error {
}
func makeFriendship(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
user := c.Locals("user").(models.Account)
relatedName := c.Query("related")
relatedId, _ := c.ParamsInt("relatedId", 0)
@ -72,14 +81,17 @@ func makeFriendship(c *fiber.Ctx) error {
}
func editFriendship(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
user := c.Locals("user").(models.Account)
relatedId, _ := c.ParamsInt("relatedId", 0)
var data struct {
Status uint8 `json:"status"`
}
if err := utils.BindAndValidate(c, &data); err != nil {
if err := exts.BindAndValidate(c, &data); err != nil {
return fiber.NewError(fiber.StatusBadRequest, err.Error())
}
@ -103,7 +115,10 @@ func editFriendship(c *fiber.Ctx) error {
}
func deleteFriendship(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
user := c.Locals("user").(models.Account)
relatedId, _ := c.ParamsInt("relatedId", 0)
related, err := services.GetAccount(uint(relatedId))

View File

@ -0,0 +1,90 @@
package api
import (
"git.solsynth.dev/hydrogen/passport/pkg/internal/server/exts"
"github.com/gofiber/contrib/websocket"
"github.com/gofiber/fiber/v2"
)
func MapAPIs(app *fiber.App) {
app.Get("/.well-known", getMetadata)
app.Get("/.well-known/openid-configuration", getOidcConfiguration)
api := app.Group("/api").Name("API")
{
notify := api.Group("/notifications").Name("Notifications API")
{
notify.Get("/", getNotifications)
notify.Post("/subscribe", addNotifySubscriber)
notify.Put("/batch/read", markNotificationReadBatch)
notify.Put("/:notificationId/read", markNotificationRead)
}
me := api.Group("/users/me").Name("Myself Operations")
{
me.Put("/avatar", setAvatar)
me.Put("/banner", setBanner)
me.Get("/", getUserinfo)
me.Get("/page", getOwnPersonalPage)
me.Put("/", editUserinfo)
me.Put("/page", editPersonalPage)
me.Get("/events", getEvents)
me.Get("/tickets", getTickets)
me.Delete("/tickets/:ticketId", killSession)
me.Post("/confirm", doRegisterConfirm)
friends := me.Group("/friends").Name("Friends")
{
friends.Get("/", listFriendship)
friends.Get("/:relatedId", getFriendship)
friends.Post("/", makeFriendship)
friends.Post("/:relatedId", makeFriendship)
friends.Put("/:relatedId", editFriendship)
friends.Delete("/:relatedId", deleteFriendship)
}
}
directory := api.Group("/users/:alias").Name("User Directory")
{
directory.Get("/", getOtherUserinfo)
directory.Get("/page", getPersonalPage)
}
api.Post("/users", doRegister)
api.Post("/auth", doAuthenticate)
api.Post("/auth/token", getToken)
api.Post("/auth/factors/:factorId", requestFactorToken)
realms := api.Group("/realms").Name("Realms API")
{
realms.Get("/", listCommunityRealm)
realms.Get("/me", listOwnedRealm)
realms.Get("/me/available", listAvailableRealm)
realms.Get("/:realm", getRealm)
realms.Get("/:realm/members", listRealmMembers)
realms.Get("/:realm/members/me", getMyRealmMember)
realms.Post("/", createRealm)
realms.Put("/:realmId", editRealm)
realms.Delete("/:realmId", deleteRealm)
realms.Post("/:realm/members", addRealmMember)
realms.Delete("/:realm/members", removeRealmMember)
realms.Delete("/:realm/members/me", leaveRealm)
}
developers := api.Group("/dev").Name("Developers API")
{
developers.Post("/notify", notifyUser)
}
api.Use(func(c *fiber.Ctx) error {
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
return c.Next()
}).Get("/ws", websocket.New(listenWebsocket))
}
}

View File

@ -1,18 +1,22 @@
package server
package api
import (
"git.solsynth.dev/hydrogen/passport/pkg/internal/database"
"git.solsynth.dev/hydrogen/passport/pkg/internal/models"
"git.solsynth.dev/hydrogen/passport/pkg/internal/server/exts"
"git.solsynth.dev/hydrogen/passport/pkg/internal/services"
"git.solsynth.dev/hydrogen/passport/pkg/internal/utils"
"github.com/gofiber/fiber/v2"
)
func getNotifications(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
take := c.QueryInt("take", 0)
offset := c.QueryInt("offset", 0)
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
user := c.Locals("user").(models.Account)
tx := database.C.Where(&models.Notification{RecipientID: user.ID}).Model(&models.Notification{})
var count int64
@ -36,9 +40,16 @@ func getNotifications(c *fiber.Ctx) error {
}
func markNotificationRead(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
user := c.Locals("user").(models.Account)
id, _ := c.ParamsInt("notificationId", 0)
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
var notify models.Notification
if err := database.C.Where(&models.Notification{
BaseModel: models.BaseModel{ID: uint(id)},
@ -55,13 +66,16 @@ func markNotificationRead(c *fiber.Ctx) error {
}
func markNotificationReadBatch(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
user := c.Locals("user").(models.Account)
var data struct {
MessageIDs []uint `json:"messages"`
}
if err := utils.BindAndValidate(c, &data); err != nil {
if err := exts.BindAndValidate(c, &data); err != nil {
return fiber.NewError(fiber.StatusBadRequest, err.Error())
}
@ -75,7 +89,10 @@ func markNotificationReadBatch(c *fiber.Ctx) error {
}
func addNotifySubscriber(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
user := c.Locals("user").(models.Account)
var data struct {
Provider string `json:"provider" validate:"required"`
@ -83,7 +100,7 @@ func addNotifySubscriber(c *fiber.Ctx) error {
DeviceID string `json:"device_id" validate:"required"`
}
if err := utils.BindAndValidate(c, &data); err != nil {
if err := exts.BindAndValidate(c, &data); err != nil {
return err
}

View File

@ -1,9 +1,9 @@
package server
package api
import (
"git.solsynth.dev/hydrogen/passport/pkg/internal/models"
"git.solsynth.dev/hydrogen/passport/pkg/internal/server/exts"
"git.solsynth.dev/hydrogen/passport/pkg/internal/services"
"git.solsynth.dev/hydrogen/passport/pkg/internal/utils"
"github.com/gofiber/fiber/v2"
)
@ -21,7 +21,7 @@ func notifyUser(c *fiber.Ctx) error {
UserID uint `json:"user_id" validate:"required"`
}
if err := utils.BindAndValidate(c, &data); err != nil {
if err := exts.BindAndValidate(c, &data); err != nil {
return err
}

View File

@ -1,9 +1,9 @@
package server
package api
import (
"git.solsynth.dev/hydrogen/passport/pkg/internal/database"
"git.solsynth.dev/hydrogen/passport/pkg/internal/models"
"git.solsynth.dev/hydrogen/passport/pkg/internal/utils"
"git.solsynth.dev/hydrogen/passport/pkg/internal/server/exts"
"github.com/gofiber/fiber/v2"
)
@ -28,7 +28,10 @@ func getPersonalPage(c *fiber.Ctx) error {
}
func getOwnPersonalPage(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
user := c.Locals("user").(models.Account)
var page models.AccountPage
if err := database.C.
@ -41,14 +44,17 @@ func getOwnPersonalPage(c *fiber.Ctx) error {
}
func editPersonalPage(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
user := c.Locals("user").(models.Account)
var data struct {
Content string `json:"content"`
Links []models.AccountPageLinks `json:"links"`
}
if err := utils.BindAndValidate(c, &data); err != nil {
if err := exts.BindAndValidate(c, &data); err != nil {
return err
}

View File

@ -1,10 +1,10 @@
package server
package api
import (
"git.solsynth.dev/hydrogen/passport/pkg/internal/database"
"git.solsynth.dev/hydrogen/passport/pkg/internal/models"
"git.solsynth.dev/hydrogen/passport/pkg/internal/server/exts"
"git.solsynth.dev/hydrogen/passport/pkg/internal/services"
"git.solsynth.dev/hydrogen/passport/pkg/internal/utils"
"github.com/gofiber/fiber/v2"
)
@ -22,7 +22,10 @@ func listRealmMembers(c *fiber.Ctx) error {
func getMyRealmMember(c *fiber.Ctx) error {
alias := c.Params("realm")
user := c.Locals("principal").(models.Account)
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
user := c.Locals("user").(models.Account)
if realm, err := services.GetRealmWithAlias(alias); err != nil {
return fiber.NewError(fiber.StatusNotFound, err.Error())
@ -34,14 +37,17 @@ func getMyRealmMember(c *fiber.Ctx) error {
}
func addRealmMember(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
user := c.Locals("user").(models.Account)
alias := c.Params("realm")
var data struct {
Target string `json:"target" validate:"required"`
}
if err := utils.BindAndValidate(c, &data); err != nil {
if err := exts.BindAndValidate(c, &data); err != nil {
return err
}
@ -65,14 +71,17 @@ func addRealmMember(c *fiber.Ctx) error {
}
func removeRealmMember(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
user := c.Locals("user").(models.Account)
alias := c.Params("realm")
var data struct {
Target string `json:"target" validate:"required"`
}
if err := utils.BindAndValidate(c, &data); err != nil {
if err := exts.BindAndValidate(c, &data); err != nil {
return err
}
@ -96,7 +105,10 @@ func removeRealmMember(c *fiber.Ctx) error {
}
func leaveRealm(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
user := c.Locals("user").(models.Account)
alias := c.Params("realm")
realm, err := services.GetRealmWithAlias(alias)

View File

@ -1,10 +1,10 @@
package server
package api
import (
"git.solsynth.dev/hydrogen/passport/pkg/internal/database"
"git.solsynth.dev/hydrogen/passport/pkg/internal/models"
"git.solsynth.dev/hydrogen/passport/pkg/internal/server/exts"
"git.solsynth.dev/hydrogen/passport/pkg/internal/services"
"git.solsynth.dev/hydrogen/passport/pkg/internal/utils"
"github.com/gofiber/fiber/v2"
)
@ -27,7 +27,10 @@ func listCommunityRealm(c *fiber.Ctx) error {
}
func listOwnedRealm(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
user := c.Locals("user").(models.Account)
if realms, err := services.ListOwnedRealm(user); err != nil {
return fiber.NewError(fiber.StatusBadRequest, err.Error())
} else {
@ -36,7 +39,10 @@ func listOwnedRealm(c *fiber.Ctx) error {
}
func listAvailableRealm(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
user := c.Locals("user").(models.Account)
if realms, err := services.ListAvailableRealm(user); err != nil {
return fiber.NewError(fiber.StatusBadRequest, err.Error())
} else {
@ -45,10 +51,10 @@ func listAvailableRealm(c *fiber.Ctx) error {
}
func createRealm(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
if err := utils.CheckPermissions(c, "CreateRealms", true); err != nil {
if err := exts.EnsureGrantedPerm(c, "CreateRealms", true); err != nil {
return err
}
user := c.Locals("user").(models.Account)
var data struct {
Alias string `json:"alias" validate:"required,lowercase,min=4,max=32"`
@ -58,7 +64,7 @@ func createRealm(c *fiber.Ctx) error {
IsCommunity bool `json:"is_community"`
}
if err := utils.BindAndValidate(c, &data); err != nil {
if err := exts.BindAndValidate(c, &data); err != nil {
return err
}
@ -78,7 +84,10 @@ func createRealm(c *fiber.Ctx) error {
}
func editRealm(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
user := c.Locals("user").(models.Account)
id, _ := c.ParamsInt("realmId", 0)
var data struct {
@ -89,7 +98,7 @@ func editRealm(c *fiber.Ctx) error {
IsCommunity bool `json:"is_community"`
}
if err := utils.BindAndValidate(c, &data); err != nil {
if err := exts.BindAndValidate(c, &data); err != nil {
return err
}
@ -116,7 +125,10 @@ func editRealm(c *fiber.Ctx) error {
}
func deleteRealm(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
user := c.Locals("user").(models.Account)
id, _ := c.ParamsInt("realmId", 0)
var realm models.Realm

View File

@ -1,13 +1,17 @@
package server
package api
import (
"git.solsynth.dev/hydrogen/passport/pkg/internal/database"
"git.solsynth.dev/hydrogen/passport/pkg/internal/models"
"git.solsynth.dev/hydrogen/passport/pkg/internal/server/exts"
"github.com/gofiber/fiber/v2"
)
func getTickets(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
user := c.Locals("user").(models.Account)
take := c.QueryInt("take", 0)
offset := c.QueryInt("offset", 0)

View File

@ -1,4 +1,4 @@
package server
package api
import (
"git.solsynth.dev/hydrogen/passport/pkg/internal/database"

View File

@ -1,4 +1,4 @@
package server
package api
import (
"fmt"

View File

@ -1,8 +1,7 @@
package server
package api
import (
"fmt"
"git.solsynth.dev/hydrogen/passport/pkg/internal/models"
"git.solsynth.dev/hydrogen/passport/pkg/internal/services"
"github.com/gofiber/contrib/websocket"
@ -12,7 +11,7 @@ import (
)
func listenWebsocket(c *websocket.Conn) {
user := c.Locals("principal").(models.Account)
user := c.Locals("user").(models.Account)
// Push connection
services.ClientRegister(user, c)

View File

@ -1,55 +0,0 @@
package server
import (
"strings"
"git.solsynth.dev/hydrogen/passport/pkg/internal/services"
"github.com/gofiber/fiber/v2"
)
func authMiddleware(c *fiber.Ctx) error {
var token string
if cookie := c.Cookies(services.CookieAccessKey); len(cookie) > 0 {
token = cookie
}
if header := c.Get(fiber.HeaderAuthorization); len(header) > 0 {
tk := strings.Replace(header, "Bearer", "", 1)
token = strings.TrimSpace(tk)
}
if query := c.Query("tk"); len(query) > 0 {
token = strings.TrimSpace(query)
}
c.Locals("token", token)
if err := authFunc(c); err != nil {
return err
}
return c.Next()
}
func authFunc(c *fiber.Ctx, overrides ...string) error {
var token string
if len(overrides) > 0 {
token = overrides[0]
} else {
if tk, ok := c.Locals("token").(string); !ok {
return fiber.NewError(fiber.StatusUnauthorized)
} else {
token = tk
}
}
rtk := c.Cookies(services.CookieRefreshKey)
if ctx, perms, atk, rtk, err := services.Authenticate(token, rtk, 0); err == nil {
if atk != token {
services.SetJwtCookieSet(c, atk, rtk)
}
c.Locals("permissions", perms)
c.Locals("principal", ctx.Account)
return nil
} else {
return err
}
}

View File

@ -0,0 +1,53 @@
package exts
import (
"fmt"
"git.solsynth.dev/hydrogen/passport/pkg/hyper"
"git.solsynth.dev/hydrogen/passport/pkg/internal/models"
"git.solsynth.dev/hydrogen/passport/pkg/internal/services"
"github.com/gofiber/fiber/v2"
"strings"
)
func AuthMiddleware(c *fiber.Ctx) error {
var atk string
if cookie := c.Cookies(hyper.CookieAtk); len(cookie) > 0 {
atk = cookie
}
if header := c.Get(fiber.HeaderAuthorization); len(header) > 0 {
tk := strings.Replace(header, "Bearer", "", 1)
atk = strings.TrimSpace(tk)
}
c.Locals("p_token", atk)
rtk := c.Cookies(hyper.CookieRtk)
if ctx, perms, newAtk, newRtk, err := services.Authenticate(atk, rtk, 0); err == nil {
if newAtk != atk {
SetAuthCookies(c, newAtk, newRtk)
}
c.Locals("permissions", perms)
c.Locals("user", ctx.Account)
}
return c.Next()
}
func EnsureAuthenticated(c *fiber.Ctx) error {
if _, ok := c.Locals("user").(models.Account); !ok {
return fiber.NewError(fiber.StatusUnauthorized)
}
return nil
}
func EnsureGrantedPerm(c *fiber.Ctx, key string, val any) error {
if err := EnsureAuthenticated(c); err != nil {
return err
}
perms := c.Locals("permissions").(map[string]any)
if !services.HasPermNode(perms, key, val) {
return fiber.NewError(fiber.StatusForbidden, fmt.Sprintf("missing permission: %s", key))
}
return nil
}

View File

@ -0,0 +1,27 @@
package exts
import (
"git.solsynth.dev/hydrogen/passport/pkg/hyper"
"github.com/gofiber/fiber/v2"
"github.com/spf13/viper"
"time"
)
func SetAuthCookies(c *fiber.Ctx, atk, rtk string) {
c.Cookie(&fiber.Cookie{
Name: hyper.CookieAtk,
Value: atk,
Domain: viper.GetString("security.cookie_domain"),
SameSite: viper.GetString("security.cookie_samesite"),
Expires: time.Now().Add(60 * time.Minute),
Path: "/",
})
c.Cookie(&fiber.Cookie{
Name: hyper.CookieRtk,
Value: rtk,
Domain: viper.GetString("security.cookie_domain"),
SameSite: viper.GetString("security.cookie_samesite"),
Expires: time.Now().Add(24 * 30 * time.Hour),
Path: "/",
})
}

View File

@ -1,8 +1,6 @@
package utils
package exts
import (
"fmt"
"git.solsynth.dev/hydrogen/passport/pkg/internal/services"
"github.com/go-playground/validator/v10"
"github.com/gofiber/fiber/v2"
"github.com/samber/lo"
@ -21,17 +19,6 @@ func BindAndValidate(c *fiber.Ctx, out any) error {
return nil
}
func GetPermissions(c *fiber.Ctx) map[string]any {
return c.Locals("permissions").(map[string]any)
}
func CheckPermissions(c *fiber.Ctx, key string, val any) error {
if !services.HasPermNode(GetPermissions(c), key, val) {
return fiber.NewError(fiber.StatusForbidden, fmt.Sprintf("requires permission: %s = %v", key, val))
}
return nil
}
func GetRedirectUri(c *fiber.Ctx, fallback ...string) *string {
if len(c.Query("redirect_uri")) > 0 {
return lo.ToPtr(c.Query("redirect_uri"))

View File

@ -1,11 +1,11 @@
package server
import (
"git.solsynth.dev/hydrogen/passport/pkg/internal/server/api"
"git.solsynth.dev/hydrogen/passport/pkg/internal/server/exts"
"net/http"
"strings"
"github.com/gofiber/contrib/websocket"
"git.solsynth.dev/hydrogen/passport/pkg/internal"
"git.solsynth.dev/hydrogen/passport/pkg/internal/i18n"
"git.solsynth.dev/hydrogen/passport/pkg/internal/server/admin"
@ -61,92 +61,18 @@ func NewServer() {
Output: log.Logger,
}))
A.Use(exts.AuthMiddleware)
A.Use(i18n.I18nMiddleware)
A.Get("/.well-known", getMetadata)
A.Get("/.well-known/openid-configuration", getOidcConfiguration)
api := A.Group("/api").Name("API")
{
notify := api.Group("/notifications").Name("Notifications API")
{
notify.Get("/", authMiddleware, getNotifications)
notify.Post("/subscribe", authMiddleware, addNotifySubscriber)
notify.Put("/batch/read", authMiddleware, markNotificationReadBatch)
notify.Put("/:notificationId/read", authMiddleware, markNotificationRead)
}
me := api.Group("/users/me").Name("Myself Operations")
{
me.Put("/avatar", authMiddleware, setAvatar)
me.Put("/banner", authMiddleware, setBanner)
me.Get("/", authMiddleware, getUserinfo)
me.Get("/page", authMiddleware, getOwnPersonalPage)
me.Put("/", authMiddleware, editUserinfo)
me.Put("/page", authMiddleware, editPersonalPage)
me.Get("/events", authMiddleware, getEvents)
me.Get("/tickets", authMiddleware, getTickets)
me.Delete("/tickets/:ticketId", authMiddleware, killSession)
me.Post("/confirm", doRegisterConfirm)
friends := me.Group("/friends").Name("Friends")
{
friends.Get("/", authMiddleware, listFriendship)
friends.Get("/:relatedId", authMiddleware, getFriendship)
friends.Post("/", authMiddleware, makeFriendship)
friends.Post("/:relatedId", authMiddleware, makeFriendship)
friends.Put("/:relatedId", authMiddleware, editFriendship)
friends.Delete("/:relatedId", authMiddleware, deleteFriendship)
}
}
directory := api.Group("/users/:alias").Name("User Directory")
{
directory.Get("/", getOtherUserinfo)
directory.Get("/page", getPersonalPage)
}
api.Post("/users", doRegister)
api.Post("/auth", doAuthenticate)
api.Post("/auth/token", getToken)
api.Post("/auth/factors/:factorId", requestFactorToken)
realms := api.Group("/realms").Name("Realms API")
{
realms.Get("/", listCommunityRealm)
realms.Get("/me", authMiddleware, listOwnedRealm)
realms.Get("/me/available", authMiddleware, listAvailableRealm)
realms.Get("/:realm", getRealm)
realms.Get("/:realm/members", listRealmMembers)
realms.Get("/:realm/members/me", authMiddleware, getMyRealmMember)
realms.Post("/", authMiddleware, createRealm)
realms.Put("/:realmId", authMiddleware, editRealm)
realms.Delete("/:realmId", authMiddleware, deleteRealm)
realms.Post("/:realm/members", authMiddleware, addRealmMember)
realms.Delete("/:realm/members", authMiddleware, removeRealmMember)
realms.Delete("/:realm/members/me", authMiddleware, leaveRealm)
}
developers := api.Group("/dev").Name("Developers API")
{
developers.Post("/notify", notifyUser)
}
api.Get("/ws", authMiddleware, websocket.New(listenWebsocket))
}
A.Use(favicon.New(favicon.Config{
FileSystem: http.FS(pkg.FS),
File: "views/favicon.png",
URL: "/favicon.png",
}))
admin.MapAdminEndpoints(A, authMiddleware)
ui.MapUserInterface(A, authFunc)
api.MapAPIs(A)
admin.MapAdminEndpoints(A)
ui.MapUserInterface(A)
}
func Listen() {

View File

@ -2,6 +2,7 @@ package ui
import (
"fmt"
"git.solsynth.dev/hydrogen/passport/pkg/internal/server/exts"
"html/template"
"time"
@ -15,7 +16,10 @@ import (
)
func selfUserinfoPage(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
if err := exts.EnsureAuthenticated(c); err != nil {
return DoAuthRedirect(c)
}
user := c.Locals("user").(models.Account)
var data models.Account
if err := database.C.

View File

@ -3,28 +3,15 @@ package ui
import (
"fmt"
"git.solsynth.dev/hydrogen/passport/pkg/internal/services"
"git.solsynth.dev/hydrogen/passport/pkg/internal/utils"
"github.com/gofiber/fiber/v2"
)
func MapUserInterface(A *fiber.App, authFunc utils.AuthFunc) {
authCheckWare := func(c *fiber.Ctx) error {
var token string
if cookie := c.Cookies(services.CookieAccessKey); len(cookie) > 0 {
token = cookie
}
c.Locals("token", token)
if err := authFunc(c); err != nil {
uri := c.Request().URI().FullURI()
return c.Redirect(fmt.Sprintf("/sign-in?redirect_uri=%s", string(uri)))
} else {
return c.Next()
}
}
func DoAuthRedirect(c *fiber.Ctx) error {
uri := c.Request().URI().FullURI()
return c.Redirect(fmt.Sprintf("/sign-in?redirect_uri=%s", string(uri)))
}
func MapUserInterface(A *fiber.App) {
pages := A.Group("/").Name("Pages")
pages.Get("/", func(c *fiber.Ctx) error {
@ -35,13 +22,13 @@ func MapUserInterface(A *fiber.App, authFunc utils.AuthFunc) {
pages.Get("/sign-in", signinPage)
pages.Get("/mfa", mfaRequestPage)
pages.Get("/mfa/apply", mfaApplyPage)
pages.Get("/authorize", authCheckWare, authorizePage)
pages.Get("/authorize", authorizePage)
pages.Post("/sign-up", signupAction)
pages.Post("/sign-in", signinAction)
pages.Post("/mfa", mfaRequestAction)
pages.Post("/mfa/apply", mfaApplyAction)
pages.Post("/authorize", authCheckWare, authorizeAction)
pages.Post("/authorize", authorizeAction)
pages.Get("/users/me", authCheckWare, selfUserinfoPage)
pages.Get("/users/me", selfUserinfoPage)
}

View File

@ -3,8 +3,8 @@ package ui
import (
"fmt"
"git.solsynth.dev/hydrogen/passport/pkg/internal/models"
"git.solsynth.dev/hydrogen/passport/pkg/internal/server/exts"
"git.solsynth.dev/hydrogen/passport/pkg/internal/services"
"git.solsynth.dev/hydrogen/passport/pkg/internal/utils"
"github.com/gofiber/fiber/v2"
"github.com/nicksnyder/go-i18n/v2/i18n"
"github.com/samber/lo"
@ -68,7 +68,7 @@ func mfaRequestAction(c *fiber.Ctx) error {
}
redirectBackUri := "/sign-in"
err := utils.BindAndValidate(c, &data)
err := exts.BindAndValidate(c, &data)
if data.TicketID > 0 {
redirectBackUri = fmt.Sprintf("/mfa?ticket=%d", data.TicketID)
@ -95,7 +95,7 @@ func mfaRequestAction(c *fiber.Ctx) error {
}
return flash.WithData(c, fiber.Map{
"redirect_uri": utils.GetRedirectUri(c),
"redirect_uri": exts.GetRedirectUri(c),
}).Redirect(fmt.Sprintf("/mfa/apply?ticket=%d&factor=%d", data.TicketID, factor.ID))
}
@ -145,7 +145,7 @@ func mfaApplyAction(c *fiber.Ctx) error {
}
redirectBackUri := "/sign-in"
err := utils.BindAndValidate(c, &data)
err := exts.BindAndValidate(c, &data)
if data.TicketID > 0 {
redirectBackUri = fmt.Sprintf("/mfa/apply?ticket=%d&factor=%d", data.TicketID, data.FactorID)
@ -187,8 +187,8 @@ func mfaApplyAction(c *fiber.Ctx) error {
"message": fmt.Sprintf("failed to exchange token: %v", err.Error()),
}).Redirect("/sign-in")
} else {
services.SetJwtCookieSet(c, access, refresh)
exts.SetAuthCookies(c, access, refresh)
}
return c.Redirect(lo.FromPtr(utils.GetRedirectUri(c, "/users/me")))
return c.Redirect(lo.FromPtr(exts.GetRedirectUri(c, "/users/me")))
}

View File

@ -4,6 +4,7 @@ import (
"fmt"
"git.solsynth.dev/hydrogen/passport/pkg/internal/database"
"git.solsynth.dev/hydrogen/passport/pkg/internal/models"
"git.solsynth.dev/hydrogen/passport/pkg/internal/server/exts"
"git.solsynth.dev/hydrogen/passport/pkg/internal/services"
"github.com/gofiber/fiber/v2"
"github.com/nicksnyder/go-i18n/v2/i18n"
@ -16,7 +17,11 @@ import (
func authorizePage(c *fiber.Ctx) error {
localizer := c.Locals("localizer").(*i18n.Localizer)
user := c.Locals("principal").(models.Account)
if err := exts.EnsureAuthenticated(c); err != nil {
return DoAuthRedirect(c)
}
user := c.Locals("user").(models.Account)
id := c.Query("client_id")
redirect := c.Query("redirect_uri")
@ -81,12 +86,19 @@ func authorizePage(c *fiber.Ctx) error {
}
func authorizeAction(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
if err := exts.EnsureAuthenticated(c); err != nil {
return err
}
user := c.Locals("user").(models.Account)
id := c.Query("client_id")
response := c.Query("response_type")
redirect := c.Query("redirect_uri")
scope := c.Query("scope")
if err := exts.EnsureAuthenticated(c); err != nil {
return DoAuthRedirect(c)
}
redirectBackUri := "/authorize?" + string(c.Request().URI().QueryString())
if len(scope) <= 0 {

View File

@ -2,8 +2,8 @@ package ui
import (
"fmt"
"git.solsynth.dev/hydrogen/passport/pkg/internal/server/exts"
"git.solsynth.dev/hydrogen/passport/pkg/internal/services"
"git.solsynth.dev/hydrogen/passport/pkg/internal/utils"
"github.com/gofiber/fiber/v2"
"github.com/nicksnyder/go-i18n/v2/i18n"
"github.com/samber/lo"
@ -47,7 +47,7 @@ func signinAction(c *fiber.Ctx) error {
Password string `form:"password" validate:"required"`
}
if err := utils.BindAndValidate(c, &data); err != nil {
if err := exts.BindAndValidate(c, &data); err != nil {
return flash.WithInfo(c, fiber.Map{
"message": err.Error(),
}).Redirect("/sign-in")
@ -76,7 +76,7 @@ func signinAction(c *fiber.Ctx) error {
if ticket.IsAvailable() != nil {
return flash.WithData(c, fiber.Map{
"redirect_uri": utils.GetRedirectUri(c),
"redirect_uri": exts.GetRedirectUri(c),
}).Redirect(fmt.Sprintf("/mfa?ticket=%d", ticket.ID))
}
@ -86,8 +86,8 @@ func signinAction(c *fiber.Ctx) error {
"message": fmt.Sprintf("failed to exchange token: %v", err.Error()),
}).Redirect("/sign-in")
} else {
services.SetJwtCookieSet(c, access, refresh)
exts.SetAuthCookies(c, access, refresh)
}
return c.Redirect(lo.FromPtr(utils.GetRedirectUri(c, "/users/me")))
return c.Redirect(lo.FromPtr(exts.GetRedirectUri(c, "/users/me")))
}

View File

@ -4,8 +4,8 @@ import (
"fmt"
"git.solsynth.dev/hydrogen/passport/pkg/internal/database"
"git.solsynth.dev/hydrogen/passport/pkg/internal/models"
"git.solsynth.dev/hydrogen/passport/pkg/internal/server/exts"
"git.solsynth.dev/hydrogen/passport/pkg/internal/services"
"git.solsynth.dev/hydrogen/passport/pkg/internal/utils"
"github.com/gofiber/fiber/v2"
"github.com/nicksnyder/go-i18n/v2/i18n"
"github.com/samber/lo"
@ -52,7 +52,7 @@ func signupAction(c *fiber.Ctx) error {
MagicToken string `form:"magic_token"`
}
if err := utils.BindAndValidate(c, &data); err != nil {
if err := exts.BindAndValidate(c, &data); err != nil {
return flash.WithInfo(c, fiber.Map{
"message": err.Error(),
}).Redirect("/sign-up")
@ -82,6 +82,6 @@ func signupAction(c *fiber.Ctx) error {
} else {
return flash.WithInfo(c, fiber.Map{
"message": "account has been created. now you can sign in!",
}).Redirect(lo.FromPtr(utils.GetRedirectUri(c, "/sign-in")))
}).Redirect(lo.FromPtr(exts.GetRedirectUri(c, "/sign-in")))
}
}

View File

@ -17,23 +17,23 @@ var (
authContextCache = make(map[string]models.AuthContext)
)
func Authenticate(access, refresh string, depth int) (ctx models.AuthContext, perms map[string]any, newAccess, newRefresh string, err error) {
func Authenticate(atk, rtk string, rty int) (ctx models.AuthContext, perms map[string]any, newAtk, newRtk string, err error) {
var claims PayloadClaims
claims, err = DecodeJwt(access)
claims, err = DecodeJwt(atk)
if err != nil {
if len(refresh) > 0 && depth < 1 {
if len(rtk) > 0 && rty < 1 {
// Auto refresh and retry
newAccess, newRefresh, err = RefreshToken(refresh)
newAtk, newRtk, err = RefreshToken(rtk)
if err == nil {
return Authenticate(newAccess, newRefresh, depth+1)
return Authenticate(newAtk, newRtk, rty+1)
}
}
err = fiber.NewError(fiber.StatusUnauthorized, fmt.Sprintf("invalid auth key: %v", err))
return
}
newAccess = access
newRefresh = refresh
newAtk = atk
newRtk = rtk
if ctx, err = GetAuthContext(claims.ID); err == nil {
var heldPerms map[string]any

View File

@ -2,16 +2,12 @@ package services
import (
"fmt"
"github.com/gofiber/fiber/v2"
"time"
"github.com/golang-jwt/jwt/v5"
"github.com/spf13/viper"
)
var CookieAccessKey = "passport_auth_key"
var CookieRefreshKey = "passport_refresh_key"
type PayloadClaims struct {
jwt.RegisteredClaims
@ -60,22 +56,3 @@ func DecodeJwt(str string) (PayloadClaims, error) {
return claims, fmt.Errorf("unexpected token payload: not payload claims type")
}
}
func SetJwtCookieSet(c *fiber.Ctx, access, refresh string) {
c.Cookie(&fiber.Cookie{
Name: CookieAccessKey,
Value: access,
Domain: viper.GetString("security.cookie_domain"),
SameSite: viper.GetString("security.cookie_samesite"),
Expires: time.Now().Add(60 * time.Minute),
Path: "/",
})
c.Cookie(&fiber.Cookie{
Name: CookieRefreshKey,
Value: refresh,
Domain: viper.GetString("security.cookie_domain"),
SameSite: viper.GetString("security.cookie_samesite"),
Expires: time.Now().Add(24 * 30 * time.Hour),
Path: "/",
})
}

View File

@ -1,5 +0,0 @@
package utils
import "github.com/gofiber/fiber/v2"
type AuthFunc func(c *fiber.Ctx, overrides ...string) error