feat/role based access (#50)
* feat: add roles based access * feat: update roles env + todo * feat: add roles to update profile * feat: add role based oauth * feat: validate role for a given token
This commit is contained in:
@@ -46,16 +46,19 @@ func Login(ctx context.Context, params model.LoginInput) (*model.AuthResponse, e
|
||||
log.Println("Compare password error:", err)
|
||||
return res, fmt.Errorf(`invalid password`)
|
||||
}
|
||||
userIdStr := fmt.Sprintf("%v", user.ID)
|
||||
refreshToken, _, _ := utils.CreateAuthToken(utils.UserAuthInfo{
|
||||
ID: userIdStr,
|
||||
Email: user.Email,
|
||||
}, enum.RefreshToken)
|
||||
role := constants.DEFAULT_ROLE
|
||||
if params.Role != nil {
|
||||
// validate role
|
||||
if !utils.IsValidRole(strings.Split(user.Roles, ","), *params.Role) {
|
||||
return res, fmt.Errorf(`invalid role`)
|
||||
}
|
||||
|
||||
accessToken, expiresAt, _ := utils.CreateAuthToken(utils.UserAuthInfo{
|
||||
ID: userIdStr,
|
||||
Email: user.Email,
|
||||
}, enum.AccessToken)
|
||||
role = *params.Role
|
||||
}
|
||||
userIdStr := fmt.Sprintf("%v", user.ID)
|
||||
refreshToken, _, _ := utils.CreateAuthToken(user, enum.RefreshToken, role)
|
||||
|
||||
accessToken, expiresAt, _ := utils.CreateAuthToken(user, enum.AccessToken, role)
|
||||
|
||||
session.SetToken(userIdStr, refreshToken)
|
||||
|
||||
@@ -71,6 +74,7 @@ func Login(ctx context.Context, params model.LoginInput) (*model.AuthResponse, e
|
||||
LastName: &user.LastName,
|
||||
SignupMethod: user.SignupMethod,
|
||||
EmailVerifiedAt: &user.EmailVerifiedAt,
|
||||
Roles: strings.Split(user.Roles, ","),
|
||||
CreatedAt: &user.CreatedAt,
|
||||
UpdatedAt: &user.UpdatedAt,
|
||||
},
|
||||
|
@@ -2,6 +2,7 @@ package resolvers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||
"github.com/authorizerdev/authorizer/server/session"
|
||||
@@ -25,7 +26,8 @@ func Logout(ctx context.Context) (*model.Response, error) {
|
||||
return res, err
|
||||
}
|
||||
|
||||
session.DeleteToken(claim.ID)
|
||||
userId := fmt.Sprintf("%v", claim["id"])
|
||||
session.DeleteToken(userId)
|
||||
res = &model.Response{
|
||||
Message: "Logged out successfully",
|
||||
}
|
||||
|
@@ -3,6 +3,7 @@ package resolvers
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/db"
|
||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||
@@ -27,13 +28,15 @@ func Profile(ctx context.Context) (*model.User, error) {
|
||||
return res, err
|
||||
}
|
||||
|
||||
sessionToken := session.GetToken(claim.ID)
|
||||
userID := fmt.Sprintf("%v", claim["id"])
|
||||
email := fmt.Sprintf("%v", claim["email"])
|
||||
sessionToken := session.GetToken(userID)
|
||||
|
||||
if sessionToken == "" {
|
||||
return res, fmt.Errorf(`unauthorized`)
|
||||
}
|
||||
|
||||
user, err := db.Mgr.GetUserByEmail(claim.Email)
|
||||
user, err := db.Mgr.GetUserByEmail(email)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
@@ -48,6 +51,7 @@ func Profile(ctx context.Context) (*model.User, error) {
|
||||
LastName: &user.LastName,
|
||||
SignupMethod: user.SignupMethod,
|
||||
EmailVerifiedAt: &user.EmailVerifiedAt,
|
||||
Roles: strings.Split(user.Roles, ","),
|
||||
CreatedAt: &user.CreatedAt,
|
||||
UpdatedAt: &user.UpdatedAt,
|
||||
}
|
||||
|
@@ -35,6 +35,20 @@ func Signup(ctx context.Context, params model.SignUpInput) (*model.AuthResponse,
|
||||
return res, fmt.Errorf(`invalid email address`)
|
||||
}
|
||||
|
||||
inputRoles := []string{}
|
||||
|
||||
if params.Roles != nil && len(params.Roles) > 0 {
|
||||
// check if roles exists
|
||||
for _, item := range params.Roles {
|
||||
inputRoles = append(inputRoles, *item)
|
||||
}
|
||||
if !utils.IsValidRolesArray(inputRoles) {
|
||||
return res, fmt.Errorf(`invalid roles`)
|
||||
}
|
||||
} else {
|
||||
inputRoles = []string{constants.DEFAULT_ROLE}
|
||||
}
|
||||
|
||||
// find user with email
|
||||
existingUser, err := db.Mgr.GetUserByEmail(params.Email)
|
||||
if err != nil {
|
||||
@@ -49,6 +63,8 @@ func Signup(ctx context.Context, params model.SignUpInput) (*model.AuthResponse,
|
||||
Email: params.Email,
|
||||
}
|
||||
|
||||
user.Roles = strings.Join(inputRoles, ",")
|
||||
|
||||
password, _ := utils.HashPassword(params.Password)
|
||||
user.Password = password
|
||||
|
||||
@@ -77,6 +93,7 @@ func Signup(ctx context.Context, params model.SignUpInput) (*model.AuthResponse,
|
||||
LastName: &user.LastName,
|
||||
SignupMethod: user.SignupMethod,
|
||||
EmailVerifiedAt: &user.EmailVerifiedAt,
|
||||
Roles: strings.Split(user.Roles, ","),
|
||||
CreatedAt: &user.CreatedAt,
|
||||
UpdatedAt: &user.UpdatedAt,
|
||||
}
|
||||
@@ -106,15 +123,9 @@ func Signup(ctx context.Context, params model.SignUpInput) (*model.AuthResponse,
|
||||
}
|
||||
} else {
|
||||
|
||||
refreshToken, _, _ := utils.CreateAuthToken(utils.UserAuthInfo{
|
||||
ID: userIdStr,
|
||||
Email: user.Email,
|
||||
}, enum.RefreshToken)
|
||||
refreshToken, _, _ := utils.CreateAuthToken(user, enum.RefreshToken, constants.DEFAULT_ROLE)
|
||||
|
||||
accessToken, expiresAt, _ := utils.CreateAuthToken(utils.UserAuthInfo{
|
||||
ID: userIdStr,
|
||||
Email: user.Email,
|
||||
}, enum.AccessToken)
|
||||
accessToken, expiresAt, _ := utils.CreateAuthToken(user, enum.AccessToken, constants.DEFAULT_ROLE)
|
||||
|
||||
session.SetToken(userIdStr, refreshToken)
|
||||
res = &model.AuthResponse{
|
||||
|
@@ -3,8 +3,10 @@ package resolvers
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"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/graph/model"
|
||||
@@ -12,7 +14,7 @@ import (
|
||||
"github.com/authorizerdev/authorizer/server/utils"
|
||||
)
|
||||
|
||||
func Token(ctx context.Context) (*model.AuthResponse, error) {
|
||||
func Token(ctx context.Context, role *string) (*model.AuthResponse, error) {
|
||||
var res *model.AuthResponse
|
||||
|
||||
gc, err := utils.GinContextFromContext(ctx)
|
||||
@@ -25,13 +27,19 @@ func Token(ctx context.Context) (*model.AuthResponse, error) {
|
||||
}
|
||||
|
||||
claim, accessTokenErr := utils.VerifyAuthToken(token)
|
||||
expiresAt := claim.ExpiresAt
|
||||
expiresAt := claim["exp"].(int64)
|
||||
email := fmt.Sprintf("%v", claim["email"])
|
||||
|
||||
user, err := db.Mgr.GetUserByEmail(claim.Email)
|
||||
claimRole := fmt.Sprintf("%v", claim[constants.JWT_ROLE_CLAIM])
|
||||
user, err := db.Mgr.GetUserByEmail(email)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
if role != nil && role != &claimRole {
|
||||
return res, fmt.Errorf(`unauthorized. invalid role for a given token`)
|
||||
}
|
||||
|
||||
userIdStr := fmt.Sprintf("%v", user.ID)
|
||||
|
||||
sessionToken := session.GetToken(userIdStr)
|
||||
@@ -46,10 +54,7 @@ func Token(ctx context.Context) (*model.AuthResponse, error) {
|
||||
if accessTokenErr != nil || expiresTimeObj.Sub(currentTimeObj).Minutes() <= 5 {
|
||||
// if access token has expired and refresh/session token is valid
|
||||
// generate new accessToken
|
||||
token, expiresAt, _ = utils.CreateAuthToken(utils.UserAuthInfo{
|
||||
ID: userIdStr,
|
||||
Email: user.Email,
|
||||
}, enum.AccessToken)
|
||||
token, expiresAt, _ = utils.CreateAuthToken(user, enum.AccessToken, claimRole)
|
||||
}
|
||||
utils.SetCookie(gc, token)
|
||||
res = &model.AuthResponse{
|
||||
@@ -62,6 +67,7 @@ func Token(ctx context.Context) (*model.AuthResponse, error) {
|
||||
Image: &user.Image,
|
||||
FirstName: &user.FirstName,
|
||||
LastName: &user.LastName,
|
||||
Roles: strings.Split(user.Roles, ","),
|
||||
CreatedAt: &user.CreatedAt,
|
||||
UpdatedAt: &user.UpdatedAt,
|
||||
},
|
||||
|
@@ -32,7 +32,8 @@ func UpdateProfile(ctx context.Context, params model.UpdateProfileInput) (*model
|
||||
return res, err
|
||||
}
|
||||
|
||||
sessionToken := session.GetToken(claim.ID)
|
||||
id := fmt.Sprintf("%v", claim["id"])
|
||||
sessionToken := session.GetToken(id)
|
||||
|
||||
if sessionToken == "" {
|
||||
return res, fmt.Errorf(`unauthorized`)
|
||||
@@ -43,7 +44,8 @@ func UpdateProfile(ctx context.Context, params model.UpdateProfileInput) (*model
|
||||
return res, fmt.Errorf("please enter atleast one param to update")
|
||||
}
|
||||
|
||||
user, err := db.Mgr.GetUserByEmail(claim.Email)
|
||||
email := fmt.Sprintf("%v", claim["email"])
|
||||
user, err := db.Mgr.GetUserByEmail(email)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
@@ -120,9 +122,33 @@ func UpdateProfile(ctx context.Context, params model.UpdateProfileInput) (*model
|
||||
go func() {
|
||||
utils.SendVerificationMail(newEmail, token)
|
||||
}()
|
||||
|
||||
}
|
||||
|
||||
// TODO this idea needs to be verified otherwise every user can make themselves super admin
|
||||
// rolesToSave := ""
|
||||
// if params.Roles != nil && len(params.Roles) > 0 {
|
||||
// currentRoles := strings.Split(user.Roles, ",")
|
||||
// inputRoles := []string{}
|
||||
// for _, item := range params.Roles {
|
||||
// inputRoles = append(inputRoles, *item)
|
||||
// }
|
||||
|
||||
// if !utils.IsValidRolesArray(inputRoles) {
|
||||
// return res, fmt.Errorf("invalid list of roles")
|
||||
// }
|
||||
|
||||
// if !utils.IsStringArrayEqual(inputRoles, currentRoles) {
|
||||
// rolesToSave = strings.Join(inputRoles, ",")
|
||||
// }
|
||||
|
||||
// session.DeleteToken(fmt.Sprintf("%v", user.ID))
|
||||
// utils.DeleteCookie(gc)
|
||||
// }
|
||||
|
||||
// if rolesToSave != "" {
|
||||
// user.Roles = rolesToSave
|
||||
// }
|
||||
|
||||
_, err = db.Mgr.UpdateUser(user)
|
||||
if err != nil {
|
||||
log.Println("Error updating user:", err)
|
||||
|
@@ -3,8 +3,10 @@ package resolvers
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"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/graph/model"
|
||||
@@ -41,15 +43,9 @@ func VerifyEmail(ctx context.Context, params model.VerifyEmailInput) (*model.Aut
|
||||
db.Mgr.DeleteToken(claim.Email)
|
||||
|
||||
userIdStr := fmt.Sprintf("%v", user.ID)
|
||||
refreshToken, _, _ := utils.CreateAuthToken(utils.UserAuthInfo{
|
||||
ID: userIdStr,
|
||||
Email: user.Email,
|
||||
}, enum.RefreshToken)
|
||||
refreshToken, _, _ := utils.CreateAuthToken(user, enum.RefreshToken, constants.DEFAULT_ROLE)
|
||||
|
||||
accessToken, expiresAt, _ := utils.CreateAuthToken(utils.UserAuthInfo{
|
||||
ID: userIdStr,
|
||||
Email: user.Email,
|
||||
}, enum.AccessToken)
|
||||
accessToken, expiresAt, _ := utils.CreateAuthToken(user, enum.AccessToken, constants.DEFAULT_ROLE)
|
||||
|
||||
session.SetToken(userIdStr, refreshToken)
|
||||
|
||||
@@ -65,6 +61,7 @@ func VerifyEmail(ctx context.Context, params model.VerifyEmailInput) (*model.Aut
|
||||
LastName: &user.LastName,
|
||||
SignupMethod: user.SignupMethod,
|
||||
EmailVerifiedAt: &user.EmailVerifiedAt,
|
||||
Roles: strings.Split(user.Roles, ","),
|
||||
CreatedAt: &user.CreatedAt,
|
||||
UpdatedAt: &user.UpdatedAt,
|
||||
},
|
||||
|
Reference in New Issue
Block a user