diff --git a/server/__test__/admin_login_test.go b/server/__test__/admin_login_test.go index 4f19b33..fbf18ec 100644 --- a/server/__test__/admin_login_test.go +++ b/server/__test__/admin_login_test.go @@ -3,6 +3,7 @@ package test import ( "testing" + "github.com/authorizerdev/authorizer/server/constants" "github.com/authorizerdev/authorizer/server/graph/model" "github.com/authorizerdev/authorizer/server/resolvers" "github.com/stretchr/testify/assert" @@ -18,7 +19,7 @@ func aminLoginTests(s TestSetup, t *testing.T) { assert.NotNil(t, err) res, err := resolvers.AdminLoginResolver(ctx, model.AdminLoginInput{ - AdminSecret: "admin", + AdminSecret: constants.EnvData.ADMIN_SECRET, }) assert.Nil(t, err) diff --git a/server/__test__/admin_session_test.go b/server/__test__/admin_session_test.go new file mode 100644 index 0000000..06d0a45 --- /dev/null +++ b/server/__test__/admin_session_test.go @@ -0,0 +1,28 @@ +package test + +import ( + "log" + "testing" + + "github.com/authorizerdev/authorizer/server/constants" + "github.com/authorizerdev/authorizer/server/resolvers" + "github.com/authorizerdev/authorizer/server/utils" + "github.com/stretchr/testify/assert" +) + +func aminSessionTests(s TestSetup, t *testing.T) { + t.Run(`should get admin session`, func(t *testing.T) { + req, ctx := createContext(s) + _, err := resolvers.AdminSession(ctx) + log.Println("error:", err) + assert.NotNil(t, err) + + h, err := utils.HashPassword(constants.EnvData.ADMIN_SECRET) + assert.Nil(t, err) + req.Header.Add("Authorization", "Bearer "+h) + res, err := resolvers.AdminSession(ctx) + + assert.Nil(t, err) + assert.Greater(t, len(res.AccessToken), 0) + }) +} diff --git a/server/__test__/resolvers_test.go b/server/__test__/resolvers_test.go index 28db423..0d4fea6 100644 --- a/server/__test__/resolvers_test.go +++ b/server/__test__/resolvers_test.go @@ -43,6 +43,7 @@ func TestResolvers(t *testing.T) { deleteUserTest(s, t) updateUserTest(s, t) aminLoginTests(s, t) + aminSessionTests(s, t) }) } } diff --git a/server/graph/generated/generated.go b/server/graph/generated/generated.go index a781e54..59bea79 100644 --- a/server/graph/generated/generated.go +++ b/server/graph/generated/generated.go @@ -45,7 +45,6 @@ type DirectiveRoot struct { type ComplexityRoot struct { AdminLoginResponse struct { AccessToken func(childComplexity int) int - ExpiresAt func(childComplexity int) int Message func(childComplexity int) int } @@ -175,13 +174,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.AdminLoginResponse.AccessToken(childComplexity), true - case "AdminLoginResponse.expires_at": - if e.complexity.AdminLoginResponse.ExpiresAt == nil { - break - } - - return e.complexity.AdminLoginResponse.ExpiresAt(childComplexity), true - case "AdminLoginResponse.message": if e.complexity.AdminLoginResponse.Message == nil { break @@ -772,7 +764,6 @@ type Response { type AdminLoginResponse { message: String! access_token: String! - expires_at: Int64! } input AdminLoginInput { @@ -1193,41 +1184,6 @@ func (ec *executionContext) _AdminLoginResponse_access_token(ctx context.Context return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) _AdminLoginResponse_expires_at(ctx context.Context, field graphql.CollectedField, obj *model.AdminLoginResponse) (ret graphql.Marshaler) { - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - fc := &graphql.FieldContext{ - Object: "AdminLoginResponse", - Field: field, - Args: nil, - IsMethod: false, - IsResolver: false, - } - - ctx = graphql.WithFieldContext(ctx, fc) - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.ExpiresAt, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } - return graphql.Null - } - res := resTmp.(int64) - fc.Result = res - return ec.marshalNInt642int64(ctx, field.Selections, res) -} - func (ec *executionContext) _AuthResponse_message(ctx context.Context, field graphql.CollectedField, obj *model.AuthResponse) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { @@ -4979,11 +4935,6 @@ func (ec *executionContext) _AdminLoginResponse(ctx context.Context, sel ast.Sel if out.Values[i] == graphql.Null { invalids++ } - case "expires_at": - out.Values[i] = ec._AdminLoginResponse_expires_at(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ - } default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -5754,21 +5705,6 @@ func (ec *executionContext) marshalNID2string(ctx context.Context, sel ast.Selec return res } -func (ec *executionContext) unmarshalNInt642int64(ctx context.Context, v interface{}) (int64, error) { - res, err := graphql.UnmarshalInt64(v) - return res, graphql.ErrorOnPath(ctx, err) -} - -func (ec *executionContext) marshalNInt642int64(ctx context.Context, sel ast.SelectionSet, v int64) graphql.Marshaler { - res := graphql.MarshalInt64(v) - if res == graphql.Null { - if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { - ec.Errorf(ctx, "must not be null") - } - } - return res -} - func (ec *executionContext) unmarshalNLoginInput2githubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐLoginInput(ctx context.Context, v interface{}) (model.LoginInput, error) { res, err := ec.unmarshalInputLoginInput(ctx, v) return res, graphql.ErrorOnPath(ctx, err) diff --git a/server/graph/model/models_gen.go b/server/graph/model/models_gen.go index 17b294b..6152385 100644 --- a/server/graph/model/models_gen.go +++ b/server/graph/model/models_gen.go @@ -9,7 +9,6 @@ type AdminLoginInput struct { type AdminLoginResponse struct { Message string `json:"message"` AccessToken string `json:"access_token"` - ExpiresAt int64 `json:"expires_at"` } type AuthResponse struct { diff --git a/server/graph/schema.graphqls b/server/graph/schema.graphqls index 67fd957..1957219 100644 --- a/server/graph/schema.graphqls +++ b/server/graph/schema.graphqls @@ -65,7 +65,6 @@ type Response { type AdminLoginResponse { message: String! access_token: String! - expires_at: Int64! } input AdminLoginInput { diff --git a/server/graph/schema.resolvers.go b/server/graph/schema.resolvers.go index 24197d4..bcf9726 100644 --- a/server/graph/schema.resolvers.go +++ b/server/graph/schema.resolvers.go @@ -5,7 +5,6 @@ package graph import ( "context" - "fmt" "github.com/authorizerdev/authorizer/server/graph/generated" "github.com/authorizerdev/authorizer/server/graph/model" @@ -81,7 +80,7 @@ func (r *queryResolver) VerificationRequests(ctx context.Context) ([]*model.Veri } func (r *queryResolver) AdminSession(ctx context.Context) (*model.AdminLoginResponse, error) { - panic(fmt.Errorf("not implemented")) + return resolvers.AdminSession(ctx) } // Mutation returns generated.MutationResolver implementation. diff --git a/server/resolvers/admin_login.go b/server/resolvers/admin_login.go index bb5f44a..8c46ffc 100644 --- a/server/resolvers/admin_login.go +++ b/server/resolvers/admin_login.go @@ -3,38 +3,32 @@ package resolvers import ( "context" "fmt" - "log" - "time" "github.com/authorizerdev/authorizer/server/constants" - "github.com/authorizerdev/authorizer/server/enum" "github.com/authorizerdev/authorizer/server/graph/model" - "github.com/authorizerdev/authorizer/server/session" "github.com/authorizerdev/authorizer/server/utils" ) func AdminLoginResolver(ctx context.Context, params model.AdminLoginInput) (*model.AdminLoginResponse, error) { gc, err := utils.GinContextFromContext(ctx) var res *model.AdminLoginResponse + if err != nil { - log.Println("=> error:", err) return res, err } + if params.AdminSecret != constants.EnvData.ADMIN_SECRET { - return nil, fmt.Errorf(`invalid admin secret`) + return res, fmt.Errorf(`invalid admin secret`) } - refreshToken, _, _ := utils.CreateAdminAuthToken(enum.RefreshToken, gc) - accessToken, expiresAt, _ := utils.CreateAdminAuthToken(enum.AccessToken, gc) - - currentTime := time.Now().Unix() - tokenId := fmt.Sprintf("authorizer_admin_%d", currentTime) - session.SetToken(tokenId, accessToken, refreshToken) - utils.SetAdminCookie(gc, accessToken) + hashedKey, err := utils.HashPassword(constants.EnvData.ADMIN_SECRET) + if err != nil { + return res, err + } + utils.SetAdminCookie(gc, hashedKey) res = &model.AdminLoginResponse{ - AccessToken: accessToken, - ExpiresAt: expiresAt, + AccessToken: hashedKey, Message: "admin logged in successfully", } return res, nil diff --git a/server/resolvers/admin_session.go b/server/resolvers/admin_session.go new file mode 100644 index 0000000..fafe33b --- /dev/null +++ b/server/resolvers/admin_session.go @@ -0,0 +1,35 @@ +package resolvers + +import ( + "context" + "fmt" + + "github.com/authorizerdev/authorizer/server/constants" + "github.com/authorizerdev/authorizer/server/graph/model" + "github.com/authorizerdev/authorizer/server/utils" +) + +func AdminSession(ctx context.Context) (*model.AdminLoginResponse, error) { + gc, err := utils.GinContextFromContext(ctx) + var res *model.AdminLoginResponse + + if err != nil { + return res, err + } + + if !utils.IsSuperAdmin(gc) { + return res, fmt.Errorf("unauthorized") + } + + hashedKey, err := utils.HashPassword(constants.EnvData.ADMIN_SECRET) + if err != nil { + return res, err + } + utils.SetAdminCookie(gc, hashedKey) + + res = &model.AdminLoginResponse{ + AccessToken: hashedKey, + Message: "admin logged in successfully", + } + return res, nil +} diff --git a/server/utils/auth_token.go b/server/utils/auth_token.go index c3c6de1..f159cd0 100644 --- a/server/utils/auth_token.go +++ b/server/utils/auth_token.go @@ -14,6 +14,7 @@ import ( "github.com/gin-gonic/gin" "github.com/golang-jwt/jwt" "github.com/robertkrimen/otto" + "golang.org/x/crypto/bcrypt" ) func CreateAuthToken(user db.User, tokenType enum.TokenType, roles []string) (string, int64, error) { @@ -124,32 +125,8 @@ func VerifyAuthToken(token string) (map[string]interface{}, error) { return res, nil } -func CreateAdminAuthToken(tokenType enum.TokenType, c *gin.Context) (string, int64, error) { - t := jwt.New(jwt.GetSigningMethod(constants.EnvData.JWT_TYPE)) - expiryBound := time.Hour - if tokenType == enum.RefreshToken { - // expires in 1 year - expiryBound = time.Hour * 8760 - } - - expiresAt := time.Now().Add(expiryBound).Unix() - - customClaims := jwt.MapClaims{ - "exp": expiresAt, - "iat": time.Now().Unix(), - "user_agent": GetUserAgent(c.Request), - "ip": GetIP(c.Request), - "role": "authorizer_admin", - "created_at": time.Now().Unix(), - } - - t.Claims = customClaims - - token, err := t.SignedString([]byte(constants.EnvData.JWT_SECRET)) - if err != nil { - return "", 0, err - } - return token, expiresAt, nil +func CreateAdminAuthToken(tokenType enum.TokenType, c *gin.Context) (string, error) { + return HashPassword(constants.EnvData.ADMIN_SECRET) } func GetAdminAuthToken(gc *gin.Context) (string, error) { @@ -162,6 +139,11 @@ func GetAdminAuthToken(gc *gin.Context) (string, error) { } token = strings.TrimPrefix(auth, "Bearer ") + + err = bcrypt.CompareHashAndPassword([]byte(token), []byte(constants.EnvData.ADMIN_SECRET)) + if err != nil { + return "", fmt.Errorf(`unauthorized`) + } } return token, nil }