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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -2,51 +2,41 @@ import React from 'react';
import { useAuthorizer } from '@authorizerdev/authorizer-react';
export default function Dashboard() {
const [loading, setLoading] = React.useState(false);
const { user, setToken, graphQlRef } = useAuthorizer();
const [loading, setLoading] = React.useState(false);
const { user, setToken, authorizerRef } = useAuthorizer();
const onLogout = async () => {
setLoading(true);
await graphQlRef
.mutation(
`
mutation {
logout {
message
}
}
`
)
.toPromise();
setToken(null);
setLoading(false);
};
const onLogout = async () => {
setLoading(true);
await authorizerRef.logout();
setToken(null);
setLoading(false);
};
return (
<div>
<h1>Hey 👋,</h1>
<p>Thank you for joining authorizer demo app.</p>
<p>
Your email address is{' '}
<a href={`mailto:${user?.email}`} style={{ color: '#3B82F6' }}>
{user?.email}
</a>
</p>
return (
<div>
<h1>Hey 👋,</h1>
<p>Thank you for joining authorizer demo app.</p>
<p>
Your email address is{' '}
<a href={`mailto:${user?.email}`} style={{ color: '#3B82F6' }}>
{user?.email}
</a>
</p>
<br />
{loading ? (
<h3>Processing....</h3>
) : (
<h3
style={{
color: '#3B82F6',
cursor: 'pointer',
}}
onClick={onLogout}
>
Logout
</h3>
)}
</div>
);
<br />
{loading ? (
<h3>Processing....</h3>
) : (
<h3
style={{
color: '#3B82F6',
cursor: 'pointer',
}}
onClick={onLogout}
>
Logout
</h3>
)}
</div>
);
}

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"},
}
}
}