From 0dc1423b9d8b2038ec22a6fbc3d6ae22d5b6783b Mon Sep 17 00:00:00 2001 From: eccentric Date: Sat, 9 Dec 2023 15:35:33 +0000 Subject: [PATCH] Start Discord Auth --- aid/config.go | 20 +++++++++++++++++ default.config.ini | 10 ++++++++- handlers/auth.go | 50 +++++++++++++++++++++++++++++++++++-------- handlers/discord.go | 12 +++++++++++ handlers/discovery.go | 4 ++-- handlers/snow.go | 6 ++++++ main.go | 21 ++++++++++++------ 7 files changed, 104 insertions(+), 19 deletions(-) create mode 100644 handlers/discord.go diff --git a/aid/config.go b/aid/config.go index 4bf3d4b..a9b1898 100644 --- a/aid/config.go +++ b/aid/config.go @@ -13,6 +13,11 @@ type CS struct { Type string DropAllTables bool } + Discord struct { + ID string + Secret string + Token string + } Output struct { Level string } @@ -61,6 +66,21 @@ func LoadConfig(file []byte) { panic("Output Level must be either dev or prod") } + Config.Discord.ID = cfg.Section("discord").Key("id").String() + if Config.Discord.ID == "" { + panic("Discord Client ID is empty") + } + + Config.Discord.Secret = cfg.Section("discord").Key("secret").String() + if Config.Discord.Secret == "" { + panic("Discord Client Secret is empty") + } + + Config.Discord.Token = cfg.Section("discord").Key("token").String() + if Config.Discord.Token == "" { + panic("Discord Bot Token is empty") + } + Config.API.Host = cfg.Section("api").Key("host").String() if Config.API.Host == "" { panic("API Host is empty") diff --git a/default.config.ini b/default.config.ini index 4c1e59a..876b42f 100644 --- a/default.config.ini +++ b/default.config.ini @@ -6,6 +6,14 @@ type="postgres" ; drop all tables at start of program drop=false +[discord] +; discord oauth2 client id +id="1234567890..." +; discord oauth2 client secret +secret="abcdefg..." +; discord bot token +token="OTK...." + [output] ; level of logging ; info = everything @@ -19,7 +27,7 @@ port=":3000" ; host that the api is running on ; e.g. if you are running the api on your local machine, you would set this to 127.0.0.1 ; if you are running the api on a server, you would set this to the ip of the server or the domain name -host="127.0.0.1" +host="http://127.0.0.1" [jwt] ; secret for jwt signing diff --git a/handlers/auth.go b/handlers/auth.go index 5b16676..ae44cad 100644 --- a/handlers/auth.go +++ b/handlers/auth.go @@ -10,20 +10,20 @@ import ( ) var ( - oauthTokenGrantTypes = map[string]func(c *fiber.Ctx, body *OAuthTokenBody) error{ - "client_credentials": PostOAuthTokenClientCredentials, - "password": PostOAuthTokenPassword, + oauthTokenGrantTypes = map[string]func(c *fiber.Ctx, body *FortniteTokenBody) error{ + "client_credentials": PostTokenClientCredentials, + "password": PostTokenPassword, } ) -type OAuthTokenBody struct { +type FortniteTokenBody struct { GrantType string `form:"grant_type" binding:"required"` Username string `form:"username"` Password string `form:"password"` } -func PostOAuthToken(c *fiber.Ctx) error { - var body OAuthTokenBody +func PostFortniteToken(c *fiber.Ctx) error { + var body FortniteTokenBody if err := c.BodyParser(&body); err != nil { return c.Status(fiber.StatusBadRequest).JSON(aid.ErrorBadRequest("Invalid Request Body")) @@ -36,7 +36,7 @@ func PostOAuthToken(c *fiber.Ctx) error { return c.Status(fiber.StatusBadRequest).JSON(aid.ErrorBadRequest("Invalid Grant Type")) } -func PostOAuthTokenClientCredentials(c *fiber.Ctx, body *OAuthTokenBody) error { +func PostTokenClientCredentials(c *fiber.Ctx, body *FortniteTokenBody) error { credentials, err := aid.JWTSign(aid.JSON{ "snow_id": 0, // custom "creation_date": time.Now().Format("2006-01-02T15:04:05.999Z"), @@ -56,7 +56,7 @@ func PostOAuthTokenClientCredentials(c *fiber.Ctx, body *OAuthTokenBody) error { }) } -func PostOAuthTokenPassword(c *fiber.Ctx, body *OAuthTokenBody) error { +func PostTokenPassword(c *fiber.Ctx, body *FortniteTokenBody) error { if body.Username == "" || body.Password == "" { return c.Status(fiber.StatusBadRequest).JSON(aid.ErrorBadRequest("Username/Password is empty")) } @@ -150,7 +150,7 @@ func GetOAuthVerify(c *fiber.Ctx) error { }) } -func OAuthMiddleware(c *fiber.Ctx) error { +func FortniteMiddleware(c *fiber.Ctx) error { auth := c.Get("Authorization") if auth == "" { return c.Status(fiber.StatusForbidden).JSON(aid.ErrorBadRequest("Authorization Header is empty")) @@ -177,7 +177,39 @@ func OAuthMiddleware(c *fiber.Ctx) error { } c.Locals("person", person) + return c.Next() +} +func FrontendMiddleware(c *fiber.Ctx) error { + auth := c.Get("Authorization") + if auth == "" { + return c.Status(fiber.StatusForbidden).JSON(aid.ErrorBadRequest("Authorization Header is empty")) + } + + claims, err := aid.JWTVerify(auth) + if err != nil { + return c.Status(fiber.StatusForbidden).JSON(aid.ErrorBadRequest("Invalid Access Token")) + } + + if claims["snow_id"] == nil { + return c.Status(fiber.StatusForbidden).JSON(aid.ErrorBadRequest("Invalid Access Token")) + } + + if claims["frontend"] == nil { + return c.Status(fiber.StatusForbidden).JSON(aid.ErrorBadRequest("Invalid Claims")) + } + + snowId, ok := claims["snow_id"].(string) + if !ok { + return c.Status(fiber.StatusForbidden).JSON(aid.ErrorBadRequest("Invalid Access Token")) + } + + person := p.Find(snowId) + if person == nil { + return c.Status(fiber.StatusForbidden).JSON(aid.ErrorBadRequest("Invalid Access Token")) + } + + c.Locals("person", person) return c.Next() } diff --git a/handlers/discord.go b/handlers/discord.go new file mode 100644 index 0000000..4495da2 --- /dev/null +++ b/handlers/discord.go @@ -0,0 +1,12 @@ +package handlers + +import ( + "net/url" + + "github.com/ectrc/snow/aid" + "github.com/gofiber/fiber/v2" +) + +func GetDiscordOAuthURL(c *fiber.Ctx) error { + return c.Status(200).SendString("https://discord.com/api/oauth2/authorize?client_id="+ aid.Config.Discord.ID +"&redirect_uri="+ url.QueryEscape(aid.Config.API.Host + aid.Config.API.Port +"/snow/discord/callback") + "&response_type=code&scope=identify") +} \ No newline at end of file diff --git a/handlers/discovery.go b/handlers/discovery.go index 436faa4..6c19b67 100644 --- a/handlers/discovery.go +++ b/handlers/discovery.go @@ -76,7 +76,7 @@ func createPlaylist(mnemonic string, image string) aid.JSON { func PostDiscovery(c *fiber.Ctx) error { results := []aid.JSON{} for playlist := range fortnite.PlaylistImages { - results = append(results, createPlaylist(playlist, "http://" + aid.Config.API.Host + aid.Config.API.Port + "/snow/image/" + playlist + ".png?cache="+strconv.Itoa(rand.Intn(9999)))) + results = append(results, createPlaylist(playlist, aid.Config.API.Host + aid.Config.API.Port + "/snow/image/" + playlist + ".png?cache="+strconv.Itoa(rand.Intn(9999)))) } results = append(results, createPlaylist("Playlist_DefaultSolo", "http://bucket.retrac.site/55737fa15677cd57fab9e7f4499d62f89cfde320.png")) @@ -157,7 +157,7 @@ func GetContentPages(c *fiber.Ctx) error { playlists := []aid.JSON{} for playlist := range fortnite.PlaylistImages { playlists = append(playlists, aid.JSON{ - "image": "http://" + aid.Config.API.Host + aid.Config.API.Port + "/snow/image/" + playlist + ".png?cache="+strconv.Itoa(rand.Intn(9999)), + "image": aid.Config.API.Host + aid.Config.API.Port + "/snow/image/" + playlist + ".png?cache="+strconv.Itoa(rand.Intn(9999)), "playlist_name": playlist, "hidden": false, }) diff --git a/handlers/snow.go b/handlers/snow.go index e55b26a..2103226 100644 --- a/handlers/snow.go +++ b/handlers/snow.go @@ -4,6 +4,7 @@ import ( "strings" "github.com/ectrc/snow/fortnite" + "github.com/ectrc/snow/person" "github.com/gofiber/fiber/v2" ) @@ -26,3 +27,8 @@ func GetPlaylistImage(c *fiber.Ctx) error { c.Set("Content-Type", "image/png") return c.Send(image) } + +func GetPlayerLocker(c *fiber.Ctx) error { + person := c.Locals("person").(*person.Person) + return c.JSON(person.AthenaProfile.Items) +} \ No newline at end of file diff --git a/main.go b/main.go index 342e98f..54e269a 100644 --- a/main.go +++ b/main.go @@ -8,8 +8,8 @@ import ( "github.com/ectrc/snow/fortnite" "github.com/ectrc/snow/handlers" "github.com/ectrc/snow/storage" - "github.com/goccy/go-json" + "github.com/goccy/go-json" "github.com/gofiber/fiber/v2" ) @@ -70,7 +70,7 @@ func main() { account.Get("/public/account/:accountId/externalAuths", handlers.GetPublicAccountExternalAuths) account.Get("/public/account/displayName/:displayName", handlers.GetPublicAccountByDisplayName) account.Get("/oauth/verify", handlers.GetOAuthVerify) - account.Post("/oauth/token", handlers.PostOAuthToken) + account.Post("/oauth/token", handlers.PostFortniteToken) account.Delete("/oauth/sessions/kill", handlers.DeleteOAuthSessions) fortnite := r.Group("/fortnite/api") @@ -79,7 +79,7 @@ func main() { fortnite.Get("/calendar/v1/timeline", handlers.GetFortniteTimeline) storefront := fortnite.Group("/storefront/v2") - storefront.Use(handlers.OAuthMiddleware) + storefront.Use(handlers.FortniteMiddleware) storefront.Get("/catalog", handlers.GetStorefrontCatalog) storefront.Get("/keychain", handlers.GetStorefrontKeychain) @@ -92,7 +92,7 @@ func main() { storage.Get("/system/:fileName", handlers.GetCloudStorageFile) user := storage.Group("/user") - user.Use(handlers.OAuthMiddleware) + user.Use(handlers.FortniteMiddleware) user.Get("/:accountId", handlers.GetUserStorageFiles) user.Get("/:accountId/:fileName", handlers.GetUserStorageFile) user.Put("/:accountId/:fileName", handlers.PutUserStorageFile) @@ -105,7 +105,7 @@ func main() { game.Post("/profileToken/verify/:accountId", handlers.AnyNoContent) profile := game.Group("/profile/:accountId") - profile.Use(handlers.OAuthMiddleware) + profile.Use(handlers.FortniteMiddleware) profile.Post("/client/:action", handlers.PostProfileAction) lightswitch := r.Group("/lightswitch/api") @@ -115,14 +115,21 @@ func main() { snow.Get("/cosmetics", handlers.GetPreloadedCosmetics) snow.Get("/image/:playlist", handlers.GetPlaylistImage) + discord := snow.Group("/discord") + discord.Get("/", handlers.GetDiscordOAuthURL) + + player := snow.Group("/player") + player.Use(handlers.FrontendMiddleware) + player.Get("/locker", handlers.GetPlayerLocker) + r.Hooks().OnListen(func(ld fiber.ListenData) error { - aid.Print("Listening on " + "0.0.0.0:" + ld.Port) + aid.Print("Listening on " + aid.Config.API.Host + ":" + ld.Port) return nil }) r.All("*", func(c *fiber.Ctx) error { return c.Status(fiber.StatusNotFound).JSON(aid.ErrorNotFound) }) - err := r.Listen(aid.Config.API.Host + aid.Config.API.Port) + err := r.Listen("0.0.0.0" + aid.Config.API.Port) if err != nil { panic(fmt.Sprintf("Failed to listen: %v", err)) }