From 21d3d719361ec6d82d49f84289ad2c9721e33325 Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Wed, 26 Jun 2024 14:47:34 +0800 Subject: [PATCH] :recycle: OAuth authenticate --- .../723637bc-6ce3-4bbe-afb3-d88730c75a1b.xml | 1 + .idea/workspace.xml | 69 +++++----- pkg/internal/server/api/accounts_api.go | 2 +- pkg/internal/server/api/index.go | 18 ++- pkg/internal/server/api/oauth_api.go | 128 ++++++++++++++++++ pkg/internal/services/ticket.go | 2 +- web/src/components/Copyright.vue | 2 +- web/src/components/NotificationList.vue | 34 +++-- web/src/components/navigation/AppBar.vue | 49 +++++++ web/src/layouts/master.vue | 28 +--- web/src/layouts/user-center.vue | 43 +++--- web/src/router/index.ts | 43 +++--- web/src/stores/notifications.ts | 2 +- web/src/views/auth/authorize.vue | 26 ++-- web/src/views/security.vue | 121 ++++------------- 15 files changed, 328 insertions(+), 240 deletions(-) create mode 100755 pkg/internal/server/api/oauth_api.go create mode 100644 web/src/components/navigation/AppBar.vue diff --git a/.idea/dataSources/723637bc-6ce3-4bbe-afb3-d88730c75a1b.xml b/.idea/dataSources/723637bc-6ce3-4bbe-afb3-d88730c75a1b.xml index 308becf..79223f4 100644 --- a/.idea/dataSources/723637bc-6ce3-4bbe-afb3-d88730c75a1b.xml +++ b/.idea/dataSources/723637bc-6ce3-4bbe-afb3-d88730c75a1b.xml @@ -5024,6 +5024,7 @@ true posixrules 16445 + 1257512postgres diff --git a/.idea/workspace.xml b/.idea/workspace.xml index db8f3bf..0e0051e 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -4,15 +4,8 @@ - - @@ -175,7 +166,9 @@ - true diff --git a/pkg/internal/server/api/accounts_api.go b/pkg/internal/server/api/accounts_api.go index 8fbcc84..d2531aa 100644 --- a/pkg/internal/server/api/accounts_api.go +++ b/pkg/internal/server/api/accounts_api.go @@ -123,7 +123,7 @@ func editUserinfo(c *fiber.Ctx) error { return c.SendStatus(fiber.StatusOK) } -func killSession(c *fiber.Ctx) error { +func killTicket(c *fiber.Ctx) error { if err := exts.EnsureAuthenticated(c); err != nil { return err } diff --git a/pkg/internal/server/api/index.go b/pkg/internal/server/api/index.go index 71a28f5..cb828b3 100644 --- a/pkg/internal/server/api/index.go +++ b/pkg/internal/server/api/index.go @@ -29,7 +29,7 @@ func MapAPIs(app *fiber.App) { me.Put("/", editUserinfo) me.Get("/events", getEvents) me.Get("/tickets", getTickets) - me.Delete("/tickets/:ticketId", killSession) + me.Delete("/tickets/:ticketId", killTicket) me.Post("/confirm", doRegisterConfirm) @@ -51,12 +51,18 @@ func MapAPIs(app *fiber.App) { api.Post("/users", doRegister) - api.Post("/auth", doAuthenticate) - api.Post("/auth/mfa", doMultiFactorAuthenticate) - api.Post("/auth/token", getToken) + auth := api.Group("/auth").Name("Auth") + { + auth.Post("/", doAuthenticate) + auth.Post("/mfa", doMultiFactorAuthenticate) + auth.Post("/token", getToken) - api.Get("/auth/factors", getAvailableFactors) - api.Post("/auth/factors/:factorId", requestFactorToken) + auth.Get("/factors", getAvailableFactors) + auth.Post("/factors/:factorId", requestFactorToken) + + auth.Get("/o/authorize", tryAuthorizeThirdClient) + auth.Post("/o/authorize", authorizeThirdClient) + } realms := api.Group("/realms").Name("Realms API") { diff --git a/pkg/internal/server/api/oauth_api.go b/pkg/internal/server/api/oauth_api.go new file mode 100755 index 0000000..4872d97 --- /dev/null +++ b/pkg/internal/server/api/oauth_api.go @@ -0,0 +1,128 @@ +package api + +import ( + "git.solsynth.dev/hydrogen/passport/pkg/internal/server/exts" + "strings" + "time" + + "git.solsynth.dev/hydrogen/passport/pkg/internal/database" + "git.solsynth.dev/hydrogen/passport/pkg/internal/models" + "git.solsynth.dev/hydrogen/passport/pkg/internal/services" + "github.com/gofiber/fiber/v2" + "github.com/samber/lo" +) + +func tryAuthorizeThirdClient(c *fiber.Ctx) error { + id := c.Query("client_id") + redirect := c.Query("redirect_uri") + + if len(id) <= 0 || len(redirect) <= 0 { + return fiber.NewError(fiber.StatusBadRequest, "invalid request, missing query parameters") + } + + var client models.ThirdClient + if err := database.C.Where(&models.ThirdClient{Alias: id}).First(&client).Error; err != nil { + return fiber.NewError(fiber.StatusNotFound, err.Error()) + } else if !client.IsDraft && !lo.Contains(client.Callbacks, strings.Split(redirect, "?")[0]) { + return fiber.NewError(fiber.StatusBadRequest, "invalid callback url") + } + + if err := exts.EnsureAuthenticated(c); err != nil { + return err + } + user := c.Locals("user").(models.Account) + + var ticket models.AuthTicket + if err := database.C.Where(&models.AuthTicket{ + AccountID: user.ID, + ClientID: &client.ID, + }).Where("last_grant_at IS NULL").First(&ticket).Error; err == nil { + if ticket.ExpiredAt != nil && ticket.ExpiredAt.Unix() < time.Now().Unix() { + return c.JSON(fiber.Map{ + "client": client, + "ticket": nil, + }) + } else { + ticket, err = services.RegenSession(ticket) + } + + return c.JSON(fiber.Map{ + "client": client, + "ticket": ticket, + }) + } + + return c.JSON(fiber.Map{ + "client": client, + "ticket": nil, + }) +} + +func authorizeThirdClient(c *fiber.Ctx) error { + id := c.Query("client_id") + response := c.Query("response_type") + redirect := c.Query("redirect_uri") + scope := c.Query("scope") + if len(scope) <= 0 { + return fiber.NewError(fiber.StatusBadRequest, "invalid request params") + } + + if err := exts.EnsureAuthenticated(c); err != nil { + return err + } + user := c.Locals("user").(models.Account) + + var client models.ThirdClient + if err := database.C.Where(&models.ThirdClient{Alias: id}).First(&client).Error; err != nil { + return fiber.NewError(fiber.StatusNotFound, err.Error()) + } + + switch response { + case "code": + // OAuth Authorization Mode + ticket, err := services.NewOauthTicket( + user, + client, + strings.Split(scope, " "), + []string{"passport", client.Alias}, + c.IP(), + c.Get(fiber.HeaderUserAgent), + ) + + if err != nil { + return fiber.NewError(fiber.StatusInternalServerError, err.Error()) + } else { + services.AddEvent(user, "oauth.connect", client.Alias, c.IP(), c.Get(fiber.HeaderUserAgent)) + return c.JSON(fiber.Map{ + "ticket": ticket, + "redirect_uri": redirect, + }) + } + case "token": + // OAuth Implicit Mode + ticket, err := services.NewOauthTicket( + user, + client, + strings.Split(scope, " "), + []string{"passport", client.Alias}, + c.IP(), + c.Get(fiber.HeaderUserAgent), + ) + + if err != nil { + return fiber.NewError(fiber.StatusInternalServerError, err.Error()) + } else if access, refresh, err := services.GetToken(ticket); err != nil { + return fiber.NewError(fiber.StatusInternalServerError, err.Error()) + } else { + services.AddEvent(user, "oauth.connect", client.Alias, c.IP(), c.Get(fiber.HeaderUserAgent)) + return c.JSON(fiber.Map{ + "access_token": access, + "refresh_token": refresh, + "redirect_uri": redirect, + "ticket": ticket, + }) + } + default: + return fiber.NewError(fiber.StatusBadRequest, "unsupported response type") + } +} diff --git a/pkg/internal/services/ticket.go b/pkg/internal/services/ticket.go index 7039e0c..e2b9c15 100644 --- a/pkg/internal/services/ticket.go +++ b/pkg/internal/services/ticket.go @@ -81,7 +81,7 @@ func NewOauthTicket( AccessToken: lo.ToPtr(uuid.NewString()), RefreshToken: lo.ToPtr(uuid.NewString()), AvailableAt: lo.ToPtr(time.Now()), - ExpiredAt: lo.ToPtr(time.Now()), + ExpiredAt: lo.ToPtr(time.Now().Add(7 * 24 * time.Hour)), ClientID: &client.ID, AccountID: user.ID, } diff --git a/web/src/components/Copyright.vue b/web/src/components/Copyright.vue index 8bca377..873ab83 100755 --- a/web/src/components/Copyright.vue +++ b/web/src/components/Copyright.vue @@ -1,6 +1,6 @@ diff --git a/web/src/components/NotificationList.vue b/web/src/components/NotificationList.vue index 7f3cfd8..2c5b79d 100755 --- a/web/src/components/NotificationList.vue +++ b/web/src/components/NotificationList.vue @@ -1,23 +1,17 @@ - + Something went wrong... {{ error }} @@ -39,8 +34,11 @@ + + diff --git a/web/src/layouts/master.vue b/web/src/layouts/master.vue index 4b1d5c9..6c8ef89 100755 --- a/web/src/layouts/master.vue +++ b/web/src/layouts/master.vue @@ -1,22 +1,5 @@