feat: add facebook login

Resolves #36
This commit is contained in:
Lakhan Samani
2021-09-05 03:57:29 +05:30
parent af3222b5e0
commit 88d2195c0a
7 changed files with 132 additions and 67 deletions

View File

@@ -4,7 +4,7 @@ var (
// Ref: https://github.com/qor/auth/blob/master/providers/google/google.go
GoogleUserInfoURL = "https://www.googleapis.com/oauth2/v3/userinfo"
// Ref: https://github.com/qor/auth/blob/master/providers/facebook/facebook.go#L18
FacebookUserInfoURL = "https://graph.facebook.com/me?access_token="
FacebookUserInfoURL = "https://graph.facebook.com/me?fields=id,first_name,last_name,name,email,picture&access_token="
// Ref: https://docs.github.com/en/developers/apps/building-github-apps/identifying-and-authorizing-users-for-github-apps#3-your-github-app-accesses-the-api-with-the-users-access-token
GithubUserInfoURL = "https://api.github.com/user"
)

View File

@@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"strings"
"time"
@@ -82,7 +83,7 @@ func processGoogleUserInfo(code string, c *gin.Context) error {
func processGithubUserInfo(code string, c *gin.Context) error {
token, err := oauth.OAuthProvider.GithubConfig.Exchange(oauth2.NoContext, code)
if err != nil {
return fmt.Errorf("invalid google exchange code: %s", err.Error())
return fmt.Errorf("invalid github exchange code: %s", err.Error())
}
client := http.Client{}
req, err := http.NewRequest("GET", constants.GithubUserInfoURL, nil)
@@ -101,7 +102,7 @@ func processGithubUserInfo(code string, c *gin.Context) error {
defer response.Body.Close()
body, err := ioutil.ReadAll(response.Body)
if err != nil {
return fmt.Errorf("failed to read google response body: %s", err.Error())
return fmt.Errorf("failed to read github response body: %s", err.Error())
}
userRawData := make(map[string]string)
@@ -156,6 +157,77 @@ func processGithubUserInfo(code string, c *gin.Context) error {
return nil
}
func processFacebookUserInfo(code string, c *gin.Context) error {
token, err := oauth.OAuthProvider.FacebookConfig.Exchange(oauth2.NoContext, code)
if err != nil {
return fmt.Errorf("invalid facebook exchange code: %s", err.Error())
}
client := http.Client{}
req, err := http.NewRequest("GET", constants.FacebookUserInfoURL+token.AccessToken, nil)
if err != nil {
return fmt.Errorf("error creating facebook user info request: %s", err.Error())
}
response, err := client.Do(req)
if err != nil {
log.Println("err:", err)
return err
}
defer response.Body.Close()
body, err := ioutil.ReadAll(response.Body)
if err != nil {
return fmt.Errorf("failed to read facebook response body: %s", err.Error())
}
userRawData := make(map[string]interface{})
json.Unmarshal(body, &userRawData)
email := fmt.Sprintf("%v", userRawData["email"])
existingUser, err := db.Mgr.GetUserByEmail(email)
picObject := userRawData["picture"].(map[string]interface{})["data"]
picDataObject := picObject.(map[string]interface{})
user := db.User{
FirstName: fmt.Sprintf("%v", userRawData["first_name"]),
LastName: fmt.Sprintf("%v", userRawData["last_name"]),
Image: fmt.Sprintf("%v", picDataObject["url"]),
Email: email,
EmailVerifiedAt: time.Now().Unix(),
}
if err != nil {
// user not registered, register user and generate session token
user.SignupMethod = enum.Github.String()
} else {
// user exists in db, check if method was google
// if not append google to existing signup method and save it
signupMethod := existingUser.SignupMethod
if !strings.Contains(signupMethod, enum.Github.String()) {
signupMethod = signupMethod + "," + enum.Github.String()
}
user.SignupMethod = signupMethod
user.Password = existingUser.Password
}
user, _ = db.Mgr.SaveUser(user)
user, _ = db.Mgr.GetUserByEmail(user.Email)
userIdStr := fmt.Sprintf("%v", user.ID)
refreshToken, _, _ := utils.CreateAuthToken(utils.UserAuthInfo{
ID: userIdStr,
Email: user.Email,
}, enum.RefreshToken)
accessToken, _, _ := utils.CreateAuthToken(utils.UserAuthInfo{
ID: userIdStr,
Email: user.Email,
}, enum.AccessToken)
utils.SetCookie(c, accessToken)
session.SetToken(userIdStr, refreshToken)
return nil
}
func OAuthCallbackHandler() gin.HandlerFunc {
return func(c *gin.Context) {
provider := c.Param("oauth_provider")
@@ -181,6 +253,8 @@ func OAuthCallbackHandler() gin.HandlerFunc {
err = processGoogleUserInfo(code, c)
case enum.Github.String():
err = processGithubUserInfo(code, c)
case enum.Facebook.String():
err = processFacebookUserInfo(code, c)
default:
err = fmt.Errorf(`invalid oauth provider`)
}

View File

@@ -41,6 +41,11 @@ func OAuthLoginHandler() gin.HandlerFunc {
oauth.OAuthProvider.GithubConfig.RedirectURL = constants.AUTHORIZER_URL + "/oauth_callback/github"
url := oauth.OAuthProvider.GithubConfig.AuthCodeURL(oauthStateString)
c.Redirect(http.StatusTemporaryRedirect, url)
case enum.Facebook.String():
session.SetToken(oauthStateString, enum.Github.String())
oauth.OAuthProvider.FacebookConfig.RedirectURL = constants.AUTHORIZER_URL + "/oauth_callback/facebook"
url := oauth.OAuthProvider.FacebookConfig.AuthCodeURL(oauthStateString)
c.Redirect(http.StatusTemporaryRedirect, url)
default:
c.JSON(422, gin.H{
"message": "Invalid oauth provider",

View File

@@ -3,14 +3,15 @@ package oauth
import (
"github.com/authorizerdev/authorizer/server/constants"
"golang.org/x/oauth2"
facebookOAuth2 "golang.org/x/oauth2/facebook"
githubOAuth2 "golang.org/x/oauth2/github"
googleOAuth2 "golang.org/x/oauth2/google"
)
type OAuthProviders struct {
GoogleConfig *oauth2.Config
GithubConfig *oauth2.Config
// FacebookConfig *oauth2.Config
GoogleConfig *oauth2.Config
GithubConfig *oauth2.Config
FacebookConfig *oauth2.Config
}
var OAuthProvider OAuthProviders
@@ -33,12 +34,13 @@ func InitOAuth() {
Endpoint: githubOAuth2.Endpoint,
}
}
// if constants.FACEBOOK_CLIENT_ID != "" && constants.FACEBOOK_CLIENT_SECRET != "" {
// OAuthProvider.FacebookConfig = &oauth2.Config{
// ClientID: constants.FACEBOOK_CLIENT_ID,
// ClientSecret: constants.FACEBOOK_CLIENT_SECRET,
// RedirectURL: "/oauth_callback/facebook/",
// Endpoint: facebookOAuth2.Endpoint,
// }
// }
if constants.FACEBOOK_CLIENT_ID != "" && constants.FACEBOOK_CLIENT_SECRET != "" {
OAuthProvider.FacebookConfig = &oauth2.Config{
ClientID: constants.FACEBOOK_CLIENT_ID,
ClientSecret: constants.FACEBOOK_CLIENT_SECRET,
RedirectURL: constants.AUTHORIZER_URL + "/oauth_callback/facebook",
Endpoint: facebookOAuth2.Endpoint,
Scopes: []string{"public_profile", "email"},
}
}
}