From dc55b6f19a57af87f402b4c0f63461c5ff92cd36 Mon Sep 17 00:00:00 2001 From: Ada Werefox Date: Tue, 22 Apr 2025 14:21:40 -0700 Subject: [PATCH] OAuth keys are saved as cookies and added to database. --- config.sample.toml | 9 +++ gin-cpularp.go | 152 ++++++++++++++++++++++------------- lib/dbcommands/dbcommands.go | 46 ++++++++++- lib/dbcommands/go.mod | 1 + lib/dbcommands/go.sum | 2 + src/templates/dashboard.html | 2 +- src/templates/userinfo.html | 10 +-- 7 files changed, 159 insertions(+), 63 deletions(-) create mode 100644 config.sample.toml diff --git a/config.sample.toml b/config.sample.toml new file mode 100644 index 0000000..328f7b2 --- /dev/null +++ b/config.sample.toml @@ -0,0 +1,9 @@ +[api] +domain = "localhost" +port = "31337" +https = false + +[oauth] +# You can get all of this from https://discord.com/developers/applications +clientid = "[CHANGE ME]" +clientsecret = "[CHANGE ME]" diff --git a/gin-cpularp.go b/gin-cpularp.go index ef0a6f6..06ac4bc 100644 --- a/gin-cpularp.go +++ b/gin-cpularp.go @@ -2,25 +2,30 @@ package main import ( "encoding/json" + "fmt" "io" "log" "net/http" "os" - "strconv" - // "example.com/lib/dbcommands" + "example.com/lib/dbcommands" "github.com/gin-gonic/gin" "github.com/pelletier/go-toml/v2" "github.com/ravener/discord-oauth2" "github.com/xyproto/randomstring" "golang.org/x/oauth2" + "gorm.io/gorm" ) type appConfig struct { + API struct { + Domain string + Port string + Https bool + } OAuth struct { ClientID string ClientSecret string - RedirectUrl string } } @@ -48,11 +53,11 @@ type discordUser struct { Premium_Type float64 } +var db *gorm.DB var config appConfig var oauthConfig *oauth2.Config -var oauthToken *oauth2.Token -func parseConfig(configPath string) { +func parseConfig(configPath string) appConfig { configFile, err := os.Open(configPath) if err != nil { log.Fatal(err) @@ -61,17 +66,28 @@ func parseConfig(configPath string) { if err != nil { log.Fatal(err) } - err = toml.Unmarshal(configFileContent, &config) + var configObject appConfig + err = toml.Unmarshal(configFileContent, &configObject) if err != nil { log.Fatal(err) } + return configObject } -func createDiscordOAuthConfig(clientId string, clientSecret string, redirectUrl string) *oauth2.Config { +func createDiscordOAuthConfig() *oauth2.Config { + protocolString := "" + if config.API.Https { + protocolString = "https://" + config.API.Port = "" + } else { + protocolString = "http://" + config.API.Port = fmt.Sprintf(":%s", config.API.Port) + } + redirectUrlString := fmt.Sprintf("%s%s%s/auth/callback", protocolString, config.API.Domain, config.API.Port) config := &oauth2.Config{ - ClientID: clientId, - ClientSecret: clientSecret, - RedirectURL: redirectUrl, + ClientID: config.OAuth.ClientID, + ClientSecret: config.OAuth.ClientSecret, + RedirectURL: redirectUrlString, Scopes: []string{discord.ScopeIdentify}, Endpoint: discord.Endpoint, } @@ -79,13 +95,19 @@ func createDiscordOAuthConfig(clientId string, clientSecret string, redirectUrl } func loginDisplay(context *gin.Context) { - if oauthToken.Valid() { - context.Redirect(http.StatusTemporaryRedirect, "/dashboard") - } else { - context.HTML(http.StatusOK, "root.html", gin.H{ - "message": "hello, world!", - }) + oauthTokenString, err := context.Cookie("discord-oauthtoken") + if err == nil { + var oauthToken *oauth2.Token + err := json.Unmarshal([]byte(oauthTokenString), &oauthToken) + if err == nil { + if oauthToken.Valid() { + context.Redirect(http.StatusTemporaryRedirect, "/dashboard") + } + } } + context.HTML(http.StatusOK, "root.html", gin.H{ + "message": "Please log in.", + }) } func loginRedirect(context *gin.Context) { @@ -96,17 +118,19 @@ func authCallback(context *gin.Context) { oauthState := randomstring.CookieFriendlyString(32) context.Set("state", oauthState) var err error - oauthToken, err = oauthConfig.Exchange(context.Request.Context(), context.Query("code")) + oauthToken, err := oauthConfig.Exchange(context.Request.Context(), context.Query("code")) if err != nil { context.HTML(http.StatusInternalServerError, "internalservererror.html", gin.H{ "message": "internalservererror", }) } + oauthTokenJSON, _ := json.Marshal(oauthToken) + context.SetCookie("discord-oauthtoken", string(oauthTokenJSON), 0, "", config.API.Domain, false, false) context.Redirect(http.StatusTemporaryRedirect, "/dashboard") } -func getDiscordUserInfo(context *gin.Context) discordUser { +func getDiscordUser(context *gin.Context, oauthToken *oauth2.Token) discordUser { response, err := oauthConfig.Client(context.Request.Context(), oauthToken).Get("https://discord.com/api/users/@me") if err != nil || response.StatusCode != 200 { responseMessage := "" @@ -126,48 +150,68 @@ func getDiscordUserInfo(context *gin.Context) discordUser { "message": err.Error(), }) } - var discordUserInfo discordUser - json.Unmarshal(body, &discordUserInfo) - return discordUserInfo + var user discordUser + json.Unmarshal(body, &user) + return user } -func dashboardDisplay(context *gin.Context) { - if oauthToken.Valid() { - userInfo := getDiscordUserInfo(context) - context.HTML(http.StatusOK, "dashboard.html", gin.H{ - "discordUser": gin.H{ - "id": userInfo.Id, - "username": userInfo.Username, - "avatar": userInfo.Avatar, - "discriminator": userInfo.Discriminator, - "public_flags": userInfo.Public_Flags, - "flags": userInfo.Flags, - "banner": userInfo.Banner, - "accent_color": strconv.FormatFloat(userInfo.Accent_Color, 'f', 0, 64), - "global_name": userInfo.Global_Name, - "avatar_decoration_data": gin.H{ - "asset": userInfo.Avatar_Decoration_Data.Asset, - "sku_id": userInfo.Avatar_Decoration_Data.Sku_Id, - "expires_at": userInfo.Avatar_Decoration_Data.Expires_At, - }, - "collectibles": userInfo.Collectibles, - "banner_color": userInfo.Banner_Color, - "clan": userInfo.Clan, - "primary_guild": userInfo.Primary_Guild, - "mfa_enabled": userInfo.Mfa_Eneabled, - "locale": userInfo.Locale, - "premium_type": userInfo.Premium_Type, - }, - }) +func createOrUpdateUser(context *gin.Context, oauthToken *oauth2.Token) { + user := getDiscordUser(context, oauthToken) + oauthTokenJSON, err := json.Marshal(oauthToken) + if err != nil { + log.Println(err) + } + dbUser := dbcommands.User{ + DisplayName: user.Global_Name, + Username: user.Username, + Id: user.Id, + Avatar: user.Avatar, + AvatarDecoration: user.Avatar_Decoration_Data.Asset, + LoginToken: string(oauthTokenJSON), + } + if dbcommands.DatabaseUserExists(db, user.Id) { + dbOAuthToken := dbcommands.GetDatabaseUserToken(db, user.Id) + if dbOAuthToken == "" { + context.SetCookie("discord-oauthtoken", dbOAuthToken, 0, "", config.API.Domain, false, false) + dbUser.LoginToken = dbOAuthToken + } + err := dbcommands.UpdateDatabaseUser(db, dbUser) + if err != nil { + log.Println(err) + } } else { - context.Redirect(http.StatusTemporaryRedirect, "/") + err := dbcommands.CreateDatabaseUser(db, dbUser) + if err != nil { + log.Println(err) + } } } +func dashboardDisplay(context *gin.Context) { + oauthTokenJSON, err := context.Cookie("discord-oauthtoken") + if err == nil { + var oauthToken *oauth2.Token + err := json.Unmarshal([]byte(oauthTokenJSON), &oauthToken) + if err == nil { + if oauthToken.Valid() { + createOrUpdateUser(context, oauthToken) + user := getDiscordUser(context, oauthToken) + context.HTML(http.StatusOK, "dashboard.html", user) + return + } + } else { + log.Println(err) + } + } else { + log.Println(err) + } + context.Redirect(http.StatusTemporaryRedirect, "/") +} + func main() { - // db := dbcommands.InitializeDatabase() - parseConfig("config.toml") - oauthConfig = createDiscordOAuthConfig(config.OAuth.ClientID, config.OAuth.ClientSecret, config.OAuth.RedirectUrl) + db = dbcommands.InitializeDatabase() + config = parseConfig("config.toml") + oauthConfig = createDiscordOAuthConfig() app := gin.Default() app.LoadHTMLGlob("src/templates/*.html") app.Static("/public", "./public") @@ -175,5 +219,5 @@ func main() { app.GET("/login", loginRedirect) app.GET("/auth/callback", authCallback) app.GET("/dashboard", dashboardDisplay) - app.Run(":8080") + app.Run(":31337") } diff --git a/lib/dbcommands/dbcommands.go b/lib/dbcommands/dbcommands.go index 602d4d2..02a591c 100644 --- a/lib/dbcommands/dbcommands.go +++ b/lib/dbcommands/dbcommands.go @@ -1,6 +1,7 @@ package dbcommands import ( + "errors" "log" "gorm.io/driver/sqlite" @@ -9,9 +10,12 @@ import ( type User struct { gorm.Model - Name string - Id string - LoginToken string + Id string + DisplayName string + Username string + Avatar string + AvatarDecoration string + LoginToken string } func InitializeDatabase() *gorm.DB { @@ -22,3 +26,39 @@ func InitializeDatabase() *gorm.DB { db.AutoMigrate(&User{}) return db } + +func GetDatabaseUserToken(db *gorm.DB, id string) string { + var dbUser User + result := db.Where("id = ?", id).Select("login_token").Take(&dbUser) + if errors.Is(result.Error, gorm.ErrRecordNotFound) { + return "" + } else { + return dbUser.LoginToken + } +} + +func DatabaseUserExists(db *gorm.DB, id string) bool { + var queryUser User + result := db.Where("id = ?", id).Take(&queryUser) + if errors.Is(result.Error, gorm.ErrRecordNotFound) { + return false + } else { + return true + } +} + +func UpdateDatabaseUser(db *gorm.DB, user User) error { + result := db.Save(&user) + if result.Error != nil { + return result.Error + } + return nil +} + +func CreateDatabaseUser(db *gorm.DB, user User) error { + result := db.Create(&user) + if result.Error != nil { + return result.Error + } + return nil +} diff --git a/lib/dbcommands/go.mod b/lib/dbcommands/go.mod index 1255bec..b160e89 100644 --- a/lib/dbcommands/go.mod +++ b/lib/dbcommands/go.mod @@ -6,6 +6,7 @@ require ( github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect github.com/mattn/go-sqlite3 v1.14.22 // indirect + golang.org/x/oauth2 v0.29.0 // indirect golang.org/x/text v0.14.0 // indirect gorm.io/driver/sqlite v1.5.7 // indirect gorm.io/gorm v1.25.12 // indirect diff --git a/lib/dbcommands/go.sum b/lib/dbcommands/go.sum index 76138d5..d440d87 100644 --- a/lib/dbcommands/go.sum +++ b/lib/dbcommands/go.sum @@ -4,6 +4,8 @@ github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98= +golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= gorm.io/driver/sqlite v1.5.7 h1:8NvsrhP0ifM7LX9G4zPB97NwovUakUxc+2V2uuf3Z1I= diff --git a/src/templates/dashboard.html b/src/templates/dashboard.html index 5419b66..7204541 100644 --- a/src/templates/dashboard.html +++ b/src/templates/dashboard.html @@ -22,7 +22,7 @@
- {{ template "userinfo.html" .discordUser }} + {{ template "userinfo.html" . }}
diff --git a/src/templates/userinfo.html b/src/templates/userinfo.html index 36f3161..808ee40 100644 --- a/src/templates/userinfo.html +++ b/src/templates/userinfo.html @@ -2,17 +2,17 @@
+ src="https://cdn.discordapp.com/avatar-decoration-presets/{{ .Avatar_Decoration_Data.Asset }}.png" /> + src="https://cdn.discordapp.com/avatars/{{ .Id }}/{{ .Avatar }}.png" />

- {{ .global_name }} + {{ .Global_Name }}

- - @{{ .username }} + @{{ .Username }}