Feat/dashboard (#105)
This commit is contained in:
@@ -7,15 +7,19 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/envstore"
|
||||
"github.com/authorizerdev/authorizer/server/utils"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// State is the struct that holds authorizer url and redirect url
|
||||
// They are provided via query string in the request
|
||||
type State struct {
|
||||
AuthorizerURL string `json:"authorizerURL"`
|
||||
RedirectURL string `json:"redirectURL"`
|
||||
}
|
||||
|
||||
// AppHandler is the handler for the /app route
|
||||
func AppHandler() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
state := c.Query("state")
|
||||
@@ -23,14 +27,8 @@ func AppHandler() gin.HandlerFunc {
|
||||
var stateObj State
|
||||
|
||||
if state == "" {
|
||||
// cookie, err := utils.GetAuthToken(c)
|
||||
// if err != nil {
|
||||
// c.JSON(400, gin.H{"error": "invalid state"})
|
||||
// return
|
||||
// }
|
||||
|
||||
stateObj.AuthorizerURL = constants.EnvData.AUTHORIZER_URL
|
||||
stateObj.RedirectURL = constants.EnvData.AUTHORIZER_URL + "/app"
|
||||
stateObj.AuthorizerURL = envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyAuthorizerURL).(string)
|
||||
stateObj.RedirectURL = stateObj.AuthorizerURL + "/app"
|
||||
|
||||
} else {
|
||||
decodedState, err := utils.DecryptB64(state)
|
||||
@@ -59,7 +57,7 @@ func AppHandler() gin.HandlerFunc {
|
||||
}
|
||||
|
||||
// validate host and domain of authorizer url
|
||||
if strings.TrimSuffix(stateObj.AuthorizerURL, "/") != constants.EnvData.AUTHORIZER_URL {
|
||||
if strings.TrimSuffix(stateObj.AuthorizerURL, "/") != envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyAuthorizerURL).(string) {
|
||||
c.JSON(400, gin.H{"error": "invalid host url"})
|
||||
return
|
||||
}
|
||||
@@ -76,8 +74,8 @@ func AppHandler() gin.HandlerFunc {
|
||||
"data": map[string]string{
|
||||
"authorizerURL": stateObj.AuthorizerURL,
|
||||
"redirectURL": stateObj.RedirectURL,
|
||||
"organizationName": constants.EnvData.ORGANIZATION_NAME,
|
||||
"organizationLogo": constants.EnvData.ORGANIZATION_LOGO,
|
||||
"organizationName": envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyOrganizationName).(string),
|
||||
"organizationLogo": envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyOrganizationLogo).(string),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
@@ -4,14 +4,16 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/envstore"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// DashboardHandler is the handler for the /dashboard route
|
||||
func DashboardHandler() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
isOnboardingCompleted := false
|
||||
|
||||
if constants.EnvData.ADMIN_SECRET != "" {
|
||||
if envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyAdminSecret) != nil && envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyAdminSecret).(string) != "" {
|
||||
isOnboardingCompleted = true
|
||||
}
|
||||
|
||||
|
@@ -7,7 +7,7 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// Defining the Graphql handler
|
||||
// GraphqlHandler is the main handler that handels all the graphql requests
|
||||
func GraphqlHandler() gin.HandlerFunc {
|
||||
// NewExecutableSchema and Config are in the generated.go file
|
||||
// Resolver is in the resolver.go file
|
||||
|
@@ -12,7 +12,7 @@ import (
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/db"
|
||||
"github.com/authorizerdev/authorizer/server/enum"
|
||||
"github.com/authorizerdev/authorizer/server/envstore"
|
||||
"github.com/authorizerdev/authorizer/server/oauth"
|
||||
"github.com/authorizerdev/authorizer/server/session"
|
||||
"github.com/authorizerdev/authorizer/server/utils"
|
||||
@@ -21,6 +21,131 @@ import (
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
// OAuthCallbackHandler handles the OAuth callback for various oauth providers
|
||||
func OAuthCallbackHandler() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
provider := c.Param("oauth_provider")
|
||||
state := c.Request.FormValue("state")
|
||||
|
||||
sessionState := session.GetSocailLoginState(state)
|
||||
if sessionState == "" {
|
||||
c.JSON(400, gin.H{"error": "invalid oauth state"})
|
||||
}
|
||||
session.RemoveSocialLoginState(state)
|
||||
// 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
|
||||
}
|
||||
|
||||
inputRoles := strings.Split(sessionSplit[2], ",")
|
||||
redirectURL := sessionSplit[1]
|
||||
|
||||
var err error
|
||||
user := db.User{}
|
||||
code := c.Request.FormValue("code")
|
||||
switch provider {
|
||||
case constants.SignupMethodGoogle:
|
||||
user, err = processGoogleUserInfo(code)
|
||||
case constants.SignupMethodGithub:
|
||||
user, err = processGithubUserInfo(code)
|
||||
case constants.SignupMethodFacebook:
|
||||
user, err = processFacebookUserInfo(code)
|
||||
default:
|
||||
err = fmt.Errorf(`invalid oauth provider`)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
c.JSON(400, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
existingUser, err := db.Mgr.GetUserByEmail(user.Email)
|
||||
|
||||
if err != nil {
|
||||
// user not registered, register user and generate session token
|
||||
user.SignupMethods = provider
|
||||
// make sure inputRoles don't include protected roles
|
||||
hasProtectedRole := false
|
||||
for _, ir := range inputRoles {
|
||||
if utils.StringSliceContains(envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyProtectedRoles).([]string), ir) {
|
||||
hasProtectedRole = true
|
||||
}
|
||||
}
|
||||
|
||||
if hasProtectedRole {
|
||||
c.JSON(400, gin.H{"error": "invalid role"})
|
||||
return
|
||||
}
|
||||
|
||||
user.Roles = strings.Join(inputRoles, ",")
|
||||
now := time.Now().Unix()
|
||||
user.EmailVerifiedAt = &now
|
||||
user, _ = db.Mgr.AddUser(user)
|
||||
} else {
|
||||
// user exists in db, check if method was google
|
||||
// if not append google to existing signup method and save it
|
||||
|
||||
signupMethod := existingUser.SignupMethods
|
||||
if !strings.Contains(signupMethod, provider) {
|
||||
signupMethod = signupMethod + "," + provider
|
||||
}
|
||||
user.SignupMethods = signupMethod
|
||||
user.Password = existingUser.Password
|
||||
|
||||
// There multiple scenarios with roles here in social login
|
||||
// 1. user has access to protected roles + roles and trying to login
|
||||
// 2. user has not signed up for one of the available role but trying to signup.
|
||||
// Need to modify roles in this case
|
||||
|
||||
// find the unassigned roles
|
||||
existingRoles := strings.Split(existingUser.Roles, ",")
|
||||
unasignedRoles := []string{}
|
||||
for _, ir := range inputRoles {
|
||||
if !utils.StringSliceContains(existingRoles, ir) {
|
||||
unasignedRoles = append(unasignedRoles, ir)
|
||||
}
|
||||
}
|
||||
|
||||
if len(unasignedRoles) > 0 {
|
||||
// check if it contains protected unassigned role
|
||||
hasProtectedRole := false
|
||||
for _, ur := range unasignedRoles {
|
||||
if utils.StringSliceContains(envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyProtectedRoles).([]string), ur) {
|
||||
hasProtectedRole = true
|
||||
}
|
||||
}
|
||||
|
||||
if hasProtectedRole {
|
||||
c.JSON(400, gin.H{"error": "invalid role"})
|
||||
return
|
||||
} else {
|
||||
user.Roles = existingUser.Roles + "," + strings.Join(unasignedRoles, ",")
|
||||
}
|
||||
} else {
|
||||
user.Roles = existingUser.Roles
|
||||
}
|
||||
user.Key = existingUser.Key
|
||||
user.ID = existingUser.ID
|
||||
user, err = db.Mgr.UpdateUser(user)
|
||||
}
|
||||
|
||||
user, _ = db.Mgr.GetUserByEmail(user.Email)
|
||||
userIdStr := fmt.Sprintf("%v", user.ID)
|
||||
refreshToken, _, _ := utils.CreateAuthToken(user, constants.TokenTypeRefreshToken, inputRoles)
|
||||
|
||||
accessToken, _, _ := utils.CreateAuthToken(user, constants.TokenTypeAccessToken, inputRoles)
|
||||
utils.SetCookie(c, accessToken)
|
||||
session.SetUserSession(userIdStr, accessToken, refreshToken)
|
||||
utils.SaveSessionInDB(user.ID, c)
|
||||
|
||||
c.Redirect(http.StatusTemporaryRedirect, redirectURL)
|
||||
}
|
||||
}
|
||||
|
||||
func processGoogleUserInfo(code string) (db.User, error) {
|
||||
user := db.User{}
|
||||
ctx := context.Background()
|
||||
@@ -145,127 +270,3 @@ func processFacebookUserInfo(code string) (db.User, error) {
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func OAuthCallbackHandler() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
provider := c.Param("oauth_provider")
|
||||
state := c.Request.FormValue("state")
|
||||
|
||||
sessionState := session.GetSocailLoginState(state)
|
||||
if sessionState == "" {
|
||||
c.JSON(400, gin.H{"error": "invalid oauth state"})
|
||||
}
|
||||
session.RemoveSocialLoginState(state)
|
||||
// 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
|
||||
}
|
||||
|
||||
inputRoles := strings.Split(sessionSplit[2], ",")
|
||||
redirectURL := sessionSplit[1]
|
||||
|
||||
var err error
|
||||
user := db.User{}
|
||||
code := c.Request.FormValue("code")
|
||||
switch provider {
|
||||
case enum.Google.String():
|
||||
user, err = processGoogleUserInfo(code)
|
||||
case enum.Github.String():
|
||||
user, err = processGithubUserInfo(code)
|
||||
case enum.Facebook.String():
|
||||
user, err = processFacebookUserInfo(code)
|
||||
default:
|
||||
err = fmt.Errorf(`invalid oauth provider`)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
c.JSON(400, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
existingUser, err := db.Mgr.GetUserByEmail(user.Email)
|
||||
|
||||
if err != nil {
|
||||
// user not registered, register user and generate session token
|
||||
user.SignupMethods = provider
|
||||
// make sure inputRoles don't include protected roles
|
||||
hasProtectedRole := false
|
||||
for _, ir := range inputRoles {
|
||||
if utils.StringSliceContains(constants.EnvData.PROTECTED_ROLES, ir) {
|
||||
hasProtectedRole = true
|
||||
}
|
||||
}
|
||||
|
||||
if hasProtectedRole {
|
||||
c.JSON(400, gin.H{"error": "invalid role"})
|
||||
return
|
||||
}
|
||||
|
||||
user.Roles = strings.Join(inputRoles, ",")
|
||||
now := time.Now().Unix()
|
||||
user.EmailVerifiedAt = &now
|
||||
user, _ = db.Mgr.AddUser(user)
|
||||
} else {
|
||||
// user exists in db, check if method was google
|
||||
// if not append google to existing signup method and save it
|
||||
|
||||
signupMethod := existingUser.SignupMethods
|
||||
if !strings.Contains(signupMethod, provider) {
|
||||
signupMethod = signupMethod + "," + provider
|
||||
}
|
||||
user.SignupMethods = signupMethod
|
||||
user.Password = existingUser.Password
|
||||
|
||||
// There multiple scenarios with roles here in social login
|
||||
// 1. user has access to protected roles + roles and trying to login
|
||||
// 2. user has not signed up for one of the available role but trying to signup.
|
||||
// Need to modify roles in this case
|
||||
|
||||
// find the unassigned roles
|
||||
existingRoles := strings.Split(existingUser.Roles, ",")
|
||||
unasignedRoles := []string{}
|
||||
for _, ir := range inputRoles {
|
||||
if !utils.StringSliceContains(existingRoles, ir) {
|
||||
unasignedRoles = append(unasignedRoles, ir)
|
||||
}
|
||||
}
|
||||
|
||||
if len(unasignedRoles) > 0 {
|
||||
// check if it contains protected unassigned role
|
||||
hasProtectedRole := false
|
||||
for _, ur := range unasignedRoles {
|
||||
if utils.StringSliceContains(constants.EnvData.PROTECTED_ROLES, ur) {
|
||||
hasProtectedRole = true
|
||||
}
|
||||
}
|
||||
|
||||
if hasProtectedRole {
|
||||
c.JSON(400, gin.H{"error": "invalid role"})
|
||||
return
|
||||
} else {
|
||||
user.Roles = existingUser.Roles + "," + strings.Join(unasignedRoles, ",")
|
||||
}
|
||||
} else {
|
||||
user.Roles = existingUser.Roles
|
||||
}
|
||||
user.Key = existingUser.Key
|
||||
user.ID = existingUser.ID
|
||||
user, err = db.Mgr.UpdateUser(user)
|
||||
}
|
||||
|
||||
user, _ = db.Mgr.GetUserByEmail(user.Email)
|
||||
userIdStr := fmt.Sprintf("%v", user.ID)
|
||||
refreshToken, _, _ := utils.CreateAuthToken(user, enum.RefreshToken, inputRoles)
|
||||
|
||||
accessToken, _, _ := utils.CreateAuthToken(user, enum.AccessToken, inputRoles)
|
||||
utils.SetCookie(c, accessToken)
|
||||
session.SetToken(userIdStr, accessToken, refreshToken)
|
||||
utils.CreateSession(user.ID, c)
|
||||
|
||||
c.Redirect(http.StatusTemporaryRedirect, redirectURL)
|
||||
}
|
||||
}
|
||||
|
@@ -5,7 +5,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/enum"
|
||||
"github.com/authorizerdev/authorizer/server/envstore"
|
||||
"github.com/authorizerdev/authorizer/server/oauth"
|
||||
"github.com/authorizerdev/authorizer/server/session"
|
||||
"github.com/authorizerdev/authorizer/server/utils"
|
||||
@@ -13,8 +13,7 @@ import (
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// set host in the oauth state that is useful for redirecting
|
||||
|
||||
// OAuthLoginHandler set host in the oauth state that is useful for redirecting to oauth_callback
|
||||
func OAuthLoginHandler() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
// TODO validate redirect URL
|
||||
@@ -34,14 +33,14 @@ func OAuthLoginHandler() gin.HandlerFunc {
|
||||
|
||||
// use protected roles verification for admin login only.
|
||||
// though if not associated with user, it will be rejected from oauth_callback
|
||||
if !utils.IsValidRoles(append([]string{}, append(constants.EnvData.ROLES, constants.EnvData.PROTECTED_ROLES...)...), rolesSplit) {
|
||||
if !utils.IsValidRoles(append([]string{}, append(envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyRoles).([]string), envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyProtectedRoles).([]string)...)...), rolesSplit) {
|
||||
c.JSON(400, gin.H{
|
||||
"error": "invalid role",
|
||||
})
|
||||
return
|
||||
}
|
||||
} else {
|
||||
roles = strings.Join(constants.EnvData.DEFAULT_ROLES, ",")
|
||||
roles = strings.Join(envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyDefaultRoles).([]string), ",")
|
||||
}
|
||||
|
||||
uuid := uuid.New()
|
||||
@@ -50,32 +49,32 @@ func OAuthLoginHandler() gin.HandlerFunc {
|
||||
provider := c.Param("oauth_provider")
|
||||
isProviderConfigured := true
|
||||
switch provider {
|
||||
case enum.Google.String():
|
||||
case constants.SignupMethodGoogle:
|
||||
if oauth.OAuthProviders.GoogleConfig == nil {
|
||||
isProviderConfigured = false
|
||||
break
|
||||
}
|
||||
session.SetSocailLoginState(oauthStateString, enum.Google.String())
|
||||
session.SetSocailLoginState(oauthStateString, constants.SignupMethodGoogle)
|
||||
// during the init of OAuthProvider authorizer url might be empty
|
||||
oauth.OAuthProviders.GoogleConfig.RedirectURL = constants.EnvData.AUTHORIZER_URL + "/oauth_callback/google"
|
||||
oauth.OAuthProviders.GoogleConfig.RedirectURL = envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyAuthorizerURL).(string) + "/oauth_callback/google"
|
||||
url := oauth.OAuthProviders.GoogleConfig.AuthCodeURL(oauthStateString)
|
||||
c.Redirect(http.StatusTemporaryRedirect, url)
|
||||
case enum.Github.String():
|
||||
case constants.SignupMethodGithub:
|
||||
if oauth.OAuthProviders.GithubConfig == nil {
|
||||
isProviderConfigured = false
|
||||
break
|
||||
}
|
||||
session.SetSocailLoginState(oauthStateString, enum.Github.String())
|
||||
oauth.OAuthProviders.GithubConfig.RedirectURL = constants.EnvData.AUTHORIZER_URL + "/oauth_callback/github"
|
||||
session.SetSocailLoginState(oauthStateString, constants.SignupMethodGithub)
|
||||
oauth.OAuthProviders.GithubConfig.RedirectURL = envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyAuthorizerURL).(string) + "/oauth_callback/github"
|
||||
url := oauth.OAuthProviders.GithubConfig.AuthCodeURL(oauthStateString)
|
||||
c.Redirect(http.StatusTemporaryRedirect, url)
|
||||
case enum.Facebook.String():
|
||||
case constants.SignupMethodFacebook:
|
||||
if oauth.OAuthProviders.FacebookConfig == nil {
|
||||
isProviderConfigured = false
|
||||
break
|
||||
}
|
||||
session.SetSocailLoginState(oauthStateString, enum.Facebook.String())
|
||||
oauth.OAuthProviders.FacebookConfig.RedirectURL = constants.EnvData.AUTHORIZER_URL + "/oauth_callback/facebook"
|
||||
session.SetSocailLoginState(oauthStateString, constants.SignupMethodFacebook)
|
||||
oauth.OAuthProviders.FacebookConfig.RedirectURL = envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyAuthorizerURL).(string) + "/oauth_callback/facebook"
|
||||
url := oauth.OAuthProviders.FacebookConfig.AuthCodeURL(oauthStateString)
|
||||
c.Redirect(http.StatusTemporaryRedirect, url)
|
||||
default:
|
||||
|
@@ -5,6 +5,7 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// PlaygroundHandler is the handler for the /playground route
|
||||
func PlaygroundHandler() gin.HandlerFunc {
|
||||
h := playground.Handler("GraphQL", "/graphql")
|
||||
|
||||
|
14
server/handlers/root.go
Normal file
14
server/handlers/root.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// RootHandler is the handler for / root route.
|
||||
func RootHandler() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
c.Redirect(http.StatusTemporaryRedirect, "/dashboard")
|
||||
}
|
||||
}
|
@@ -5,13 +5,15 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/db"
|
||||
"github.com/authorizerdev/authorizer/server/enum"
|
||||
"github.com/authorizerdev/authorizer/server/session"
|
||||
"github.com/authorizerdev/authorizer/server/utils"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// VerifyEmailHandler handles the verify email route.
|
||||
// It verifies email based on JWT token in query string
|
||||
func VerifyEmailHandler() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
errorRes := gin.H{
|
||||
@@ -54,12 +56,12 @@ func VerifyEmailHandler() gin.HandlerFunc {
|
||||
db.Mgr.DeleteVerificationRequest(verificationRequest)
|
||||
|
||||
roles := strings.Split(user.Roles, ",")
|
||||
refreshToken, _, _ := utils.CreateAuthToken(user, enum.RefreshToken, roles)
|
||||
refreshToken, _, _ := utils.CreateAuthToken(user, constants.TokenTypeRefreshToken, roles)
|
||||
|
||||
accessToken, _, _ := utils.CreateAuthToken(user, enum.AccessToken, roles)
|
||||
accessToken, _, _ := utils.CreateAuthToken(user, constants.TokenTypeAccessToken, roles)
|
||||
|
||||
session.SetToken(user.ID, accessToken, refreshToken)
|
||||
utils.CreateSession(user.ID, c)
|
||||
session.SetUserSession(user.ID, accessToken, refreshToken)
|
||||
utils.SaveSessionInDB(user.ID, c)
|
||||
utils.SetCookie(c, accessToken)
|
||||
c.Redirect(http.StatusTemporaryRedirect, claim.RedirectURL)
|
||||
}
|
||||
|
Reference in New Issue
Block a user