From d0e6392f1a1706a38baee34c4c89b8d5253b9917 Mon Sep 17 00:00:00 2001 From: Lakhan Samani Date: Wed, 1 May 2024 16:36:21 +0530 Subject: [PATCH] fix roblox oauth --- server/constants/auth_methods.go | 2 + server/constants/oauth_info_urls.go | 3 ++ server/handlers/oauth_callback.go | 67 +++++++++++++++++++++++++++++ server/handlers/oauth_login.go | 18 ++++++++ server/oauth/oauth.go | 13 +----- 5 files changed, 92 insertions(+), 11 deletions(-) diff --git a/server/constants/auth_methods.go b/server/constants/auth_methods.go index 6c800c4..ca2002f 100644 --- a/server/constants/auth_methods.go +++ b/server/constants/auth_methods.go @@ -27,4 +27,6 @@ const ( AuthRecipeMethodMicrosoft = "microsoft" // AuthRecipeMethodTwitch is the twitch auth method AuthRecipeMethodTwitch = "twitch" + // AuthRecipeMethodRoblox is the roblox auth method + AuthRecipeMethodRoblox = "roblox" ) diff --git a/server/constants/oauth_info_urls.go b/server/constants/oauth_info_urls.go index 1079db3..1297a2e 100644 --- a/server/constants/oauth_info_urls.go +++ b/server/constants/oauth_info_urls.go @@ -17,6 +17,9 @@ const ( TwitterUserInfoURL = "https://api.twitter.com/2/users/me?user.fields=id,name,profile_image_url,username" + // RobloxUserInfoURL is the URL to get user info from Roblox + RobloxUserInfoURL = "https://apis.roblox.com/oauth/v1/userinfo" + DiscordUserInfoURL = "https://discord.com/api/oauth2/@me" // Get microsoft user info. // Ref: https://learn.microsoft.com/en-us/azure/active-directory/develop/userinfo diff --git a/server/handlers/oauth_callback.go b/server/handlers/oauth_callback.go index 2a947d6..415bd0a 100644 --- a/server/handlers/oauth_callback.go +++ b/server/handlers/oauth_callback.go @@ -89,6 +89,8 @@ func OAuthCallbackHandler() gin.HandlerFunc { user, err = processMicrosoftUserInfo(ctx, oauthCode) case constants.AuthRecipeMethodTwitch: user, err = processTwitchUserInfo(ctx, oauthCode) + case constants.AuthRecipeMethodRoblox: + user, err = processRobloxUserInfo(ctx, oauthCode, sessionState) default: log.Info("Invalid oauth provider") err = fmt.Errorf(`invalid oauth provider`) @@ -818,3 +820,68 @@ func processTwitchUserInfo(ctx context.Context, code string) (*models.User, erro return user, nil } + +// process roblox user information +func processRobloxUserInfo(ctx context.Context, code, verifier string) (*models.User, error) { + oauth2Token, err := oauth.OAuthProviders.RobloxConfig.Exchange(ctx, code, oauth2.SetAuthURLParam("code_verifier", verifier)) + if err != nil { + log.Debug("Failed to exchange code for token: ", err) + return nil, fmt.Errorf("invalid roblox exchange code: %s", err.Error()) + } + + client := http.Client{} + req, err := http.NewRequest("GET", constants.RobloxUserInfoURL, nil) + if err != nil { + log.Debug("Failed to create roblox user info request: ", err) + return nil, fmt.Errorf("error creating roblox user info request: %s", err.Error()) + } + req.Header = http.Header{ + "Authorization": []string{fmt.Sprintf("Bearer %s", oauth2Token.AccessToken)}, + } + + response, err := client.Do(req) + if err != nil { + log.Debug("Failed to request roblox user info: ", err) + return nil, err + } + + defer response.Body.Close() + body, err := io.ReadAll(response.Body) + if err != nil { + log.Debug("Failed to read roblox user info response body: ", err) + return nil, fmt.Errorf("failed to read roblox response body: %s", err.Error()) + } + + if response.StatusCode >= 400 { + log.Debug("Failed to request roblox user info: ", string(body)) + return nil, fmt.Errorf("failed to request roblox user info: %s", string(body)) + } + + userRawData := make(map[string]interface{}) + json.Unmarshal(body, &userRawData) + + // log.Info(userRawData) + nameArr := strings.SplitAfterN(userRawData["name"].(string), " ", 2) + firstName := nameArr[0] + lastName := "" + if len(nameArr) == 2 { + lastName = nameArr[1] + } + nickname := userRawData["nickname"].(string) + profilePicture := userRawData["picture"].(string) + email := "" + if val, ok := userRawData["email"]; ok { + email = val.(string) + } else { + email = userRawData["preferred_username"].(string) + } + user := &models.User{ + GivenName: &firstName, + FamilyName: &lastName, + Picture: &profilePicture, + Nickname: &nickname, + Email: &email, + } + + return user, nil +} diff --git a/server/handlers/oauth_login.go b/server/handlers/oauth_login.go index 75a219f..6402340 100644 --- a/server/handlers/oauth_login.go +++ b/server/handlers/oauth_login.go @@ -265,6 +265,24 @@ func OAuthLoginHandler() gin.HandlerFunc { oauth.OAuthProviders.TwitchConfig.RedirectURL = hostname + "/oauth_callback/" + constants.AuthRecipeMethodTwitch url := oauth.OAuthProviders.TwitchConfig.AuthCodeURL(oauthStateString) c.Redirect(http.StatusTemporaryRedirect, url) + case constants.AuthRecipeMethodRoblox: + if oauth.OAuthProviders.RobloxConfig == nil { + log.Debug("RobloxConfig OAuth provider is not configured") + isProviderConfigured = false + break + } + err := memorystore.Provider.SetState(oauthStateString, constants.AuthRecipeMethodRoblox) + if err != nil { + log.Debug("Error setting state: ", err) + c.JSON(500, gin.H{ + "error": "internal server error", + }) + return + } + // during the init of OAuthProvider authorizer url might be empty + oauth.OAuthProviders.RobloxConfig.RedirectURL = hostname + "/oauth_callback/" + constants.AuthRecipeMethodRoblox + url := oauth.OAuthProviders.RobloxConfig.AuthCodeURL(oauthStateString) + c.Redirect(http.StatusTemporaryRedirect, url) default: log.Debug("Invalid oauth provider: ", provider) c.JSON(422, gin.H{ diff --git a/server/oauth/oauth.go b/server/oauth/oauth.go index 1ea738e..8f64fee 100644 --- a/server/oauth/oauth.go +++ b/server/oauth/oauth.go @@ -42,7 +42,6 @@ type OIDCProvider struct { GoogleOIDC *oidc.Provider MicrosoftOIDC *oidc.Provider TwitchOIDC *oidc.Provider - RobloxOIDC *oidc.Provider } var ( @@ -261,22 +260,14 @@ func InitOAuth() error { if err != nil { robloxClientSecret = "" } - if robloxClientID != "" && robloxClientSecret != "" { - p, err := oidc.NewProvider(ctx, "https://apis.roblox.com/oauth") - if err != nil { - log.Debugf(ctx, "Error while creating OIDC provider for roblox: %v", err) - return err - } - - OIDCProviders.RobloxOIDC = p OAuthProviders.RobloxConfig = &oauth2.Config{ ClientID: robloxClientID, ClientSecret: robloxClientSecret, RedirectURL: "/oauth_callback/roblox", Endpoint: oauth2.Endpoint{ - AuthURL: "https://apis.roblox.com/oauth/authorize", - TokenURL: "https://apis.roblox.com/oauth/token", + AuthURL: "https://apis.roblox.com/oauth/v1/authorize", + TokenURL: "https://apis.roblox.com/oauth/v1/token", }, Scopes: []string{oidc.ScopeOpenID, "profile"}, }