authorizer/server/handlers/oauthCallback.go

275 lines
8.1 KiB
Go
Raw Normal View History

package handlers
import (
"encoding/json"
"fmt"
"io/ioutil"
2021-09-04 22:27:29 +00:00
"log"
"net/http"
"strings"
"time"
2021-07-23 16:27:44 +00:00
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/enum"
"github.com/authorizerdev/authorizer/server/oauth"
"github.com/authorizerdev/authorizer/server/session"
"github.com/authorizerdev/authorizer/server/utils"
"github.com/gin-gonic/gin"
"golang.org/x/oauth2"
)
func processGoogleUserInfo(code string, role string, c *gin.Context) error {
token, err := oauth.OAuthProvider.GoogleConfig.Exchange(oauth2.NoContext, code)
if err != nil {
return fmt.Errorf("invalid google exchange code: %s", err.Error())
}
client := oauth.OAuthProvider.GoogleConfig.Client(oauth2.NoContext, token)
response, err := client.Get(constants.GoogleUserInfoURL)
if err != nil {
return err
}
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())
}
userRawData := make(map[string]string)
json.Unmarshal(body, &userRawData)
existingUser, err := db.Mgr.GetUserByEmail(userRawData["email"])
2021-07-17 23:18:42 +00:00
user := db.User{
FirstName: userRawData["given_name"],
LastName: userRawData["family_name"],
Image: userRawData["picture"],
Email: userRawData["email"],
EmailVerifiedAt: time.Now().Unix(),
}
if err != nil {
// user not registered, register user and generate session token
2021-07-17 23:18:42 +00:00
user.SignupMethod = enum.Google.String()
user.Roles = role
} 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.Google.String()) {
2021-07-17 23:18:42 +00:00
signupMethod = signupMethod + "," + enum.Google.String()
}
2021-07-17 23:18:42 +00:00
user.SignupMethod = signupMethod
user.Password = existingUser.Password
if !utils.IsValidRole(strings.Split(existingUser.Roles, ","), role) {
return fmt.Errorf("invalid role")
}
user.Roles = existingUser.Roles
2021-07-17 23:18:42 +00:00
}
2021-07-17 23:18:42 +00:00
user, _ = db.Mgr.SaveUser(user)
user, _ = db.Mgr.GetUserByEmail(user.Email)
2021-08-07 08:41:26 +00:00
userIdStr := fmt.Sprintf("%v", user.ID)
refreshToken, _, _ := utils.CreateAuthToken(user, enum.RefreshToken, role)
2021-07-17 23:18:42 +00:00
accessToken, _, _ := utils.CreateAuthToken(user, enum.AccessToken, role)
2021-07-17 23:18:42 +00:00
utils.SetCookie(c, accessToken)
session.SetToken(userIdStr, refreshToken)
return nil
}
func processGithubUserInfo(code string, role string, c *gin.Context) error {
2021-07-17 23:18:42 +00:00
token, err := oauth.OAuthProvider.GithubConfig.Exchange(oauth2.NoContext, code)
if err != nil {
2021-09-04 22:27:29 +00:00
return fmt.Errorf("invalid github exchange code: %s", err.Error())
2021-07-17 23:18:42 +00:00
}
client := http.Client{}
req, err := http.NewRequest("GET", constants.GithubUserInfoURL, nil)
if err != nil {
return fmt.Errorf("error creating github user info request: %s", err.Error())
}
req.Header = http.Header{
"Authorization": []string{fmt.Sprintf("token %s", token.AccessToken)},
}
2021-07-17 23:18:42 +00:00
response, err := client.Do(req)
if err != nil {
return err
}
defer response.Body.Close()
body, err := ioutil.ReadAll(response.Body)
if err != nil {
2021-09-04 22:27:29 +00:00
return fmt.Errorf("failed to read github response body: %s", err.Error())
2021-07-17 23:18:42 +00:00
}
userRawData := make(map[string]string)
json.Unmarshal(body, &userRawData)
existingUser, err := db.Mgr.GetUserByEmail(userRawData["email"])
name := strings.Split(userRawData["name"], " ")
firstName := ""
lastName := ""
if len(name) >= 1 && strings.TrimSpace(name[0]) != "" {
firstName = name[0]
}
if len(name) > 1 && strings.TrimSpace(name[1]) != "" {
lastName = name[0]
}
user := db.User{
FirstName: firstName,
LastName: lastName,
Image: userRawData["avatar_url"],
Email: userRawData["email"],
EmailVerifiedAt: time.Now().Unix(),
}
if err != nil {
// user not registered, register user and generate session token
user.SignupMethod = enum.Github.String()
user.Roles = role
2021-07-17 23:18:42 +00:00
} else {
// user exists in db, check if method was google
// if not append google to existing signup method and save it
2021-07-17 17:09:50 +00:00
2021-07-17 23:18:42 +00:00
signupMethod := existingUser.SignupMethod
if !strings.Contains(signupMethod, enum.Github.String()) {
signupMethod = signupMethod + "," + enum.Github.String()
}
user.SignupMethod = signupMethod
user.Password = existingUser.Password
if !utils.IsValidRole(strings.Split(existingUser.Roles, ","), role) {
return fmt.Errorf("invalid role")
}
user.Roles = existingUser.Roles
2021-07-17 23:18:42 +00:00
}
user, _ = db.Mgr.SaveUser(user)
user, _ = db.Mgr.GetUserByEmail(user.Email)
2021-08-07 08:41:26 +00:00
userIdStr := fmt.Sprintf("%v", user.ID)
refreshToken, _, _ := utils.CreateAuthToken(user, enum.RefreshToken, role)
accessToken, _, _ := utils.CreateAuthToken(user, enum.AccessToken, role)
utils.SetCookie(c, accessToken)
session.SetToken(userIdStr, refreshToken)
return nil
}
func processFacebookUserInfo(code string, role string, c *gin.Context) error {
2021-09-04 22:27:29 +00:00
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()
user.Roles = role
2021-09-04 22:27:29 +00:00
} 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
if !utils.IsValidRole(strings.Split(existingUser.Roles, ","), role) {
return fmt.Errorf("invalid role")
}
user.Roles = existingUser.Roles
2021-09-04 22:27:29 +00:00
}
user, _ = db.Mgr.SaveUser(user)
user, _ = db.Mgr.GetUserByEmail(user.Email)
userIdStr := fmt.Sprintf("%v", user.ID)
refreshToken, _, _ := utils.CreateAuthToken(user, enum.RefreshToken, role)
accessToken, _, _ := utils.CreateAuthToken(user, enum.AccessToken, role)
2021-09-04 22:27:29 +00:00
utils.SetCookie(c, accessToken)
session.SetToken(userIdStr, refreshToken)
return nil
}
func OAuthCallbackHandler() gin.HandlerFunc {
return func(c *gin.Context) {
provider := c.Param("oauth_provider")
state := c.Request.FormValue("state")
sessionState := session.GetToken(state)
if sessionState == "" {
c.JSON(400, gin.H{"error": "invalid oauth state"})
}
session.DeleteToken(sessionState)
// contains random token, redirect url, role
sessionSplit := strings.Split(state, "___")
// TODO validate redirect url
if len(sessionSplit) < 2 {
c.JSON(400, gin.H{"error": "invalid redirect url"})
return
}
role := sessionSplit[2]
redirectURL := sessionSplit[1]
2021-07-17 23:18:42 +00:00
var err error
code := c.Request.FormValue("code")
switch provider {
case enum.Google.String():
err = processGoogleUserInfo(code, role, c)
case enum.Github.String():
err = processGithubUserInfo(code, role, c)
2021-09-04 22:27:29 +00:00
case enum.Facebook.String():
err = processFacebookUserInfo(code, role, c)
default:
err = fmt.Errorf(`invalid oauth provider`)
2021-07-17 23:18:42 +00:00
}
2021-07-17 23:18:42 +00:00
if err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
c.Redirect(http.StatusTemporaryRedirect, redirectURL)
}
}