diff --git a/dashboard/src/App.tsx b/dashboard/src/App.tsx
index d64be95..c05f223 100644
--- a/dashboard/src/App.tsx
+++ b/dashboard/src/App.tsx
@@ -1,19 +1,12 @@
import * as React from 'react';
import { Text, ChakraProvider } from '@chakra-ui/react';
import { MdStar } from 'react-icons/md';
+import { BrowserRouter } from 'react-router-dom';
export default function Example() {
return (
-
- Authorizer Dashboard
-
+
);
}
diff --git a/dashboard/src/Router.tsx b/dashboard/src/Router.tsx
new file mode 100644
index 0000000..e69de29
diff --git a/dashboard/src/components/layouts/AuthLayout.tsx b/dashboard/src/components/layouts/AuthLayout.tsx
new file mode 100644
index 0000000..0ba7800
--- /dev/null
+++ b/dashboard/src/components/layouts/AuthLayout.tsx
@@ -0,0 +1,5 @@
+import React from 'react';
+
+export default function AuthLayout() {
+ return
Auth Layout
;
+}
diff --git a/dashboard/src/components/layouts/DefaultLayout.tsx b/dashboard/src/components/layouts/DefaultLayout.tsx
new file mode 100644
index 0000000..505b6c5
--- /dev/null
+++ b/dashboard/src/components/layouts/DefaultLayout.tsx
@@ -0,0 +1,5 @@
+import React from 'react';
+
+export default function DefaultLayout() {
+ return Default Layout
;
+}
diff --git a/dashboard/src/contexts/AuthContext.tsx b/dashboard/src/contexts/AuthContext.tsx
new file mode 100644
index 0000000..e69de29
diff --git a/server/__test__/admin_login_test.go b/server/__test__/admin_login_test.go
new file mode 100644
index 0000000..4f19b33
--- /dev/null
+++ b/server/__test__/admin_login_test.go
@@ -0,0 +1,27 @@
+package test
+
+import (
+ "testing"
+
+ "github.com/authorizerdev/authorizer/server/graph/model"
+ "github.com/authorizerdev/authorizer/server/resolvers"
+ "github.com/stretchr/testify/assert"
+)
+
+func aminLoginTests(s TestSetup, t *testing.T) {
+ t.Run(`should complete admin login`, func(t *testing.T) {
+ _, ctx := createContext(s)
+ _, err := resolvers.AdminLoginResolver(ctx, model.AdminLoginInput{
+ AdminSecret: "admin_test",
+ })
+
+ assert.NotNil(t, err)
+
+ res, err := resolvers.AdminLoginResolver(ctx, model.AdminLoginInput{
+ AdminSecret: "admin",
+ })
+
+ 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 160265c..9fe05ec 100644
--- a/server/__test__/resolvers_test.go
+++ b/server/__test__/resolvers_test.go
@@ -10,9 +10,9 @@ import (
func TestResolvers(t *testing.T) {
databases := map[string]string{
- enum.Sqlite.String(): "../../data.db",
- enum.Arangodb.String(): "http://root:root@localhost:8529",
- enum.Mongodb.String(): "mongodb://localhost:27017",
+ enum.Sqlite.String(): "../../data.db",
+ // enum.Arangodb.String(): "http://root:root@localhost:8529",
+ // enum.Mongodb.String(): "mongodb://localhost:27017",
}
for dbType, dbURL := range databases {
@@ -42,6 +42,7 @@ func TestResolvers(t *testing.T) {
usersTest(s, t)
deleteUserTest(s, t)
updateUserTest(s, t)
+ aminLoginTests(s, t)
})
}
}
diff --git a/server/go.mod b/server/go.mod
index 04b55a4..982e431 100644
--- a/server/go.mod
+++ b/server/go.mod
@@ -20,7 +20,6 @@ require (
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.1 // indirect
github.com/robertkrimen/otto v0.0.0-20211024170158-b87d35c0b86f
- github.com/stretchr/testify v1.7.0
github.com/ugorji/go v1.2.6 // indirect
github.com/vektah/gqlparser/v2 v2.2.0
go.mongodb.org/mongo-driver v1.8.1
diff --git a/server/graph/generated/generated.go b/server/graph/generated/generated.go
index 300307a..a781e54 100644
--- a/server/graph/generated/generated.go
+++ b/server/graph/generated/generated.go
@@ -43,6 +43,12 @@ type DirectiveRoot struct {
}
type ComplexityRoot struct {
+ AdminLoginResponse struct {
+ AccessToken func(childComplexity int) int
+ ExpiresAt func(childComplexity int) int
+ Message func(childComplexity int) int
+ }
+
AuthResponse struct {
AccessToken func(childComplexity int) int
ExpiresAt func(childComplexity int) int
@@ -66,6 +72,7 @@ type ComplexityRoot struct {
}
Mutation struct {
+ AdminLogin func(childComplexity int, params model.AdminLoginInput) int
DeleteUser func(childComplexity int, params model.DeleteUserInput) int
ForgotPassword func(childComplexity int, params model.ForgotPasswordInput) int
Login func(childComplexity int, params model.LoginInput) int
@@ -80,6 +87,7 @@ type ComplexityRoot struct {
}
Query struct {
+ AdminSession func(childComplexity int) int
Meta func(childComplexity int) int
Profile func(childComplexity int) int
Session func(childComplexity int, roles []string) int
@@ -134,6 +142,7 @@ type MutationResolver interface {
ResetPassword(ctx context.Context, params model.ResetPasswordInput) (*model.Response, error)
DeleteUser(ctx context.Context, params model.DeleteUserInput) (*model.Response, error)
UpdateUser(ctx context.Context, params model.UpdateUserInput) (*model.User, error)
+ AdminLogin(ctx context.Context, params model.AdminLoginInput) (*model.AdminLoginResponse, error)
}
type QueryResolver interface {
Meta(ctx context.Context) (*model.Meta, error)
@@ -141,6 +150,7 @@ type QueryResolver interface {
Profile(ctx context.Context) (*model.User, error)
Users(ctx context.Context) ([]*model.User, error)
VerificationRequests(ctx context.Context) ([]*model.VerificationRequest, error)
+ AdminSession(ctx context.Context) (*model.AdminLoginResponse, error)
}
type executableSchema struct {
@@ -158,6 +168,27 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
_ = ec
switch typeName + "." + field {
+ case "AdminLoginResponse.access_token":
+ if e.complexity.AdminLoginResponse.AccessToken == nil {
+ break
+ }
+
+ 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
+ }
+
+ return e.complexity.AdminLoginResponse.Message(childComplexity), true
+
case "AuthResponse.access_token":
if e.complexity.AuthResponse.AccessToken == nil {
break
@@ -249,6 +280,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Meta.Version(childComplexity), true
+ case "Mutation._admin_login":
+ if e.complexity.Mutation.AdminLogin == nil {
+ break
+ }
+
+ args, err := ec.field_Mutation__admin_login_args(context.TODO(), rawArgs)
+ if err != nil {
+ return 0, false
+ }
+
+ return e.complexity.Mutation.AdminLogin(childComplexity, args["params"].(model.AdminLoginInput)), true
+
case "Mutation._delete_user":
if e.complexity.Mutation.DeleteUser == nil {
break
@@ -376,6 +419,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Mutation.VerifyEmail(childComplexity, args["params"].(model.VerifyEmailInput)), true
+ case "Query._admin_session":
+ if e.complexity.Query.AdminSession == nil {
+ break
+ }
+
+ return e.complexity.Query.AdminSession(childComplexity), true
+
case "Query.meta":
if e.complexity.Query.Meta == nil {
break
@@ -719,6 +769,16 @@ type Response {
message: String!
}
+type AdminLoginResponse {
+ message: String!
+ access_token: String!
+ expires_at: Int64!
+}
+
+input AdminLoginInput {
+ admin_secret: String!
+}
+
input SignUpInput {
email: String!
given_name: String
@@ -810,6 +870,7 @@ type Mutation {
# admin only apis
_delete_user(params: DeleteUserInput!): Response!
_update_user(params: UpdateUserInput!): User!
+ _admin_login(params: AdminLoginInput!): AdminLoginResponse
}
type Query {
@@ -819,6 +880,7 @@ type Query {
# admin only apis
_users: [User!]!
_verification_requests: [VerificationRequest!]!
+ _admin_session: AdminLoginResponse
}
`, BuiltIn: false},
}
@@ -828,6 +890,21 @@ var parsedSchema = gqlparser.MustLoadSchema(sources...)
// region ***************************** args.gotpl *****************************
+func (ec *executionContext) field_Mutation__admin_login_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
+ var err error
+ args := map[string]interface{}{}
+ var arg0 model.AdminLoginInput
+ if tmp, ok := rawArgs["params"]; ok {
+ ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("params"))
+ arg0, err = ec.unmarshalNAdminLoginInput2githubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐAdminLoginInput(ctx, tmp)
+ if err != nil {
+ return nil, err
+ }
+ }
+ args["params"] = arg0
+ return args, nil
+}
+
func (ec *executionContext) field_Mutation__delete_user_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
var err error
args := map[string]interface{}{}
@@ -1046,6 +1123,111 @@ func (ec *executionContext) field___Type_fields_args(ctx context.Context, rawArg
// region **************************** field.gotpl *****************************
+func (ec *executionContext) _AdminLoginResponse_message(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.Message, 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.(string)
+ fc.Result = res
+ return ec.marshalNString2string(ctx, field.Selections, res)
+}
+
+func (ec *executionContext) _AdminLoginResponse_access_token(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.AccessToken, 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.(string)
+ fc.Result = res
+ 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 {
@@ -1947,6 +2129,45 @@ func (ec *executionContext) _Mutation__update_user(ctx context.Context, field gr
return ec.marshalNUser2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐUser(ctx, field.Selections, res)
}
+func (ec *executionContext) _Mutation__admin_login(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ ret = graphql.Null
+ }
+ }()
+ fc := &graphql.FieldContext{
+ Object: "Mutation",
+ Field: field,
+ Args: nil,
+ IsMethod: true,
+ IsResolver: true,
+ }
+
+ ctx = graphql.WithFieldContext(ctx, fc)
+ rawArgs := field.ArgumentMap(ec.Variables)
+ args, err := ec.field_Mutation__admin_login_args(ctx, rawArgs)
+ if err != nil {
+ ec.Error(ctx, err)
+ return graphql.Null
+ }
+ fc.Args = args
+ resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
+ ctx = rctx // use context from middleware stack in children
+ return ec.resolvers.Mutation().AdminLogin(rctx, args["params"].(model.AdminLoginInput))
+ })
+ if err != nil {
+ ec.Error(ctx, err)
+ return graphql.Null
+ }
+ if resTmp == nil {
+ return graphql.Null
+ }
+ res := resTmp.(*model.AdminLoginResponse)
+ fc.Result = res
+ return ec.marshalOAdminLoginResponse2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐAdminLoginResponse(ctx, field.Selections, res)
+}
+
func (ec *executionContext) _Query_meta(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
@@ -2126,6 +2347,38 @@ func (ec *executionContext) _Query__verification_requests(ctx context.Context, f
return ec.marshalNVerificationRequest2ᚕᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐVerificationRequestᚄ(ctx, field.Selections, res)
}
+func (ec *executionContext) _Query__admin_session(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ ret = graphql.Null
+ }
+ }()
+ fc := &graphql.FieldContext{
+ Object: "Query",
+ Field: field,
+ Args: nil,
+ IsMethod: true,
+ IsResolver: true,
+ }
+
+ 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 ec.resolvers.Query().AdminSession(rctx)
+ })
+ if err != nil {
+ ec.Error(ctx, err)
+ return graphql.Null
+ }
+ if resTmp == nil {
+ return graphql.Null
+ }
+ res := resTmp.(*model.AdminLoginResponse)
+ fc.Result = res
+ return ec.marshalOAdminLoginResponse2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐAdminLoginResponse(ctx, field.Selections, res)
+}
+
func (ec *executionContext) _Query___type(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
@@ -4140,6 +4393,29 @@ func (ec *executionContext) ___Type_ofType(ctx context.Context, field graphql.Co
// region **************************** input.gotpl *****************************
+func (ec *executionContext) unmarshalInputAdminLoginInput(ctx context.Context, obj interface{}) (model.AdminLoginInput, error) {
+ var it model.AdminLoginInput
+ asMap := map[string]interface{}{}
+ for k, v := range obj.(map[string]interface{}) {
+ asMap[k] = v
+ }
+
+ for k, v := range asMap {
+ switch k {
+ case "admin_secret":
+ var err error
+
+ ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("admin_secret"))
+ it.AdminSecret, err = ec.unmarshalNString2string(ctx, v)
+ if err != nil {
+ return it, err
+ }
+ }
+ }
+
+ return it, nil
+}
+
func (ec *executionContext) unmarshalInputDeleteUserInput(ctx context.Context, obj interface{}) (model.DeleteUserInput, error) {
var it model.DeleteUserInput
asMap := map[string]interface{}{}
@@ -4682,6 +4958,43 @@ func (ec *executionContext) unmarshalInputVerifyEmailInput(ctx context.Context,
// region **************************** object.gotpl ****************************
+var adminLoginResponseImplementors = []string{"AdminLoginResponse"}
+
+func (ec *executionContext) _AdminLoginResponse(ctx context.Context, sel ast.SelectionSet, obj *model.AdminLoginResponse) graphql.Marshaler {
+ fields := graphql.CollectFields(ec.OperationContext, sel, adminLoginResponseImplementors)
+
+ out := graphql.NewFieldSet(fields)
+ var invalids uint32
+ for i, field := range fields {
+ switch field.Name {
+ case "__typename":
+ out.Values[i] = graphql.MarshalString("AdminLoginResponse")
+ case "message":
+ out.Values[i] = ec._AdminLoginResponse_message(ctx, field, obj)
+ if out.Values[i] == graphql.Null {
+ invalids++
+ }
+ case "access_token":
+ out.Values[i] = ec._AdminLoginResponse_access_token(ctx, field, obj)
+ 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))
+ }
+ }
+ out.Dispatch()
+ if invalids > 0 {
+ return graphql.Null
+ }
+ return out
+}
+
var authResponseImplementors = []string{"AuthResponse"}
func (ec *executionContext) _AuthResponse(ctx context.Context, sel ast.SelectionSet, obj *model.AuthResponse) graphql.Marshaler {
@@ -4874,6 +5187,8 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet)
if out.Values[i] == graphql.Null {
invalids++
}
+ case "_admin_login":
+ out.Values[i] = ec._Mutation__admin_login(ctx, field)
default:
panic("unknown field " + strconv.Quote(field.Name))
}
@@ -4967,6 +5282,17 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr
}
return res
})
+ case "_admin_session":
+ field := field
+ out.Concurrently(i, func() (res graphql.Marshaler) {
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ }
+ }()
+ res = ec._Query__admin_session(ctx, field)
+ return res
+ })
case "__type":
out.Values[i] = ec._Query___type(ctx, field)
case "__schema":
@@ -5369,6 +5695,11 @@ func (ec *executionContext) ___Type(ctx context.Context, sel ast.SelectionSet, o
// region ***************************** type.gotpl *****************************
+func (ec *executionContext) unmarshalNAdminLoginInput2githubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐAdminLoginInput(ctx context.Context, v interface{}) (model.AdminLoginInput, error) {
+ res, err := ec.unmarshalInputAdminLoginInput(ctx, v)
+ return res, graphql.ErrorOnPath(ctx, err)
+}
+
func (ec *executionContext) marshalNAuthResponse2githubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐAuthResponse(ctx context.Context, sel ast.SelectionSet, v model.AuthResponse) graphql.Marshaler {
return ec._AuthResponse(ctx, sel, &v)
}
@@ -5423,6 +5754,21 @@ 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)
@@ -5911,6 +6257,13 @@ func (ec *executionContext) marshalN__TypeKind2string(ctx context.Context, sel a
return res
}
+func (ec *executionContext) marshalOAdminLoginResponse2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐAdminLoginResponse(ctx context.Context, sel ast.SelectionSet, v *model.AdminLoginResponse) graphql.Marshaler {
+ if v == nil {
+ return graphql.Null
+ }
+ return ec._AdminLoginResponse(ctx, sel, v)
+}
+
func (ec *executionContext) marshalOAuthResponse2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐAuthResponse(ctx context.Context, sel ast.SelectionSet, v *model.AuthResponse) graphql.Marshaler {
if v == nil {
return graphql.Null
diff --git a/server/graph/model/models_gen.go b/server/graph/model/models_gen.go
index ffd76ea..17b294b 100644
--- a/server/graph/model/models_gen.go
+++ b/server/graph/model/models_gen.go
@@ -2,6 +2,16 @@
package model
+type AdminLoginInput struct {
+ AdminSecret string `json:"admin_secret"`
+}
+
+type AdminLoginResponse struct {
+ Message string `json:"message"`
+ AccessToken string `json:"access_token"`
+ ExpiresAt int64 `json:"expires_at"`
+}
+
type AuthResponse struct {
Message string `json:"message"`
AccessToken *string `json:"access_token"`
diff --git a/server/graph/schema.graphqls b/server/graph/schema.graphqls
index 4353073..67fd957 100644
--- a/server/graph/schema.graphqls
+++ b/server/graph/schema.graphqls
@@ -62,6 +62,16 @@ type Response {
message: String!
}
+type AdminLoginResponse {
+ message: String!
+ access_token: String!
+ expires_at: Int64!
+}
+
+input AdminLoginInput {
+ admin_secret: String!
+}
+
input SignUpInput {
email: String!
given_name: String
@@ -153,6 +163,7 @@ type Mutation {
# admin only apis
_delete_user(params: DeleteUserInput!): Response!
_update_user(params: UpdateUserInput!): User!
+ _admin_login(params: AdminLoginInput!): AdminLoginResponse
}
type Query {
@@ -162,4 +173,5 @@ type Query {
# admin only apis
_users: [User!]!
_verification_requests: [VerificationRequest!]!
+ _admin_session: AdminLoginResponse
}
diff --git a/server/graph/schema.resolvers.go b/server/graph/schema.resolvers.go
index 9510800..24197d4 100644
--- a/server/graph/schema.resolvers.go
+++ b/server/graph/schema.resolvers.go
@@ -5,6 +5,7 @@ package graph
import (
"context"
+ "fmt"
"github.com/authorizerdev/authorizer/server/graph/generated"
"github.com/authorizerdev/authorizer/server/graph/model"
@@ -55,6 +56,10 @@ func (r *mutationResolver) UpdateUser(ctx context.Context, params model.UpdateUs
return resolvers.UpdateUser(ctx, params)
}
+func (r *mutationResolver) AdminLogin(ctx context.Context, params model.AdminLoginInput) (*model.AdminLoginResponse, error) {
+ return resolvers.AdminLoginResolver(ctx, params)
+}
+
func (r *queryResolver) Meta(ctx context.Context) (*model.Meta, error) {
return resolvers.Meta(ctx)
}
@@ -75,6 +80,10 @@ func (r *queryResolver) VerificationRequests(ctx context.Context) ([]*model.Veri
return resolvers.VerificationRequests(ctx)
}
+func (r *queryResolver) AdminSession(ctx context.Context) (*model.AdminLoginResponse, error) {
+ panic(fmt.Errorf("not implemented"))
+}
+
// Mutation returns generated.MutationResolver implementation.
func (r *Resolver) Mutation() generated.MutationResolver { return &mutationResolver{r} }
diff --git a/server/handlers/dashboard.go b/server/handlers/dashboard.go
index 88eb40b..9ad06ec 100644
--- a/server/handlers/dashboard.go
+++ b/server/handlers/dashboard.go
@@ -1,79 +1,22 @@
package handlers
import (
- "encoding/base64"
- "encoding/json"
- "log"
"net/http"
- "strings"
"github.com/authorizerdev/authorizer/server/constants"
- "github.com/authorizerdev/authorizer/server/utils"
"github.com/gin-gonic/gin"
)
func DashboardHandler() gin.HandlerFunc {
return func(c *gin.Context) {
- state := c.Query("state")
-
- 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.AUTHORIZER_URL
- stateObj.RedirectURL = constants.AUTHORIZER_URL + "/app"
-
- } else {
- decodedState, err := base64.StdEncoding.DecodeString(state)
- if err != nil {
- c.JSON(400, gin.H{"error": "[unable to decode state] invalid state"})
- return
- }
-
- err = json.Unmarshal(decodedState, &stateObj)
- if err != nil {
- c.JSON(400, gin.H{"error": "[unable to parse state] invalid state"})
- return
- }
- stateObj.AuthorizerURL = strings.TrimSuffix(stateObj.AuthorizerURL, "/")
- stateObj.RedirectURL = strings.TrimSuffix(stateObj.RedirectURL, "/")
-
- // validate redirect url with allowed origins
- if !utils.IsValidOrigin(stateObj.RedirectURL) {
- c.JSON(400, gin.H{"error": "invalid redirect url"})
- return
- }
-
- if stateObj.AuthorizerURL == "" {
- c.JSON(400, gin.H{"error": "invalid authorizer url"})
- return
- }
-
- // validate host and domain of authorizer url
- if strings.TrimSuffix(stateObj.AuthorizerURL, "/") != constants.AUTHORIZER_URL {
- c.JSON(400, gin.H{"error": "invalid host url"})
- return
- }
+ isOnboardingCompleted := false
+ if constants.ADMIN_SECRET != "" && constants.DATABASE_TYPE != "" && constants.DATABASE_URL != "" {
+ isOnboardingCompleted = true
}
- // debug the request state
- if pusher := c.Writer.Pusher(); pusher != nil {
- // use pusher.Push() to do server push
- if err := pusher.Push("/app/build/bundle.js", nil); err != nil {
- log.Printf("Failed to push: %v", err)
- }
- }
c.HTML(http.StatusOK, "dashboard.tmpl", gin.H{
- "data": map[string]string{
- "authorizerURL": stateObj.AuthorizerURL,
- "redirectURL": stateObj.RedirectURL,
- "organizationName": constants.ORGANIZATION_NAME,
- "organizationLogo": constants.ORGANIZATION_LOGO,
+ "data": map[string]interface{}{
+ "isOnboardingCompleted": isOnboardingCompleted,
},
})
}
diff --git a/server/resolvers/admin_login.go b/server/resolvers/admin_login.go
new file mode 100644
index 0000000..e61bae7
--- /dev/null
+++ b/server/resolvers/admin_login.go
@@ -0,0 +1,41 @@
+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.ADMIN_SECRET {
+ return nil, 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)
+
+ res = &model.AdminLoginResponse{
+ AccessToken: accessToken,
+ ExpiresAt: expiresAt,
+ Message: "admin logged in successfully",
+ }
+ return res, nil
+}
diff --git a/server/utils/auth_token.go b/server/utils/auth_token.go
index 23995de..7d4a3ad 100644
--- a/server/utils/auth_token.go
+++ b/server/utils/auth_token.go
@@ -124,3 +124,31 @@ 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.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.JWT_SECRET))
+ if err != nil {
+ return "", 0, err
+ }
+ return token, expiresAt, nil
+}
diff --git a/server/utils/cookie.go b/server/utils/cookie.go
index 38cf327..c9323f7 100644
--- a/server/utils/cookie.go
+++ b/server/utils/cookie.go
@@ -47,3 +47,19 @@ func DeleteCookie(gc *gin.Context) {
gc.SetCookie(constants.COOKIE_NAME, "", -1, "/", host, secure, httpOnly)
gc.SetCookie(constants.COOKIE_NAME+"-client", "", -1, "/", domain, secure, httpOnly)
}
+
+func SetAdminCookie(gc *gin.Context, token string) {
+ secure := true
+ httpOnly := true
+ host, _ := GetHostParts(constants.AUTHORIZER_URL)
+
+ gc.SetCookie("authorizer-admin", token, 3600, "/", host, secure, httpOnly)
+}
+
+func DeleteAdminCookie(gc *gin.Context, token string) {
+ secure := true
+ httpOnly := true
+ host, _ := GetHostParts(constants.AUTHORIZER_URL)
+
+ gc.SetCookie("authorizer-admin", "", -1, "/", host, secure, httpOnly)
+}