Merge pull request #87 from authorizerdev/feat/use-opend-id-standard-claims

feat/use opend id standard claims
This commit is contained in:
Lakhan Samani 2021-12-24 08:49:01 +05:30 committed by GitHub
commit e5761f1e42
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
79 changed files with 2685 additions and 1197 deletions

View File

@ -2,8 +2,11 @@ ENV=production
DATABASE_URL=data.db
DATABASE_TYPE=sqlite
ADMIN_SECRET=admin
DISABLE_EMAIL_VERIFICATION=true
JWT_SECRET=random_string
SENDER_EMAIL=username
SENDER_PASSWORD=password
SMTP_HOST=smtp.mailtrap.io
SMTP_PORT=2525
JWT_TYPE=HS256
ROLES=user
DEFAULT_ROLES=user

View File

@ -6,4 +6,4 @@ cmd:
clean:
rm -rf build
test:
cd server && go clean --testcache && go test -v ./...
cd server && go clean --testcache && go test -v ./__test__

10
TODO.md
View File

@ -1,5 +1,15 @@
# Task List
## Open ID compatible claims and schema
- [x] Rename `schema.graphqls` and re generate schema
- [x] Rename to snake case [files + schema]
- [x] Refactor db models
- [x] Check extra data in oauth profile and save accordingly
- [x] Update all the resolver to make them compatible with schema changes
- [x] Update JWT claims
- [x] Write integration tests for all resolvers
## Feature Multiple sessions
- Multiple sessions for users to login use hMset from redis for this

View File

@ -0,0 +1,27 @@
package test
import (
"net/http"
"testing"
"github.com/stretchr/testify/assert"
)
func TestCors(t *testing.T) {
allowedOrigin := "http://localhost:8080" // The allowed origin that you want to check
notAllowedOrigin := "http://myapp.com"
s := testSetup()
defer s.Server.Close()
client := &http.Client{}
req, _ := createContext(s)
req.Header.Add("Origin", allowedOrigin)
res, _ := client.Do(req)
// You should get your origin (or a * depending on your config) if the
// passed origin is allowed.
o := res.Header.Get("Access-Control-Allow-Origin")
assert.NotEqual(t, o, notAllowedOrigin, "Origins should not match")
assert.Equal(t, o, allowedOrigin, "Origins do match")
}

View File

@ -0,0 +1,34 @@
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"
)
func deleteUserTest(s TestSetup, t *testing.T) {
t.Run(`should delete users with admin secret only`, func(t *testing.T) {
req, ctx := createContext(s)
email := "delete_user." + s.TestInfo.Email
resolvers.Signup(ctx, model.SignUpInput{
Email: email,
Password: s.TestInfo.Password,
ConfirmPassword: s.TestInfo.Password,
})
_, err := resolvers.DeleteUser(ctx, model.DeleteUserInput{
Email: email,
})
assert.NotNil(t, err, "unauthorized")
req.Header.Add("x-authorizer-admin-secret", constants.ADMIN_SECRET)
_, err = resolvers.DeleteUser(ctx, model.DeleteUserInput{
Email: email,
})
assert.Nil(t, err)
cleanData(email)
})
}

View File

@ -1,23 +1,19 @@
package env
package test
import (
"testing"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/enum"
"github.com/stretchr/testify/assert"
)
func TestEnvs(t *testing.T) {
constants.ENV_PATH = "../../.env.sample"
InitEnv()
assert.Equal(t, constants.ADMIN_SECRET, "admin")
assert.Equal(t, constants.ENV, "production")
assert.Equal(t, constants.DATABASE_URL, "data.db")
assert.Equal(t, constants.DATABASE_TYPE, enum.Sqlite.String())
assert.True(t, constants.DISABLE_EMAIL_VERIFICATION)
assert.True(t, constants.DISABLE_MAGIC_LOGIN)
assert.False(t, constants.DISABLE_EMAIL_VERIFICATION)
assert.False(t, constants.DISABLE_MAGIC_LINK_LOGIN)
assert.False(t, constants.DISABLE_BASIC_AUTHENTICATION)
assert.Equal(t, constants.JWT_TYPE, "HS256")
assert.Equal(t, constants.JWT_SECRET, "random_string")

View File

@ -0,0 +1,35 @@
package test
import (
"testing"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/enum"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/resolvers"
"github.com/stretchr/testify/assert"
)
func forgotPasswordTest(s TestSetup, t *testing.T) {
t.Run(`should run forgot password`, func(t *testing.T) {
_, ctx := createContext(s)
email := "forgot_password." + s.TestInfo.Email
_, err := resolvers.Signup(ctx, model.SignUpInput{
Email: email,
Password: s.TestInfo.Password,
ConfirmPassword: s.TestInfo.Password,
})
_, err = resolvers.ForgotPassword(ctx, model.ForgotPasswordInput{
Email: email,
})
assert.Nil(t, err, "no errors for forgot password")
verificationRequest, err := db.Mgr.GetVerificationByEmail(email, enum.ForgotPassword.String())
assert.Nil(t, err)
assert.Equal(t, verificationRequest.Identifier, enum.ForgotPassword.String())
cleanData(email)
})
}

View File

@ -0,0 +1,58 @@
package test
import (
"testing"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/enum"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/resolvers"
"github.com/stretchr/testify/assert"
)
func loginTests(s TestSetup, t *testing.T) {
t.Run(`should login`, func(t *testing.T) {
_, ctx := createContext(s)
email := "login." + s.TestInfo.Email
_, err := resolvers.Signup(ctx, model.SignUpInput{
Email: email,
Password: s.TestInfo.Password,
ConfirmPassword: s.TestInfo.Password,
})
_, err = resolvers.Login(ctx, model.LoginInput{
Email: email,
Password: s.TestInfo.Password,
})
assert.NotNil(t, err, "should fail because email is not verified")
verificationRequest, err := db.Mgr.GetVerificationByEmail(email, enum.BasicAuthSignup.String())
resolvers.VerifyEmail(ctx, model.VerifyEmailInput{
Token: verificationRequest.Token,
})
_, err = resolvers.Login(ctx, model.LoginInput{
Email: email,
Password: s.TestInfo.Password,
Roles: []string{"test"},
})
assert.NotNil(t, err, "invalid roles")
_, err = resolvers.Login(ctx, model.LoginInput{
Email: email,
Password: s.TestInfo.Password + "s",
})
assert.NotNil(t, err, "invalid password")
loginRes, err := resolvers.Login(ctx, model.LoginInput{
Email: email,
Password: s.TestInfo.Password,
})
assert.Nil(t, err, "login successful")
assert.NotNil(t, loginRes.AccessToken, "access token should not be empty")
cleanData(email)
})
}

View File

@ -0,0 +1,35 @@
package test
import (
"testing"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/enum"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/resolvers"
"github.com/stretchr/testify/assert"
)
func logoutTests(s TestSetup, t *testing.T) {
t.Run(`should logout user`, func(t *testing.T) {
req, ctx := createContext(s)
email := "logout." + s.TestInfo.Email
_, err := resolvers.MagicLinkLogin(ctx, model.MagicLinkLoginInput{
Email: email,
})
verificationRequest, err := db.Mgr.GetVerificationByEmail(email, enum.MagicLinkLogin.String())
verifyRes, err := resolvers.VerifyEmail(ctx, model.VerifyEmailInput{
Token: verificationRequest.Token,
})
token := *verifyRes.AccessToken
req.Header.Add("Authorization", "Bearer "+token)
_, err = resolvers.Logout(ctx)
assert.Nil(t, err)
_, err = resolvers.Profile(ctx)
assert.NotNil(t, err, "unauthorized")
cleanData(email)
})
}

View File

@ -0,0 +1,35 @@
package test
import (
"testing"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/enum"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/resolvers"
"github.com/stretchr/testify/assert"
)
func magicLinkLoginTests(s TestSetup, t *testing.T) {
t.Run(`should login with magic link`, func(t *testing.T) {
req, ctx := createContext(s)
email := "magic_link_login." + s.TestInfo.Email
_, err := resolvers.MagicLinkLogin(ctx, model.MagicLinkLoginInput{
Email: email,
})
assert.Nil(t, err)
verificationRequest, err := db.Mgr.GetVerificationByEmail(email, enum.MagicLinkLogin.String())
verifyRes, err := resolvers.VerifyEmail(ctx, model.VerifyEmailInput{
Token: verificationRequest.Token,
})
token := *verifyRes.AccessToken
req.Header.Add("Authorization", "Bearer "+token)
_, err = resolvers.Profile(ctx)
assert.Nil(t, err)
cleanData(email)
})
}

View File

@ -0,0 +1,23 @@
package test
import (
"context"
"testing"
"github.com/authorizerdev/authorizer/server/resolvers"
"github.com/stretchr/testify/assert"
)
func metaTests(s TestSetup, t *testing.T) {
t.Run(`should get meta information`, func(t *testing.T) {
ctx := context.Background()
meta, err := resolvers.Meta(ctx)
assert.Nil(t, err)
assert.False(t, meta.IsFacebookLoginEnabled)
assert.False(t, meta.IsGoogleLoginEnabled)
assert.False(t, meta.IsGithubLoginEnabled)
assert.True(t, meta.IsEmailVerificationEnabled)
assert.True(t, meta.IsBasicAuthenticationEnabled)
assert.True(t, meta.IsMagicLinkLoginEnabled)
})
}

View File

@ -0,0 +1,42 @@
package test
import (
"testing"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/enum"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/resolvers"
"github.com/stretchr/testify/assert"
)
func profileTests(s TestSetup, t *testing.T) {
t.Run(`should get profile only with token`, func(t *testing.T) {
req, ctx := createContext(s)
email := "profile." + s.TestInfo.Email
resolvers.Signup(ctx, model.SignUpInput{
Email: email,
Password: s.TestInfo.Password,
ConfirmPassword: s.TestInfo.Password,
})
_, err := resolvers.Profile(ctx)
assert.NotNil(t, err, "unauthorized")
verificationRequest, err := db.Mgr.GetVerificationByEmail(email, enum.BasicAuthSignup.String())
verifyRes, err := resolvers.VerifyEmail(ctx, model.VerifyEmailInput{
Token: verificationRequest.Token,
})
token := *verifyRes.AccessToken
req.Header.Add("Authorization", "Bearer "+token)
profileRes, err := resolvers.Profile(ctx)
assert.Nil(t, err)
newEmail := *&profileRes.Email
assert.Equal(t, email, newEmail, "emails should be equal")
cleanData(email)
})
}

View File

@ -0,0 +1,31 @@
package test
import (
"testing"
"github.com/authorizerdev/authorizer/server/enum"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/resolvers"
"github.com/stretchr/testify/assert"
)
func resendVerifyEmailTests(s TestSetup, t *testing.T) {
t.Run(`should resend verification email`, func(t *testing.T) {
_, ctx := createContext(s)
email := "resend_verify_email." + s.TestInfo.Email
_, err := resolvers.Signup(ctx, model.SignUpInput{
Email: email,
Password: s.TestInfo.Password,
ConfirmPassword: s.TestInfo.Password,
})
_, err = resolvers.ResendVerifyEmail(ctx, model.ResendVerifyEmailInput{
Email: email,
Identifier: enum.BasicAuthSignup.String(),
})
assert.Nil(t, err)
cleanData(email)
})
}

View File

@ -0,0 +1,49 @@
package test
import (
"testing"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/enum"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/resolvers"
"github.com/stretchr/testify/assert"
)
func resetPasswordTest(s TestSetup, t *testing.T) {
t.Run(`should reset password`, func(t *testing.T) {
email := "reset_password." + s.TestInfo.Email
_, ctx := createContext(s)
_, err := resolvers.Signup(ctx, model.SignUpInput{
Email: email,
Password: s.TestInfo.Password,
ConfirmPassword: s.TestInfo.Password,
})
_, err = resolvers.ForgotPassword(ctx, model.ForgotPasswordInput{
Email: email,
})
assert.Nil(t, err, "no errors for forgot password")
verificationRequest, err := db.Mgr.GetVerificationByEmail(email, enum.ForgotPassword.String())
assert.Nil(t, err, "should get forgot password request")
_, err = resolvers.ResetPassword(ctx, model.ResetPasswordInput{
Token: verificationRequest.Token,
Password: "test1",
ConfirmPassword: "test",
})
assert.NotNil(t, err, "passowrds don't match")
_, err = resolvers.ResetPassword(ctx, model.ResetPasswordInput{
Token: verificationRequest.Token,
Password: "test1",
ConfirmPassword: "test1",
})
assert.Nil(t, err, "password changed successfully")
cleanData(email)
})
}

View File

@ -0,0 +1,47 @@
package test
import (
"testing"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/enum"
)
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",
}
for dbType, dbURL := range databases {
constants.DATABASE_URL = dbURL
constants.DATABASE_TYPE = dbType
db.InitDB()
s := testSetup()
defer s.Server.Close()
t.Run("should pass tests for "+dbType, func(t *testing.T) {
loginTests(s, t)
signupTests(s, t)
forgotPasswordTest(s, t)
resendVerifyEmailTests(s, t)
resetPasswordTest(s, t)
verifyEmailTest(s, t)
sessionTests(s, t)
profileTests(s, t)
updateProfileTests(s, t)
magicLinkLoginTests(s, t)
logoutTests(s, t)
metaTests(s, t)
// admin tests
verificationRequestsTest(s, t)
usersTest(s, t)
deleteUserTest(s, t)
updateUserTest(s, t)
})
}
}

View File

@ -0,0 +1,42 @@
package test
import (
"testing"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/enum"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/resolvers"
"github.com/stretchr/testify/assert"
)
func sessionTests(s TestSetup, t *testing.T) {
t.Run(`should allow access to profile with session only`, func(t *testing.T) {
req, ctx := createContext(s)
email := "session." + s.TestInfo.Email
resolvers.Signup(ctx, model.SignUpInput{
Email: email,
Password: s.TestInfo.Password,
ConfirmPassword: s.TestInfo.Password,
})
_, err := resolvers.Session(ctx, []string{})
assert.NotNil(t, err, "unauthorized")
verificationRequest, err := db.Mgr.GetVerificationByEmail(email, enum.BasicAuthSignup.String())
verifyRes, err := resolvers.VerifyEmail(ctx, model.VerifyEmailInput{
Token: verificationRequest.Token,
})
token := *verifyRes.AccessToken
req.Header.Add("Authorization", "Bearer "+token)
sessionRes, err := resolvers.Session(ctx, []string{})
assert.Nil(t, err)
newToken := *sessionRes.AccessToken
assert.Equal(t, token, newToken, "tokens should be equal")
cleanData(email)
})
}

View File

@ -0,0 +1,47 @@
package test
import (
"testing"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/enum"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/resolvers"
"github.com/stretchr/testify/assert"
)
func signupTests(s TestSetup, t *testing.T) {
t.Run(`should complete the signup and check duplicates`, func(t *testing.T) {
_, ctx := createContext(s)
email := "signup." + s.TestInfo.Email
res, err := resolvers.Signup(ctx, model.SignUpInput{
Email: email,
Password: s.TestInfo.Password,
ConfirmPassword: s.TestInfo.Password + "s",
})
assert.NotNil(t, err, "invalid password errors")
res, err = resolvers.Signup(ctx, model.SignUpInput{
Email: email,
Password: s.TestInfo.Password,
ConfirmPassword: s.TestInfo.Password,
})
user := *res.User
assert.Equal(t, email, user.Email)
assert.Nil(t, res.AccessToken, "access token should be nil")
res, err = resolvers.Signup(ctx, model.SignUpInput{
Email: email,
Password: s.TestInfo.Password,
ConfirmPassword: s.TestInfo.Password,
})
assert.NotNil(t, err, "should throw duplicate email error")
verificationRequest, err := db.Mgr.GetVerificationByEmail(email, enum.BasicAuthSignup.String())
assert.Nil(t, err)
assert.Equal(t, email, verificationRequest.Email)
cleanData(email)
})
}

93
server/__test__/test.go Normal file
View File

@ -0,0 +1,93 @@
package test
import (
"context"
"net/http"
"net/http/httptest"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/enum"
"github.com/authorizerdev/authorizer/server/env"
"github.com/authorizerdev/authorizer/server/handlers"
"github.com/authorizerdev/authorizer/server/middlewares"
"github.com/authorizerdev/authorizer/server/session"
"github.com/gin-contrib/location"
"github.com/gin-gonic/gin"
)
// common user data to share across tests
type TestData struct {
Email string
Password string
}
type TestSetup struct {
GinEngine *gin.Engine
GinContext *gin.Context
Server *httptest.Server
TestInfo TestData
}
func cleanData(email string) {
verificationRequest, err := db.Mgr.GetVerificationByEmail(email, enum.BasicAuthSignup.String())
if err == nil {
err = db.Mgr.DeleteVerificationRequest(verificationRequest)
}
verificationRequest, err = db.Mgr.GetVerificationByEmail(email, enum.ForgotPassword.String())
if err == nil {
err = db.Mgr.DeleteVerificationRequest(verificationRequest)
}
verificationRequest, err = db.Mgr.GetVerificationByEmail(email, enum.UpdateEmail.String())
if err == nil {
err = db.Mgr.DeleteVerificationRequest(verificationRequest)
}
dbUser, err := db.Mgr.GetUserByEmail(email)
if err == nil {
db.Mgr.DeleteUser(dbUser)
db.Mgr.DeleteUserSession(dbUser.ID)
}
}
func createContext(s TestSetup) (*http.Request, context.Context) {
req, _ := http.NewRequest(
"POST",
"http://"+s.Server.Listener.Addr().String()+"/graphql",
nil,
)
ctx := context.WithValue(req.Context(), "GinContextKey", s.GinContext)
s.GinContext.Request = req
return req, ctx
}
func testSetup() TestSetup {
testData := TestData{
Email: "authorizer_tester@yopmail.com",
Password: "test",
}
constants.ENV_PATH = "../../.env.sample"
env.InitEnv()
session.InitSession()
w := httptest.NewRecorder()
c, r := gin.CreateTestContext(w)
r.Use(location.Default())
r.Use(middlewares.GinContextToContextMiddleware())
r.Use(middlewares.CORSMiddleware())
r.POST("/graphql", handlers.GraphqlHandler())
server := httptest.NewServer(r)
return TestSetup{
GinEngine: r,
GinContext: c,
Server: server,
TestInfo: testData,
}
}

View File

@ -0,0 +1,53 @@
package test
import (
"testing"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/enum"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/resolvers"
"github.com/stretchr/testify/assert"
)
func updateProfileTests(s TestSetup, t *testing.T) {
t.Run(`should update the profile with access token only`, func(t *testing.T) {
req, ctx := createContext(s)
email := "update_profile." + s.TestInfo.Email
resolvers.Signup(ctx, model.SignUpInput{
Email: email,
Password: s.TestInfo.Password,
ConfirmPassword: s.TestInfo.Password,
})
fName := "samani"
_, err := resolvers.UpdateProfile(ctx, model.UpdateProfileInput{
FamilyName: &fName,
})
assert.NotNil(t, err, "unauthorized")
verificationRequest, err := db.Mgr.GetVerificationByEmail(email, enum.BasicAuthSignup.String())
verifyRes, err := resolvers.VerifyEmail(ctx, model.VerifyEmailInput{
Token: verificationRequest.Token,
})
token := *verifyRes.AccessToken
req.Header.Add("Authorization", "Bearer "+token)
_, err = resolvers.UpdateProfile(ctx, model.UpdateProfileInput{
FamilyName: &fName,
})
assert.Nil(t, err)
newEmail := "new_" + email
_, err = resolvers.UpdateProfile(ctx, model.UpdateProfileInput{
Email: &newEmail,
})
assert.Nil(t, err)
_, err = resolvers.Profile(ctx)
assert.NotNil(t, err, "unauthorized")
cleanData(newEmail)
cleanData(email)
})
}

View File

@ -0,0 +1,40 @@
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"
)
func updateUserTest(s TestSetup, t *testing.T) {
t.Run(`should update the user with admin secret only`, func(t *testing.T) {
req, ctx := createContext(s)
email := "update_user." + s.TestInfo.Email
signupRes, _ := resolvers.Signup(ctx, model.SignUpInput{
Email: email,
Password: s.TestInfo.Password,
ConfirmPassword: s.TestInfo.Password,
})
user := *signupRes.User
adminRole := "admin"
userRole := "user"
newRoles := []*string{&adminRole, &userRole}
_, err := resolvers.UpdateUser(ctx, model.UpdateUserInput{
ID: user.ID,
Roles: newRoles,
})
assert.NotNil(t, err, "unauthorized")
req.Header.Add("x-authorizer-admin-secret", constants.ADMIN_SECRET)
_, err = resolvers.UpdateUser(ctx, model.UpdateUserInput{
ID: user.ID,
Roles: newRoles,
})
assert.Nil(t, err)
cleanData(email)
})
}

View File

@ -1,15 +1,16 @@
package utils
package test
import (
"testing"
"github.com/authorizerdev/authorizer/server/utils"
"github.com/stretchr/testify/assert"
)
func TestGetHostName(t *testing.T) {
authorizer_url := "http://test.herokuapp.com:80"
host, port := GetHostParts(authorizer_url)
host, port := utils.GetHostParts(authorizer_url)
expectedHost := "test.herokuapp.com"
assert.Equal(t, host, expectedHost, "hostname should be equal")
@ -19,7 +20,7 @@ func TestGetHostName(t *testing.T) {
func TestGetDomainName(t *testing.T) {
authorizer_url := "http://test.herokuapp.com"
got := GetDomainName(authorizer_url)
got := utils.GetDomainName(authorizer_url)
want := "herokuapp.com"
assert.Equal(t, got, want, "domain name should be equal")

View File

@ -0,0 +1,33 @@
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"
)
func usersTest(s TestSetup, t *testing.T) {
t.Run(`should get users list with admin secret only`, func(t *testing.T) {
req, ctx := createContext(s)
email := "users." + s.TestInfo.Email
resolvers.Signup(ctx, model.SignUpInput{
Email: email,
Password: s.TestInfo.Password,
ConfirmPassword: s.TestInfo.Password,
})
users, err := resolvers.Users(ctx)
assert.NotNil(t, err, "unauthorized")
req.Header.Add("x-authorizer-admin-secret", constants.ADMIN_SECRET)
users, err = resolvers.Users(ctx)
assert.Nil(t, err)
rLen := len(users)
assert.GreaterOrEqual(t, rLen, 1)
cleanData(email)
})
}

View File

@ -0,0 +1,43 @@
package test
import (
"testing"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/enum"
"github.com/authorizerdev/authorizer/server/utils"
"github.com/stretchr/testify/assert"
)
func TestIsValidEmail(t *testing.T) {
validEmail := "lakhan@gmail.com"
invalidEmail1 := "lakhan"
invalidEmail2 := "lakhan.me"
assert.True(t, utils.IsValidEmail(validEmail), "it should be valid email")
assert.False(t, utils.IsValidEmail(invalidEmail1), "it should be invalid email")
assert.False(t, utils.IsValidEmail(invalidEmail2), "it should be invalid email")
}
func TestIsValidOrigin(t *testing.T) {
// don't use portocal(http/https) for ALLOWED_ORIGINS while testing,
// as we trim them off while running the main function
constants.ALLOWED_ORIGINS = []string{"localhost:8080", "*.google.com", "*.google.in", "*abc.*"}
assert.False(t, utils.IsValidOrigin("http://myapp.com"), "it should be invalid origin")
assert.False(t, utils.IsValidOrigin("http://appgoogle.com"), "it should be invalid origin")
assert.True(t, utils.IsValidOrigin("http://app.google.com"), "it should be valid origin")
assert.False(t, utils.IsValidOrigin("http://app.google.ind"), "it should be invalid origin")
assert.True(t, utils.IsValidOrigin("http://app.google.in"), "it should be valid origin")
assert.True(t, utils.IsValidOrigin("http://xyx.abc.com"), "it should be valid origin")
assert.True(t, utils.IsValidOrigin("http://xyx.abc.in"), "it should be valid origin")
assert.True(t, utils.IsValidOrigin("http://xyxabc.in"), "it should be valid origin")
assert.True(t, utils.IsValidOrigin("http://localhost:8080"), "it should be valid origin")
}
func TestIsValidIdentifier(t *testing.T) {
assert.False(t, utils.IsValidVerificationIdentifier("test"), "it should be invalid identifier")
assert.True(t, utils.IsValidVerificationIdentifier(enum.BasicAuthSignup.String()), "it should be valid identifier")
assert.True(t, utils.IsValidVerificationIdentifier(enum.UpdateEmail.String()), "it should be valid identifier")
assert.True(t, utils.IsValidVerificationIdentifier(enum.ForgotPassword.String()), "it should be valid identifier")
}

View File

@ -0,0 +1,35 @@
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"
)
func verificationRequestsTest(s TestSetup, t *testing.T) {
t.Run(`should get verification requests with admin secret only`, func(t *testing.T) {
req, ctx := createContext(s)
email := "verification_requests." + s.TestInfo.Email
resolvers.Signup(ctx, model.SignUpInput{
Email: email,
Password: s.TestInfo.Password,
ConfirmPassword: s.TestInfo.Password,
})
requests, err := resolvers.VerificationRequests(ctx)
assert.NotNil(t, err, "unauthorizer")
req.Header.Add("x-authorizer-admin-secret", constants.ADMIN_SECRET)
requests, err = resolvers.VerificationRequests(ctx)
assert.Nil(t, err)
rLen := len(requests)
assert.GreaterOrEqual(t, rLen, 1)
cleanData(email)
})
}

View File

@ -0,0 +1,38 @@
package test
import (
"testing"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/enum"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/resolvers"
"github.com/stretchr/testify/assert"
)
func verifyEmailTest(s TestSetup, t *testing.T) {
t.Run(`should verify email`, func(t *testing.T) {
_, ctx := createContext(s)
email := "verify_email." + s.TestInfo.Email
res, err := resolvers.Signup(ctx, model.SignUpInput{
Email: email,
Password: s.TestInfo.Password,
ConfirmPassword: s.TestInfo.Password,
})
user := *res.User
assert.Equal(t, email, user.Email)
assert.Nil(t, res.AccessToken, "access token should be nil")
verificationRequest, err := db.Mgr.GetVerificationByEmail(email, enum.BasicAuthSignup.String())
assert.Nil(t, err)
assert.Equal(t, email, verificationRequest.Email)
verifyRes, err := resolvers.VerifyEmail(ctx, model.VerifyEmailInput{
Token: verificationRequest.Token,
})
assert.Nil(t, err)
assert.NotEqual(t, verifyRes.AccessToken, "", "access token should not be empty")
cleanData(email)
})
}

View File

@ -1,5 +1,6 @@
package constants
// this constants are configured via env
var (
ADMIN_SECRET = ""
ENV = ""
@ -17,14 +18,14 @@ var (
ALLOWED_ORIGINS = []string{}
AUTHORIZER_URL = ""
APP_URL = ""
PORT = "8080"
PORT = ""
REDIS_URL = ""
IS_PROD = false
COOKIE_NAME = ""
RESET_PASSWORD_URL = ""
DISABLE_EMAIL_VERIFICATION = false
DISABLE_BASIC_AUTHENTICATION = false
DISABLE_MAGIC_LOGIN = false
DISABLE_MAGIC_LINK_LOGIN = false
// ROLES
ROLES = []string{}

View File

@ -36,16 +36,12 @@ func initArangodb() (arangoDriver.Database, error) {
if arangodb_exists {
log.Println(constants.DATABASE_NAME + " db exists already")
arangodb, err = arangoClient.Database(nil, constants.DATABASE_NAME)
if err != nil {
return nil, err
}
} else {
arangodb, err = arangoClient.CreateDatabase(nil, constants.DATABASE_NAME, nil)
if err != nil {
return nil, err
}
@ -61,11 +57,11 @@ func initArangodb() (arangoDriver.Database, error) {
}
}
userCollection, _ := arangodb.Collection(nil, Collections.User)
userCollection.EnsureHashIndex(ctx, []string{"id"}, &arangoDriver.EnsureHashIndexOptions{
userCollection.EnsureHashIndex(ctx, []string{"email"}, &arangoDriver.EnsureHashIndexOptions{
Unique: true,
Sparse: true,
})
userCollection.EnsureHashIndex(ctx, []string{"email"}, &arangoDriver.EnsureHashIndexOptions{
userCollection.EnsureHashIndex(ctx, []string{"phone_number"}, &arangoDriver.EnsureHashIndexOptions{
Unique: true,
Sparse: true,
})
@ -79,11 +75,8 @@ func initArangodb() (arangoDriver.Database, error) {
log.Println("error creating collection("+Collections.VerificationRequest+"):", err)
}
}
verificationRequestCollection, _ := arangodb.Collection(nil, Collections.VerificationRequest)
verificationRequestCollection.EnsureHashIndex(ctx, []string{"id"}, &arangoDriver.EnsureHashIndexOptions{
Unique: true,
Sparse: true,
})
verificationRequestCollection.EnsureHashIndex(ctx, []string{"email", "identifier"}, &arangoDriver.EnsureHashIndexOptions{
Unique: true,
Sparse: true,
@ -103,8 +96,7 @@ func initArangodb() (arangoDriver.Database, error) {
}
sessionCollection, _ := arangodb.Collection(nil, Collections.Session)
sessionCollection.EnsureHashIndex(ctx, []string{"id"}, &arangoDriver.EnsureHashIndexOptions{
Unique: true,
sessionCollection.EnsureHashIndex(ctx, []string{"user_id"}, &arangoDriver.EnsureHashIndexOptions{
Sparse: true,
})

View File

@ -26,8 +26,9 @@ type Manager interface {
GetVerificationByToken(token string) (VerificationRequest, error)
DeleteVerificationRequest(verificationRequest VerificationRequest) error
GetVerificationRequests() ([]VerificationRequest, error)
GetVerificationByEmail(email string) (VerificationRequest, error)
GetVerificationByEmail(email string, identifier string) (VerificationRequest, error)
AddSession(session Session) error
DeleteUserSession(userId string) error
}
type manager struct {
@ -94,8 +95,8 @@ func InitDB() {
Mgr = &manager{
sqlDB: nil,
mongodb: nil,
arangodb: arangodb,
mongodb: nil,
}
break

View File

@ -36,25 +36,21 @@ func initMongodb() (*mongo.Database, error) {
userCollection := mongodb.Collection(Collections.User, options.Collection())
userCollection.Indexes().CreateMany(ctx, []mongo.IndexModel{
mongo.IndexModel{
Keys: bson.M{"id": 1},
Keys: bson.M{"email": 1},
Options: options.Index().SetUnique(true).SetSparse(true),
},
}, options.CreateIndexes())
userCollection.Indexes().CreateMany(ctx, []mongo.IndexModel{
mongo.IndexModel{
Keys: bson.M{"email": 1},
Options: options.Index().SetUnique(true).SetSparse(true),
Keys: bson.M{"phone_number": 1},
Options: options.Index().SetUnique(true).SetSparse(true).SetPartialFilterExpression(map[string]interface{}{
"phone_number": map[string]string{"$type": "string"},
}),
},
}, options.CreateIndexes())
mongodb.CreateCollection(ctx, Collections.VerificationRequest, options.CreateCollection())
verificationRequestCollection := mongodb.Collection(Collections.VerificationRequest, options.Collection())
verificationRequestCollection.Indexes().CreateMany(ctx, []mongo.IndexModel{
mongo.IndexModel{
Keys: bson.M{"id": 1},
Options: options.Index().SetUnique(true).SetSparse(true),
},
}, options.CreateIndexes())
verificationRequestCollection.Indexes().CreateMany(ctx, []mongo.IndexModel{
mongo.IndexModel{
Keys: bson.M{"email": 1, "identifier": 1},
@ -72,8 +68,8 @@ func initMongodb() (*mongo.Database, error) {
sessionCollection := mongodb.Collection(Collections.Session, options.Collection())
sessionCollection.Indexes().CreateMany(ctx, []mongo.IndexModel{
mongo.IndexModel{
Keys: bson.M{"id": 1},
Options: options.Index().SetUnique(true).SetSparse(true),
Keys: bson.M{"user_id": 1},
Options: options.Index().SetSparse(true),
},
}, options.CreateIndexes())

View File

@ -1,19 +1,20 @@
package db
import (
"fmt"
"log"
"time"
"github.com/google/uuid"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo/options"
"gorm.io/gorm/clause"
)
type Session struct {
Key string `json:"_key,omitempty" bson:"_key,omitempty"` // for arangodb
ObjectID string `json:"_id,omitempty" bson:"_id"` // for arangodb & mongodb
ID string `gorm:"primaryKey;type:char(36)" json:"id" bson:"id"`
UserID string `gorm:"type:char(36)" json:"user_id" bson:"user_id"`
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id"`
UserID string `gorm:"type:char(36),index:" json:"user_id" bson:"user_id"`
User User `json:"-" bson:"-"`
UserAgent string `json:"user_agent" bson:"user_agent"`
IP string `json:"ip" bson:"ip"`
@ -29,7 +30,6 @@ func (mgr *manager) AddSession(session Session) error {
if IsORMSupported {
session.Key = session.ID
session.ObjectID = session.ID
res := mgr.sqlDB.Clauses(
clause.OnConflict{
DoNothing: true,
@ -53,7 +53,6 @@ func (mgr *manager) AddSession(session Session) error {
if IsMongoDB {
session.Key = session.ID
session.ObjectID = session.ID
session.CreatedAt = time.Now().Unix()
session.UpdatedAt = time.Now().Unix()
sessionCollection := mgr.mongodb.Collection(Collections.Session, options.Collection())
@ -66,3 +65,38 @@ func (mgr *manager) AddSession(session Session) error {
return nil
}
func (mgr *manager) DeleteUserSession(userId string) error {
if IsORMSupported {
result := mgr.sqlDB.Where("user_id = ?", userId).Delete(&Session{})
if result.Error != nil {
log.Println(`error deleting session:`, result.Error)
return result.Error
}
}
if IsArangoDB {
query := fmt.Sprintf(`FOR d IN %s FILTER d.user_id == @userId REMOVE { _key: d._key } IN %s`, Collections.Session, Collections.Session)
bindVars := map[string]interface{}{
"userId": userId,
}
cursor, err := mgr.arangodb.Query(nil, query, bindVars)
if err != nil {
log.Println("=> error deleting arangodb session:", err)
return err
}
defer cursor.Close()
}
if IsMongoDB {
sessionCollection := mgr.mongodb.Collection(Collections.Session, options.Collection())
_, err := sessionCollection.DeleteMany(nil, bson.M{"user_id": userId}, options.Delete())
if err != nil {
log.Println("error deleting session:", err)
return err
}
}
return nil
}

View File

@ -7,6 +7,7 @@ import (
"github.com/arangodb/go-driver"
arangoDriver "github.com/arangodb/go-driver"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/google/uuid"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo/options"
@ -15,18 +16,24 @@ import (
type User struct {
Key string `json:"_key,omitempty" bson:"_key"` // for arangodb
ObjectID string `json:"_id,omitempty" bson:"_id"` // for arangodb & mongodb
ID string `gorm:"primaryKey;type:char(36)" json:"id" bson:"id"`
FirstName string `json:"first_name" bson:"first_name"`
LastName string `json:"last_name" bson:"last_name"`
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id"`
Email string `gorm:"unique" json:"email" bson:"email"`
Password string `gorm:"type:text" json:"password" bson:"password"`
SignupMethod string `json:"signup_method" bson:"signup_method"`
EmailVerifiedAt int64 `json:"email_verified_at" bson:"email_verified_at"`
CreatedAt int64 `gorm:"autoCreateTime" json:"created_at" bson:"created_at"`
UpdatedAt int64 `gorm:"autoUpdateTime" json:"updated_at" bson:"updated_at"`
Image string `gorm:"type:text" json:"image" bson:"image"`
EmailVerifiedAt *int64 `json:"email_verified_at" bson:"email_verified_at"`
Password *string `gorm:"type:text" json:"password" bson:"password"`
SignupMethods string `json:"signup_methods" bson:"signup_methods"`
GivenName *string `json:"given_name" bson:"given_name"`
FamilyName *string `json:"family_name" bson:"family_name"`
MiddleName *string `json:"middle_name" bson:"middle_name"`
Nickname *string `json:"nickname" bson:"nickname"`
Gender *string `json:"gender" bson:"gender"`
Birthdate *string `json:"birthdate" bson:"birthdate"`
PhoneNumber *string `gorm:"unique" json:"phone_number" bson:"phone_number"`
PhoneNumberVerifiedAt *int64 `json:"phone_number_verified_at" bson:"phone_number_verified_at"`
Picture *string `gorm:"type:text" json:"picture" bson:"picture"`
Roles string `json:"roles" bson:"roles"`
UpdatedAt int64 `gorm:"autoUpdateTime" json:"updated_at" bson:"updated_at"`
CreatedAt int64 `gorm:"autoCreateTime" json:"created_at" bson:"created_at"`
}
// AddUser function to add user even with email conflict
@ -35,10 +42,13 @@ func (mgr *manager) AddUser(user User) (User, error) {
user.ID = uuid.New().String()
}
if user.Roles == "" {
user.Roles = constants.DEFAULT_ROLES[0]
}
if IsORMSupported {
// copy id as value for fields required for mongodb & arangodb
user.Key = user.ID
user.ObjectID = user.ID
result := mgr.sqlDB.Clauses(
clause.OnConflict{
UpdateAll: true,
@ -61,14 +71,13 @@ func (mgr *manager) AddUser(user User) (User, error) {
return user, err
}
user.Key = meta.Key
user.ObjectID = meta.ID.String()
user.ID = meta.ID.String()
}
if IsMongoDB {
user.CreatedAt = time.Now().Unix()
user.UpdatedAt = time.Now().Unix()
user.Key = user.ID
user.ObjectID = user.ID
userCollection := mgr.mongodb.Collection(Collections.User, options.Collection())
_, err := userCollection.InsertOne(nil, user)
if err != nil {
@ -102,12 +111,12 @@ func (mgr *manager) UpdateUser(user User) (User, error) {
}
user.Key = meta.Key
user.ObjectID = meta.ID.String()
user.ID = meta.ID.String()
}
if IsMongoDB {
userCollection := mgr.mongodb.Collection(Collections.User, options.Collection())
_, err := userCollection.UpdateOne(nil, bson.M{"id": bson.M{"$eq": user.ID}}, bson.M{"$set": user}, options.MergeUpdateOptions())
_, err := userCollection.UpdateOne(nil, bson.M{"_id": bson.M{"$eq": user.ID}}, bson.M{"$set": user}, options.MergeUpdateOptions())
if err != nil {
log.Println("error updating user:", err)
return user, err
@ -236,7 +245,7 @@ func (mgr *manager) GetUserByID(id string) (User, error) {
}
if IsArangoDB {
query := fmt.Sprintf("FOR d in %s FILTER d.id == @id LIMIT 1 RETURN d", Collections.User)
query := fmt.Sprintf("FOR d in %s FILTER d._id == @id LIMIT 1 RETURN d", Collections.User)
bindVars := map[string]interface{}{
"id": id,
}
@ -263,7 +272,7 @@ func (mgr *manager) GetUserByID(id string) (User, error) {
if IsMongoDB {
userCollection := mgr.mongodb.Collection(Collections.User, options.Collection())
err := userCollection.FindOne(nil, bson.M{"id": id}).Decode(&user)
err := userCollection.FindOne(nil, bson.M{"_id": id}).Decode(&user)
if err != nil {
return user, err
}
@ -293,7 +302,7 @@ func (mgr *manager) DeleteUser(user User) error {
if IsMongoDB {
userCollection := mgr.mongodb.Collection(Collections.User, options.Collection())
_, err := userCollection.DeleteOne(nil, bson.M{"id": user.ID}, options.Delete())
_, err := userCollection.DeleteOne(nil, bson.M{"_id": user.ID}, options.Delete())
if err != nil {
log.Println("error deleting user:", err)
return err

View File

@ -14,8 +14,7 @@ import (
type VerificationRequest struct {
Key string `json:"_key,omitempty" bson:"_key"` // for arangodb
ObjectID string `json:"_id,omitempty" bson:"_id"` // for arangodb & mongodb
ID string `gorm:"primaryKey;type:char(36)" json:"id" bson:"id"`
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id"`
Token string `gorm:"type:text" json:"token" bson:"token"`
Identifier string `gorm:"uniqueIndex:idx_email_identifier" json:"identifier" bson:"identifier"`
ExpiresAt int64 `json:"expires_at" bson:"expires_at"`
@ -32,7 +31,6 @@ func (mgr *manager) AddVerification(verification VerificationRequest) (Verificat
if IsORMSupported {
// copy id as value for fields required for mongodb & arangodb
verification.Key = verification.ID
verification.ObjectID = verification.ID
result := mgr.sqlDB.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "email"}, {Name: "identifier"}},
DoUpdates: clause.AssignmentColumns([]string{"token", "expires_at"}),
@ -54,14 +52,13 @@ func (mgr *manager) AddVerification(verification VerificationRequest) (Verificat
return verification, err
}
verification.Key = meta.Key
verification.ObjectID = meta.ID.String()
verification.ID = meta.ID.String()
}
if IsMongoDB {
verification.CreatedAt = time.Now().Unix()
verification.UpdatedAt = time.Now().Unix()
verification.Key = verification.ID
verification.ObjectID = verification.ID
verificationRequestCollection := mgr.mongodb.Collection(Collections.VerificationRequest, options.Collection())
_, err := verificationRequestCollection.InsertOne(nil, verification)
if err != nil {
@ -182,10 +179,10 @@ func (mgr *manager) GetVerificationByToken(token string) (VerificationRequest, e
return verification, nil
}
func (mgr *manager) GetVerificationByEmail(email string) (VerificationRequest, error) {
func (mgr *manager) GetVerificationByEmail(email string, identifier string) (VerificationRequest, error) {
var verification VerificationRequest
if IsORMSupported {
result := mgr.sqlDB.Where("email = ?", email).First(&verification)
result := mgr.sqlDB.Where("email = ? AND identifier = ?", email, identifier).First(&verification)
if result.Error != nil {
log.Println(`error getting verification token:`, result.Error)
@ -194,9 +191,10 @@ func (mgr *manager) GetVerificationByEmail(email string) (VerificationRequest, e
}
if IsArangoDB {
query := fmt.Sprintf("FOR d in %s FILTER d.email == @email LIMIT 1 RETURN d", Collections.VerificationRequest)
query := fmt.Sprintf("FOR d in %s FILTER d.email == @email FILTER d.identifier == @identifier LIMIT 1 RETURN d", Collections.VerificationRequest)
bindVars := map[string]interface{}{
"email": email,
"identifier": identifier,
}
cursor, err := mgr.arangodb.Query(nil, query, bindVars)
@ -221,7 +219,7 @@ func (mgr *manager) GetVerificationByEmail(email string) (VerificationRequest, e
if IsMongoDB {
verificationRequestCollection := mgr.mongodb.Collection(Collections.VerificationRequest, options.Collection())
err := verificationRequestCollection.FindOne(nil, bson.M{"email": email}).Decode(&verification)
err := verificationRequestCollection.FindOne(nil, bson.M{"email": email, "identifier": identifier}).Decode(&verification)
if err != nil {
return verification, err
}
@ -251,7 +249,7 @@ func (mgr *manager) DeleteVerificationRequest(verificationRequest VerificationRe
if IsMongoDB {
verificationRequestCollection := mgr.mongodb.Collection(Collections.VerificationRequest, options.Collection())
_, err := verificationRequestCollection.DeleteOne(nil, bson.M{"id": verificationRequest.ID}, options.Delete())
_, err := verificationRequestCollection.DeleteOne(nil, bson.M{"_id": verificationRequest.ID}, options.Delete())
if err != nil {
log.Println("error deleting verification request::", err)
return err

View File

@ -4,7 +4,7 @@ type SignupMethod int
const (
BasicAuth SignupMethod = iota
MagicLink
MagicLinkLogin
Google
Github
Facebook
@ -13,7 +13,7 @@ const (
func (d SignupMethod) String() string {
return [...]string{
"basic_auth",
"magic_link",
"magic_link_login",
"google",
"github",
"facebook",

201
server/env/env.go vendored
View File

@ -1,7 +1,6 @@
package env
import (
"flag"
"log"
"os"
"strings"
@ -25,13 +24,8 @@ func InitEnv() {
if constants.ENV_PATH == "" {
constants.ENV_PATH = `.env`
}
ARG_DB_URL = flag.String("database_url", "", "Database connection string")
ARG_DB_TYPE = flag.String("database_type", "", "Database type, possible values are postgres,mysql,sqlite")
ARG_AUTHORIZER_URL = flag.String("authorizer_url", "", "URL for authorizer instance, eg: https://xyz.herokuapp.com")
ARG_ENV_FILE = flag.String("env_file", "", "Env file path")
flag.Parse()
if *ARG_ENV_FILE != "" {
if ARG_ENV_FILE != nil && *ARG_ENV_FILE != "" {
constants.ENV_PATH = *ARG_ENV_FILE
}
@ -41,39 +35,16 @@ func InitEnv() {
}
constants.VERSION = VERSION
constants.ADMIN_SECRET = os.Getenv("ADMIN_SECRET")
constants.ENV = os.Getenv("ENV")
constants.DATABASE_TYPE = os.Getenv("DATABASE_TYPE")
constants.DATABASE_URL = os.Getenv("DATABASE_URL")
constants.DATABASE_NAME = os.Getenv("DATABASE_NAME")
constants.SMTP_HOST = os.Getenv("SMTP_HOST")
constants.SMTP_PORT = os.Getenv("SMTP_PORT")
constants.SENDER_EMAIL = os.Getenv("SENDER_EMAIL")
constants.SENDER_PASSWORD = os.Getenv("SENDER_PASSWORD")
constants.JWT_SECRET = os.Getenv("JWT_SECRET")
constants.JWT_TYPE = os.Getenv("JWT_TYPE")
constants.AUTHORIZER_URL = strings.TrimSuffix(os.Getenv("AUTHORIZER_URL"), "/")
constants.PORT = os.Getenv("PORT")
constants.REDIS_URL = os.Getenv("REDIS_URL")
constants.COOKIE_NAME = os.Getenv("COOKIE_NAME")
constants.GOOGLE_CLIENT_ID = os.Getenv("GOOGLE_CLIENT_ID")
constants.GOOGLE_CLIENT_SECRET = os.Getenv("GOOGLE_CLIENT_SECRET")
constants.GITHUB_CLIENT_ID = os.Getenv("GITHUB_CLIENT_ID")
constants.GITHUB_CLIENT_SECRET = os.Getenv("GITHUB_CLIENT_SECRET")
constants.FACEBOOK_CLIENT_ID = os.Getenv("FACEBOOK_CLIENT_ID")
constants.FACEBOOK_CLIENT_SECRET = os.Getenv("FACEBOOK_CLIENT_SECRET")
constants.TWITTER_CLIENT_ID = os.Getenv("TWITTER_CLIENT_ID")
constants.TWITTER_CLIENT_SECRET = os.Getenv("TWITTER_CLIENT_SECRET")
constants.RESET_PASSWORD_URL = strings.TrimPrefix(os.Getenv("RESET_PASSWORD_URL"), "/")
constants.DISABLE_BASIC_AUTHENTICATION = os.Getenv("DISABLE_BASIC_AUTHENTICATION") == "true"
constants.DISABLE_EMAIL_VERIFICATION = os.Getenv("DISABLE_EMAIL_VERIFICATION") == "true"
constants.DISABLE_MAGIC_LOGIN = os.Getenv("DISABLE_MAGIC_LOGIN") == "true"
constants.JWT_ROLE_CLAIM = os.Getenv("JWT_ROLE_CLAIM")
if constants.ADMIN_SECRET == "" {
constants.ADMIN_SECRET = os.Getenv("ADMIN_SECRET")
if constants.ADMIN_SECRET == "" {
panic("root admin secret is required")
}
}
if constants.ENV == "" {
constants.ENV = os.Getenv("ENV")
if constants.ENV == "" {
constants.ENV = "production"
}
@ -84,6 +55,131 @@ func InitEnv() {
} else {
constants.IS_PROD = false
}
}
if constants.DATABASE_TYPE == "" {
constants.DATABASE_TYPE = os.Getenv("DATABASE_TYPE")
log.Println(constants.DATABASE_TYPE)
if ARG_DB_TYPE != nil && *ARG_DB_TYPE != "" {
constants.DATABASE_TYPE = *ARG_DB_TYPE
}
if constants.DATABASE_TYPE == "" {
panic("DATABASE_TYPE is required")
}
}
if constants.DATABASE_URL == "" {
constants.DATABASE_URL = os.Getenv("DATABASE_URL")
if ARG_DB_URL != nil && *ARG_DB_URL != "" {
constants.DATABASE_URL = *ARG_DB_URL
}
if constants.DATABASE_URL == "" {
panic("DATABASE_URL is required")
}
}
if constants.DATABASE_NAME == "" {
constants.DATABASE_NAME = os.Getenv("DATABASE_NAME")
if constants.DATABASE_NAME == "" {
constants.DATABASE_NAME = "authorizer"
}
}
if constants.SMTP_HOST == "" {
constants.SMTP_HOST = os.Getenv("SMTP_HOST")
}
if constants.SMTP_PORT == "" {
constants.SMTP_PORT = os.Getenv("SMTP_PORT")
}
if constants.SENDER_EMAIL == "" {
constants.SENDER_EMAIL = os.Getenv("SENDER_EMAIL")
}
if constants.SENDER_PASSWORD == "" {
constants.SENDER_PASSWORD = os.Getenv("SENDER_PASSWORD")
}
if constants.JWT_SECRET == "" {
constants.JWT_SECRET = os.Getenv("JWT_SECRET")
}
if constants.JWT_TYPE == "" {
constants.JWT_TYPE = os.Getenv("JWT_TYPE")
}
if constants.JWT_ROLE_CLAIM == "" {
constants.JWT_ROLE_CLAIM = os.Getenv("JWT_ROLE_CLAIM")
if constants.JWT_ROLE_CLAIM == "" {
constants.JWT_ROLE_CLAIM = "role"
}
}
if constants.AUTHORIZER_URL == "" {
constants.AUTHORIZER_URL = strings.TrimSuffix(os.Getenv("AUTHORIZER_URL"), "/")
if ARG_AUTHORIZER_URL != nil && *ARG_AUTHORIZER_URL != "" {
constants.AUTHORIZER_URL = *ARG_AUTHORIZER_URL
}
}
if constants.PORT == "" {
constants.PORT = os.Getenv("PORT")
if constants.PORT == "" {
constants.PORT = "8080"
}
}
if constants.REDIS_URL == "" {
constants.REDIS_URL = os.Getenv("REDIS_URL")
}
if constants.COOKIE_NAME == "" {
constants.COOKIE_NAME = os.Getenv("COOKIE_NAME")
}
if constants.GOOGLE_CLIENT_ID == "" {
constants.GOOGLE_CLIENT_ID = os.Getenv("GOOGLE_CLIENT_ID")
}
if constants.GOOGLE_CLIENT_SECRET == "" {
constants.GOOGLE_CLIENT_SECRET = os.Getenv("GOOGLE_CLIENT_SECRET")
}
if constants.GITHUB_CLIENT_ID == "" {
constants.GITHUB_CLIENT_ID = os.Getenv("GITHUB_CLIENT_ID")
}
if constants.GITHUB_CLIENT_SECRET == "" {
constants.GITHUB_CLIENT_SECRET = os.Getenv("GITHUB_CLIENT_SECRET")
}
if constants.FACEBOOK_CLIENT_ID == "" {
constants.FACEBOOK_CLIENT_ID = os.Getenv("FACEBOOK_CLIENT_ID")
}
if constants.FACEBOOK_CLIENT_SECRET == "" {
constants.FACEBOOK_CLIENT_SECRET = os.Getenv("FACEBOOK_CLIENT_SECRET")
}
if constants.RESET_PASSWORD_URL == "" {
constants.RESET_PASSWORD_URL = strings.TrimPrefix(os.Getenv("RESET_PASSWORD_URL"), "/")
}
constants.DISABLE_BASIC_AUTHENTICATION = os.Getenv("DISABLE_BASIC_AUTHENTICATION") == "true"
constants.DISABLE_EMAIL_VERIFICATION = os.Getenv("DISABLE_EMAIL_VERIFICATION") == "true"
constants.DISABLE_MAGIC_LINK_LOGIN = os.Getenv("DISABLE_MAGIC_LINK_LOGIN") == "true"
if constants.SMTP_HOST == "" || constants.SENDER_EMAIL == "" || constants.SENDER_PASSWORD == "" {
constants.DISABLE_EMAIL_VERIFICATION = true
constants.DISABLE_MAGIC_LINK_LOGIN = true
}
allowedOriginsSplit := strings.Split(os.Getenv("ALLOWED_ORIGINS"), ",")
allowedOrigins := []string{}
@ -113,30 +209,6 @@ func InitEnv() {
constants.ALLOWED_ORIGINS = allowedOrigins
if *ARG_AUTHORIZER_URL != "" {
constants.AUTHORIZER_URL = *ARG_AUTHORIZER_URL
}
if *ARG_DB_URL != "" {
constants.DATABASE_URL = *ARG_DB_URL
}
if *ARG_DB_TYPE != "" {
constants.DATABASE_TYPE = *ARG_DB_TYPE
}
if constants.DATABASE_URL == "" {
panic("Database url is required")
}
if constants.DATABASE_TYPE == "" {
panic("Database type is required")
}
if constants.DATABASE_NAME == "" {
constants.DATABASE_NAME = "authorizer"
}
if constants.JWT_TYPE == "" {
constants.JWT_TYPE = "HS256"
}
@ -145,13 +217,8 @@ func InitEnv() {
constants.COOKIE_NAME = "authorizer"
}
if constants.SMTP_HOST == "" || constants.SENDER_EMAIL == "" || constants.SENDER_PASSWORD == "" {
constants.DISABLE_EMAIL_VERIFICATION = true
constants.DISABLE_MAGIC_LOGIN = true
}
if constants.DISABLE_EMAIL_VERIFICATION {
constants.DISABLE_MAGIC_LOGIN = true
constants.DISABLE_MAGIC_LINK_LOGIN = true
}
rolesSplit := strings.Split(os.Getenv("ROLES"), ",")
@ -196,10 +263,6 @@ func InitEnv() {
constants.DEFAULT_ROLES = defaultRoles
constants.PROTECTED_ROLES = protectedRoles
if constants.JWT_ROLE_CLAIM == "" {
constants.JWT_ROLE_CLAIM = "role"
}
if os.Getenv("ORGANIZATION_NAME") != "" {
constants.ORGANIZATION_NAME = os.Getenv("ORGANIZATION_NAME")
}

View File

@ -3,7 +3,7 @@ module github.com/authorizerdev/authorizer/server
go 1.16
require (
github.com/99designs/gqlgen v0.13.0
github.com/99designs/gqlgen v0.14.0
github.com/arangodb/go-driver v1.2.1
github.com/coreos/go-oidc/v3 v3.1.0
github.com/gin-contrib/location v0.0.2
@ -20,10 +20,10 @@ 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 // indirect
github.com/stretchr/testify v1.7.0
github.com/ugorji/go v1.2.6 // indirect
github.com/vektah/gqlparser/v2 v2.1.0
go.mongodb.org/mongo-driver v1.8.1 // indirect
github.com/vektah/gqlparser/v2 v2.2.0
go.mongodb.org/mongo-driver v1.8.1
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519
golang.org/x/net v0.0.0-20210614182718-04defd469f4e // indirect
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914

View File

@ -31,15 +31,15 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/99designs/gqlgen v0.13.0 h1:haLTcUp3Vwp80xMVEg5KRNwzfUrgFdRmtBY8fuB8scA=
github.com/99designs/gqlgen v0.13.0/go.mod h1:NV130r6f4tpRWuAI+zsrSdooO/eWUv+Gyyoi3rEfXIk=
github.com/99designs/gqlgen v0.14.0 h1:Wg8aNYQUjMR/4v+W3xD+7SizOy6lSvVeQ06AobNQAXI=
github.com/99designs/gqlgen v0.14.0/go.mod h1:S7z4boV+Nx4VvzMUpVrY/YuHjFX4n7rDyuTqvAkuoRE=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
github.com/agnivade/levenshtein v1.0.3 h1:M5ZnqLOoZR8ygVq0FfkXsNOKzMCk0xRiow0R5+5VkQ0=
github.com/agnivade/levenshtein v1.0.3/go.mod h1:4SFRZbbXWLF4MU1T9Qg0pGgH3Pjs+t6ie5efyrwRJXs=
github.com/agnivade/levenshtein v1.1.0 h1:n6qGwyHG61v3ABce1rPVZklEYRT8NFpCMrpZdBUbYGM=
github.com/agnivade/levenshtein v1.1.0/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/arangodb/go-driver v1.2.1 h1:HREDHhDmzdIWxHmfkfTESbYUnRjESjPh4WUuXq7FZa8=
@ -73,8 +73,8 @@ github.com/denisenkom/go-mssqldb v0.11.0 h1:9rHa233rhdOyrz2GcP9NM+gi2psgJZ4GWDpL
github.com/denisenkom/go-mssqldb v0.11.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/dgryski/trifles v0.0.0-20190318185328-a8d75aae118c h1:TUuUh0Xgj97tLMNtWtNvI9mIV6isjEb9lBMNv+77IGM=
github.com/dgryski/trifles v0.0.0-20190318185328-a8d75aae118c/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA=
github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48 h1:fRzb/w+pyskVMQ+UbP35JkH8yB7MYb4q/qhBarqZE6g=
github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
@ -89,7 +89,6 @@ github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
github.com/gin-gonic/gin v1.7.2 h1:Tg03T9yM2xa8j6I3Z3oqLaQRSmKvxPd6g/2HJ6zICFA=
github.com/gin-gonic/gin v1.7.2/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY=
github.com/go-chi/chi v3.3.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
@ -113,7 +112,7 @@ github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/protobuf v1.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
@ -246,6 +245,7 @@ github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMW
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
@ -328,7 +328,6 @@ github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMB
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
@ -338,6 +337,7 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go v1.2.6 h1:tGiWC9HENWE2tqYycIqFTNorMmFRVhNwCpDOpWqnk8E=
@ -347,8 +347,8 @@ github.com/ugorji/go/codec v1.2.6 h1:7kbGefxLoDBuYXOms4yD7223OpNMMPNPZxXk5TvFcyQ
github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw=
github.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
github.com/vektah/dataloaden v0.2.1-0.20190515034641-a19b9a6e7c9e/go.mod h1:/HUdMve7rvxZma+2ZELQeNh88+003LL7Pf/CZ089j8U=
github.com/vektah/gqlparser/v2 v2.1.0 h1:uiKJ+T5HMGGQM2kRKQ8Pxw8+Zq9qhhZhz/lieYvCMns=
github.com/vektah/gqlparser/v2 v2.1.0/go.mod h1:SyUiHgLATUR8BiYURfTirrTcGpcE+4XkV2se04Px1Ms=
github.com/vektah/gqlparser/v2 v2.2.0 h1:bAc3slekAAJW6sZTi07aGq0OrfaCjj4jxARAaC7g2EM=
github.com/vektah/gqlparser/v2 v2.2.0/go.mod h1:i3mQIGIrbK2PD1RrCeMTlVbkF2FJ6WkU1KJlJlC+3F4=
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.0.2 h1:akYIkZ28e6A96dkWNJQu3nmCzH3YfwMPQExUYDaRv7w=
@ -565,7 +565,6 @@ golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200114235610-7ae403b6b589/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
@ -581,10 +580,12 @@ golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roY
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -715,5 +716,5 @@ honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
sourcegraph.com/sourcegraph/appdash v0.0.0-20180110180208-2cc67fd64755/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
sourcegraph.com/sourcegraph/appdash-data v0.0.0-20151005221446-73f23eafcf67/go.mod h1:L5q+DGLGOQFpo1snNEkLOJT2d1YTW66rWNzatr3He1k=

File diff suppressed because it is too large Load Diff

View File

@ -2,19 +2,10 @@
package model
type AdminUpdateUserInput struct {
ID string `json:"id"`
Email *string `json:"email"`
FirstName *string `json:"firstName"`
LastName *string `json:"lastName"`
Image *string `json:"image"`
Roles []*string `json:"roles"`
}
type AuthResponse struct {
Message string `json:"message"`
AccessToken *string `json:"accessToken"`
AccessTokenExpiresAt *int64 `json:"accessTokenExpiresAt"`
AccessToken *string `json:"access_token"`
ExpiresAt *int64 `json:"expires_at"`
User *User `json:"user"`
}
@ -37,30 +28,30 @@ type LoginInput struct {
Roles []string `json:"roles"`
}
type MagicLoginInput struct {
type MagicLinkLoginInput struct {
Email string `json:"email"`
Roles []string `json:"roles"`
}
type Meta struct {
Version string `json:"version"`
IsGoogleLoginEnabled bool `json:"isGoogleLoginEnabled"`
IsFacebookLoginEnabled bool `json:"isFacebookLoginEnabled"`
IsTwitterLoginEnabled bool `json:"isTwitterLoginEnabled"`
IsGithubLoginEnabled bool `json:"isGithubLoginEnabled"`
IsEmailVerificationEnabled bool `json:"isEmailVerificationEnabled"`
IsBasicAuthenticationEnabled bool `json:"isBasicAuthenticationEnabled"`
IsMagicLoginEnabled bool `json:"isMagicLoginEnabled"`
IsGoogleLoginEnabled bool `json:"is_google_login_enabled"`
IsFacebookLoginEnabled bool `json:"is_facebook_login_enabled"`
IsGithubLoginEnabled bool `json:"is_github_login_enabled"`
IsEmailVerificationEnabled bool `json:"is_email_verification_enabled"`
IsBasicAuthenticationEnabled bool `json:"is_basic_authentication_enabled"`
IsMagicLinkLoginEnabled bool `json:"is_magic_link_login_enabled"`
}
type ResendVerifyEmailInput struct {
Email string `json:"email"`
Identifier string `json:"identifier"`
}
type ResetPasswordInput struct {
Token string `json:"token"`
Password string `json:"password"`
ConfirmPassword string `json:"confirmPassword"`
ConfirmPassword string `json:"confirm_password"`
}
type Response struct {
@ -68,36 +59,67 @@ type Response struct {
}
type SignUpInput struct {
FirstName *string `json:"firstName"`
LastName *string `json:"lastName"`
Email string `json:"email"`
GivenName *string `json:"given_name"`
FamilyName *string `json:"family_name"`
MiddleName *string `json:"middle_name"`
Nickname *string `json:"nickname"`
Gender *string `json:"gender"`
Birthdate *string `json:"birthdate"`
PhoneNumber *string `json:"phone_number"`
Picture *string `json:"picture"`
Password string `json:"password"`
ConfirmPassword string `json:"confirmPassword"`
Image *string `json:"image"`
ConfirmPassword string `json:"confirm_password"`
Roles []string `json:"roles"`
}
type UpdateProfileInput struct {
OldPassword *string `json:"oldPassword"`
NewPassword *string `json:"newPassword"`
ConfirmNewPassword *string `json:"confirmNewPassword"`
FirstName *string `json:"firstName"`
LastName *string `json:"lastName"`
Image *string `json:"image"`
OldPassword *string `json:"old_password"`
NewPassword *string `json:"new_password"`
ConfirmNewPassword *string `json:"confirm_new_password"`
Email *string `json:"email"`
GivenName *string `json:"given_name"`
FamilyName *string `json:"family_name"`
MiddleName *string `json:"middle_name"`
Nickname *string `json:"nickname"`
Gender *string `json:"gender"`
Birthdate *string `json:"birthdate"`
PhoneNumber *string `json:"phone_number"`
Picture *string `json:"picture"`
}
type UpdateUserInput struct {
ID string `json:"id"`
Email *string `json:"email"`
GivenName *string `json:"given_name"`
FamilyName *string `json:"family_name"`
MiddleName *string `json:"middle_name"`
Nickname *string `json:"nickname"`
Gender *string `json:"gender"`
Birthdate *string `json:"birthdate"`
PhoneNumber *string `json:"phone_number"`
Picture *string `json:"picture"`
Roles []*string `json:"roles"`
}
type User struct {
ID string `json:"id"`
Email string `json:"email"`
SignupMethod string `json:"signupMethod"`
FirstName *string `json:"firstName"`
LastName *string `json:"lastName"`
EmailVerifiedAt *int64 `json:"emailVerifiedAt"`
Image *string `json:"image"`
CreatedAt *int64 `json:"createdAt"`
UpdatedAt *int64 `json:"updatedAt"`
EmailVerified bool `json:"email_verified"`
SignupMethods string `json:"signup_methods"`
GivenName *string `json:"given_name"`
FamilyName *string `json:"family_name"`
MiddleName *string `json:"middle_name"`
Nickname *string `json:"nickname"`
PreferredUsername *string `json:"preferred_username"`
Gender *string `json:"gender"`
Birthdate *string `json:"birthdate"`
PhoneNumber *string `json:"phone_number"`
PhoneNumberVerified *bool `json:"phone_number_verified"`
Picture *string `json:"picture"`
Roles []string `json:"roles"`
CreatedAt *int64 `json:"created_at"`
UpdatedAt *int64 `json:"updated_at"`
}
type VerificationRequest struct {
@ -106,8 +128,8 @@ type VerificationRequest struct {
Token *string `json:"token"`
Email *string `json:"email"`
Expires *int64 `json:"expires"`
CreatedAt *int64 `json:"createdAt"`
UpdatedAt *int64 `json:"updatedAt"`
CreatedAt *int64 `json:"created_at"`
UpdatedAt *int64 `json:"updated_at"`
}
type VerifyEmailInput struct {

View File

@ -7,26 +7,33 @@ scalar Any
type Meta {
version: String!
isGoogleLoginEnabled: Boolean!
isFacebookLoginEnabled: Boolean!
isTwitterLoginEnabled: Boolean!
isGithubLoginEnabled: Boolean!
isEmailVerificationEnabled: Boolean!
isBasicAuthenticationEnabled: Boolean!
isMagicLoginEnabled: Boolean!
is_google_login_enabled: Boolean!
is_facebook_login_enabled: Boolean!
is_github_login_enabled: Boolean!
is_email_verification_enabled: Boolean!
is_basic_authentication_enabled: Boolean!
is_magic_link_login_enabled: Boolean!
}
type User {
id: ID!
email: String!
signupMethod: String!
firstName: String
lastName: String
emailVerifiedAt: Int64
image: String
createdAt: Int64
updatedAt: Int64
email_verified: Boolean!
signup_methods: String!
given_name: String
family_name: String
middle_name: String
nickname: String
# defaults to email
preferred_username: String
gender: String
birthdate: String
phone_number: String
phone_number_verified: Boolean
picture: String
roles: [String!]!
created_at: Int64
updated_at: Int64
}
type VerificationRequest {
@ -35,8 +42,8 @@ type VerificationRequest {
token: String
email: String
expires: Int64
createdAt: Int64
updatedAt: Int64
created_at: Int64
updated_at: Int64
}
type Error {
@ -46,8 +53,8 @@ type Error {
type AuthResponse {
message: String!
accessToken: String
accessTokenExpiresAt: Int64
access_token: String
expires_at: Int64
user: User
}
@ -56,12 +63,17 @@ type Response {
}
input SignUpInput {
firstName: String
lastName: String
email: String!
given_name: String
family_name: String
middle_name: String
nickname: String
gender: String
birthdate: String
phone_number: String
picture: String
password: String!
confirmPassword: String!
image: String
confirm_password: String!
roles: [String!]
}
@ -77,25 +89,35 @@ input VerifyEmailInput {
input ResendVerifyEmailInput {
email: String!
identifier: String!
}
input UpdateProfileInput {
oldPassword: String
newPassword: String
confirmNewPassword: String
firstName: String
lastName: String
image: String
old_password: String
new_password: String
confirm_new_password: String
email: String
# roles: [String]
given_name: String
family_name: String
middle_name: String
nickname: String
gender: String
birthdate: String
phone_number: String
picture: String
}
input AdminUpdateUserInput {
input UpdateUserInput {
id: ID!
email: String
firstName: String
lastName: String
image: String
given_name: String
family_name: String
middle_name: String
nickname: String
gender: String
birthdate: String
phone_number: String
picture: String
roles: [String]
}
@ -106,14 +128,14 @@ input ForgotPasswordInput {
input ResetPasswordInput {
token: String!
password: String!
confirmPassword: String!
confirm_password: String!
}
input DeleteUserInput {
email: String!
}
input MagicLoginInput {
input MagicLinkLoginInput {
email: String!
roles: [String!]
}
@ -121,21 +143,23 @@ input MagicLoginInput {
type Mutation {
signup(params: SignUpInput!): AuthResponse!
login(params: LoginInput!): AuthResponse!
magicLogin(params: MagicLoginInput!): Response!
magic_link_login(params: MagicLinkLoginInput!): Response!
logout: Response!
updateProfile(params: UpdateProfileInput!): Response!
adminUpdateUser(params: AdminUpdateUserInput!): User!
verifyEmail(params: VerifyEmailInput!): AuthResponse!
resendVerifyEmail(params: ResendVerifyEmailInput!): Response!
forgotPassword(params: ForgotPasswordInput!): Response!
resetPassword(params: ResetPasswordInput!): Response!
deleteUser(params: DeleteUserInput!): Response!
update_profile(params: UpdateProfileInput!): Response!
verify_email(params: VerifyEmailInput!): AuthResponse!
resend_verify_email(params: ResendVerifyEmailInput!): Response!
forgot_password(params: ForgotPasswordInput!): Response!
reset_password(params: ResetPasswordInput!): Response!
# admin only apis
_delete_user(params: DeleteUserInput!): Response!
_update_user(params: UpdateUserInput!): User!
}
type Query {
meta: Meta!
users: [User!]!
token(roles: [String!]): AuthResponse
session(roles: [String!]): AuthResponse
profile: User!
verificationRequests: [VerificationRequest!]!
# admin only apis
_users: [User!]!
_verification_requests: [VerificationRequest!]!
}

View File

@ -19,8 +19,8 @@ func (r *mutationResolver) Login(ctx context.Context, params model.LoginInput) (
return resolvers.Login(ctx, params)
}
func (r *mutationResolver) MagicLogin(ctx context.Context, params model.MagicLoginInput) (*model.Response, error) {
return resolvers.MagicLogin(ctx, params)
func (r *mutationResolver) MagicLinkLogin(ctx context.Context, params model.MagicLinkLoginInput) (*model.Response, error) {
return resolvers.MagicLinkLogin(ctx, params)
}
func (r *mutationResolver) Logout(ctx context.Context) (*model.Response, error) {
@ -31,10 +31,6 @@ func (r *mutationResolver) UpdateProfile(ctx context.Context, params model.Updat
return resolvers.UpdateProfile(ctx, params)
}
func (r *mutationResolver) AdminUpdateUser(ctx context.Context, params model.AdminUpdateUserInput) (*model.User, error) {
return resolvers.AdminUpdateUser(ctx, params)
}
func (r *mutationResolver) VerifyEmail(ctx context.Context, params model.VerifyEmailInput) (*model.AuthResponse, error) {
return resolvers.VerifyEmail(ctx, params)
}
@ -55,22 +51,26 @@ func (r *mutationResolver) DeleteUser(ctx context.Context, params model.DeleteUs
return resolvers.DeleteUser(ctx, params)
}
func (r *mutationResolver) UpdateUser(ctx context.Context, params model.UpdateUserInput) (*model.User, error) {
return resolvers.UpdateUser(ctx, params)
}
func (r *queryResolver) Meta(ctx context.Context) (*model.Meta, error) {
return resolvers.Meta(ctx)
}
func (r *queryResolver) Users(ctx context.Context) ([]*model.User, error) {
return resolvers.Users(ctx)
}
func (r *queryResolver) Token(ctx context.Context, roles []string) (*model.AuthResponse, error) {
return resolvers.Token(ctx, roles)
func (r *queryResolver) Session(ctx context.Context, roles []string) (*model.AuthResponse, error) {
return resolvers.Session(ctx, roles)
}
func (r *queryResolver) Profile(ctx context.Context) (*model.User, error) {
return resolvers.Profile(ctx)
}
func (r *queryResolver) Users(ctx context.Context) ([]*model.User, error) {
return resolvers.Users(ctx)
}
func (r *queryResolver) VerificationRequests(ctx context.Context) ([]*model.VerificationRequest, error) {
return resolvers.VerificationRequests(ctx)
}
@ -81,5 +81,7 @@ func (r *Resolver) Mutation() generated.MutationResolver { return &mutationResol
// Query returns generated.QueryResolver implementation.
func (r *Resolver) Query() generated.QueryResolver { return &queryResolver{r} }
type mutationResolver struct{ *Resolver }
type queryResolver struct{ *Resolver }
type (
mutationResolver struct{ *Resolver }
queryResolver struct{ *Resolver }
)

View File

@ -43,26 +43,10 @@ func processGoogleUserInfo(code string) (db.User, error) {
return user, fmt.Errorf("unable to verify id_token: %s", err.Error())
}
// Extract custom claims
var claims struct {
Email string `json:"email"`
Picture string `json:"picture"`
GivenName string `json:"given_name"`
FamilyName string `json:"family_name"`
Verified bool `json:"email_verified"`
}
if err := idToken.Claims(&claims); err != nil {
if err := idToken.Claims(&user); err != nil {
return user, fmt.Errorf("unable to extract claims")
}
user = db.User{
FirstName: claims.GivenName,
LastName: claims.FamilyName,
Image: claims.Picture,
Email: claims.Email,
EmailVerifiedAt: time.Now().Unix(),
}
return user, nil
}
@ -104,12 +88,14 @@ func processGithubUserInfo(code string) (db.User, error) {
if len(name) > 1 && strings.TrimSpace(name[1]) != "" {
lastName = name[0]
}
picture := userRawData["avatar_url"]
user = db.User{
FirstName: firstName,
LastName: lastName,
Image: userRawData["avatar_url"],
GivenName: &firstName,
FamilyName: &lastName,
Picture: &picture,
Email: userRawData["email"],
EmailVerifiedAt: time.Now().Unix(),
}
return user, nil
@ -146,12 +132,15 @@ func processFacebookUserInfo(code string) (db.User, error) {
picObject := userRawData["picture"].(map[string]interface{})["data"]
picDataObject := picObject.(map[string]interface{})
firstName := fmt.Sprintf("%v", userRawData["first_name"])
lastName := fmt.Sprintf("%v", userRawData["last_name"])
picture := fmt.Sprintf("%v", picDataObject["url"])
user = db.User{
FirstName: fmt.Sprintf("%v", userRawData["first_name"]),
LastName: fmt.Sprintf("%v", userRawData["last_name"]),
Image: fmt.Sprintf("%v", picDataObject["url"]),
GivenName: &firstName,
FamilyName: &lastName,
Picture: &picture,
Email: email,
EmailVerifiedAt: time.Now().Unix(),
}
return user, nil
@ -202,7 +191,7 @@ func OAuthCallbackHandler() gin.HandlerFunc {
if err != nil {
// user not registered, register user and generate session token
user.SignupMethod = provider
user.SignupMethods = provider
// make sure inputRoles don't include protected roles
hasProtectedRole := false
for _, ir := range inputRoles {
@ -217,16 +206,18 @@ func OAuthCallbackHandler() gin.HandlerFunc {
}
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.SignupMethod
signupMethod := existingUser.SignupMethods
if !strings.Contains(signupMethod, provider) {
signupMethod = signupMethod + "," + provider
}
user.SignupMethod = signupMethod
user.SignupMethods = signupMethod
user.Password = existingUser.Password
// There multiple scenarios with roles here in social login
@ -262,7 +253,6 @@ func OAuthCallbackHandler() gin.HandlerFunc {
user.Roles = existingUser.Roles
}
user.Key = existingUser.Key
user.ObjectID = existingUser.ObjectID
user.ID = existingUser.ID
user, err = db.Mgr.UpdateUser(user)
}
@ -274,15 +264,7 @@ func OAuthCallbackHandler() gin.HandlerFunc {
accessToken, _, _ := utils.CreateAuthToken(user, enum.AccessToken, inputRoles)
utils.SetCookie(c, accessToken)
session.SetToken(userIdStr, accessToken, refreshToken)
go func() {
sessionData := db.Session{
UserID: user.ID,
UserAgent: utils.GetUserAgent(c.Request),
IP: utils.GetIP(c.Request),
}
db.Mgr.AddSession(sessionData)
}()
utils.CreateSession(user.ID, c)
c.Redirect(http.StatusTemporaryRedirect, redirectURL)
}

View File

@ -1,7 +1,6 @@
package handlers
import (
"fmt"
"net/http"
"strings"
"time"
@ -46,29 +45,21 @@ func VerifyEmailHandler() gin.HandlerFunc {
}
// update email_verified_at in users table
if user.EmailVerifiedAt <= 0 {
user.EmailVerifiedAt = time.Now().Unix()
if user.EmailVerifiedAt == nil {
now := time.Now().Unix()
user.EmailVerifiedAt = &now
db.Mgr.UpdateUser(user)
}
// delete from verification table
db.Mgr.DeleteVerificationRequest(verificationRequest)
userIdStr := fmt.Sprintf("%v", user.ID)
roles := strings.Split(user.Roles, ",")
refreshToken, _, _ := utils.CreateAuthToken(user, enum.RefreshToken, roles)
accessToken, _, _ := utils.CreateAuthToken(user, enum.AccessToken, roles)
session.SetToken(userIdStr, accessToken, refreshToken)
go func() {
sessionData := db.Session{
UserID: user.ID,
UserAgent: utils.GetUserAgent(c.Request),
IP: utils.GetIP(c.Request),
}
db.Mgr.AddSession(sessionData)
}()
session.SetToken(user.ID, accessToken, refreshToken)
utils.CreateSession(user.ID, c)
utils.SetCookie(c, accessToken)
c.Redirect(http.StatusTemporaryRedirect, claim.RedirectURL)
}

View File

@ -1,44 +0,0 @@
package integration_test
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/env"
"github.com/authorizerdev/authorizer/server/middlewares"
"github.com/gin-contrib/location"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
)
func TestCors(t *testing.T) {
constants.ENV_PATH = "../../.env.local"
env.InitEnv()
r := gin.Default()
r.Use(location.Default())
r.Use(middlewares.GinContextToContextMiddleware())
r.Use(middlewares.CORSMiddleware())
allowedOrigin := "http://localhost:8080" // The allowed origin that you want to check
notAllowedOrigin := "http://myapp.com"
server := httptest.NewServer(r)
defer server.Close()
client := &http.Client{}
req, _ := http.NewRequest(
"GET",
"http://"+server.Listener.Addr().String()+"/api",
nil,
)
req.Header.Add("Origin", allowedOrigin)
get, _ := client.Do(req)
// You should get your origin (or a * depending on your config) if the
// passed origin is allowed.
o := get.Header.Get("Access-Control-Allow-Origin")
assert.NotEqual(t, o, notAllowedOrigin, "Origins should not match")
assert.Equal(t, o, allowedOrigin, "Origins don't match")
}

View File

@ -1,44 +1,41 @@
package main
import (
"flag"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/env"
"github.com/authorizerdev/authorizer/server/handlers"
"github.com/authorizerdev/authorizer/server/middlewares"
"github.com/authorizerdev/authorizer/server/oauth"
"github.com/authorizerdev/authorizer/server/router"
"github.com/authorizerdev/authorizer/server/session"
"github.com/authorizerdev/authorizer/server/utils"
"github.com/gin-contrib/location"
"github.com/gin-gonic/gin"
)
func main() {
env.ARG_DB_URL = flag.String("database_url", "", "Database connection string")
env.ARG_DB_TYPE = flag.String("database_type", "", "Database type, possible values are postgres,mysql,sqlite")
env.ARG_AUTHORIZER_URL = flag.String("authorizer_url", "", "URL for authorizer instance, eg: https://xyz.herokuapp.com")
env.ARG_ENV_FILE = flag.String("env_file", "", "Env file path")
flag.Parse()
env.InitEnv()
db.InitDB()
session.InitSession()
oauth.InitOAuth()
utils.InitServer()
r := gin.Default()
r.Use(location.Default())
r.Use(middlewares.GinContextToContextMiddleware())
r.Use(middlewares.CORSMiddleware())
router := router.InitRouter()
r.GET("/", handlers.PlaygroundHandler())
r.POST("/graphql", handlers.GraphqlHandler())
r.GET("/verify_email", handlers.VerifyEmailHandler())
r.GET("/oauth_login/:oauth_provider", handlers.OAuthLoginHandler())
r.GET("/oauth_callback/:oauth_provider", handlers.OAuthCallbackHandler())
// login wall app related routes
r.LoadHTMLGlob("templates/*")
app := r.Group("/app")
// login wall app related routes.
// if we put them in router file then tests would fail as templates or build path will be different
router.LoadHTMLGlob("templates/*")
app := router.Group("/app")
{
app.Static("/build", "app/build")
app.GET("/", handlers.AppHandler())
app.GET("/reset-password", handlers.AppHandler())
}
r.Run()
router.Run(":" + constants.PORT)
}

View File

@ -14,7 +14,7 @@ func GinContextToContextMiddleware() gin.HandlerFunc {
if constants.AUTHORIZER_URL == "" {
url := location.Get(c)
constants.AUTHORIZER_URL = url.Scheme + "://" + c.Request.Host
log.Println("=> authorizer url:", constants.AUTHORIZER_URL)
log.Println("authorizer url:", constants.AUTHORIZER_URL)
}
ctx := context.WithValue(c.Request.Context(), "GinContextKey", c)
c.Request = c.Request.WithContext(ctx)

View File

@ -32,15 +32,15 @@ func Login(ctx context.Context, params model.LoginInput) (*model.AuthResponse, e
return res, fmt.Errorf(`user with this email not found`)
}
if !strings.Contains(user.SignupMethod, enum.BasicAuth.String()) {
if !strings.Contains(user.SignupMethods, enum.BasicAuth.String()) {
return res, fmt.Errorf(`user has not signed up email & password`)
}
if user.EmailVerifiedAt <= 0 {
if user.EmailVerifiedAt == nil {
return res, fmt.Errorf(`email not verified`)
}
err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(params.Password))
err = bcrypt.CompareHashAndPassword([]byte(*user.Password), []byte(params.Password))
if err != nil {
log.Println("compare password error:", err)
@ -55,38 +55,18 @@ func Login(ctx context.Context, params model.LoginInput) (*model.AuthResponse, e
roles = params.Roles
}
userIdStr := fmt.Sprintf("%v", user.ID)
refreshToken, _, _ := utils.CreateAuthToken(user, enum.RefreshToken, roles)
accessToken, expiresAt, _ := utils.CreateAuthToken(user, enum.AccessToken, roles)
session.SetToken(userIdStr, accessToken, refreshToken)
go func() {
sessionData := db.Session{
UserID: user.ID,
UserAgent: utils.GetUserAgent(gc.Request),
IP: utils.GetIP(gc.Request),
}
db.Mgr.AddSession(sessionData)
}()
session.SetToken(user.ID, accessToken, refreshToken)
utils.CreateSession(user.ID, gc)
res = &model.AuthResponse{
Message: `Logged in successfully`,
AccessToken: &accessToken,
AccessTokenExpiresAt: &expiresAt,
User: &model.User{
ID: userIdStr,
Email: user.Email,
Image: &user.Image,
FirstName: &user.FirstName,
LastName: &user.LastName,
SignupMethod: user.SignupMethod,
EmailVerifiedAt: &user.EmailVerifiedAt,
Roles: strings.Split(user.Roles, ","),
CreatedAt: &user.CreatedAt,
UpdatedAt: &user.UpdatedAt,
},
ExpiresAt: &expiresAt,
User: utils.GetResponseUserData(user),
}
utils.SetCookie(gc, accessToken)

View File

@ -14,10 +14,10 @@ import (
"github.com/authorizerdev/authorizer/server/utils"
)
func MagicLogin(ctx context.Context, params model.MagicLoginInput) (*model.Response, error) {
func MagicLinkLogin(ctx context.Context, params model.MagicLinkLoginInput) (*model.Response, error) {
var res *model.Response
if constants.DISABLE_MAGIC_LOGIN {
if constants.DISABLE_MAGIC_LINK_LOGIN {
return res, fmt.Errorf(`magic link login is disabled for this instance`)
}
@ -37,7 +37,7 @@ func MagicLogin(ctx context.Context, params model.MagicLoginInput) (*model.Respo
existingUser, err := db.Mgr.GetUserByEmail(params.Email)
if err != nil {
user.SignupMethod = enum.MagicLink.String()
user.SignupMethods = enum.MagicLinkLogin.String()
// define roles for new user
if len(params.Roles) > 0 {
// check if roles exists
@ -86,12 +86,12 @@ func MagicLogin(ctx context.Context, params model.MagicLoginInput) (*model.Respo
user.Roles = existingUser.Roles
}
signupMethod := existingUser.SignupMethod
if !strings.Contains(signupMethod, enum.MagicLink.String()) {
signupMethod = signupMethod + "," + enum.MagicLink.String()
signupMethod := existingUser.SignupMethods
if !strings.Contains(signupMethod, enum.MagicLinkLogin.String()) {
signupMethod = signupMethod + "," + enum.MagicLinkLogin.String()
}
user.SignupMethod = signupMethod
user.SignupMethods = signupMethod
user, _ = db.Mgr.UpdateUser(user)
if err != nil {
log.Println("error updating user:", err)
@ -100,7 +100,7 @@ func MagicLogin(ctx context.Context, params model.MagicLoginInput) (*model.Respo
if !constants.DISABLE_EMAIL_VERIFICATION {
// insert verification request
verificationType := enum.MagicLink.String()
verificationType := enum.MagicLinkLogin.String()
token, err := utils.CreateVerificationToken(params.Email, verificationType)
if err != nil {
log.Println(`error generating token`, err)

View File

@ -3,7 +3,6 @@ package resolvers
import (
"context"
"fmt"
"strings"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/graph/model"
@ -41,20 +40,7 @@ func Profile(ctx context.Context) (*model.User, error) {
return res, err
}
userIdStr := fmt.Sprintf("%v", user.ID)
res = &model.User{
ID: userIdStr,
Email: user.Email,
Image: &user.Image,
FirstName: &user.FirstName,
LastName: &user.LastName,
SignupMethod: user.SignupMethod,
EmailVerifiedAt: &user.EmailVerifiedAt,
Roles: strings.Split(user.Roles, ","),
CreatedAt: &user.CreatedAt,
UpdatedAt: &user.UpdatedAt,
}
res = utils.GetResponseUserData(user)
return res, nil
}

View File

@ -20,18 +20,28 @@ func ResendVerifyEmail(ctx context.Context, params model.ResendVerifyEmailInput)
return res, fmt.Errorf("invalid email")
}
verificationRequest, err := db.Mgr.GetVerificationByEmail(params.Email)
if !utils.IsValidVerificationIdentifier(params.Identifier) {
return res, fmt.Errorf("invalid identifier")
}
verificationRequest, err := db.Mgr.GetVerificationByEmail(params.Email, params.Identifier)
if err != nil {
return res, fmt.Errorf(`verification request not found`)
}
token, err := utils.CreateVerificationToken(params.Email, verificationRequest.Identifier)
// delete current verification and create new one
err = db.Mgr.DeleteVerificationRequest(verificationRequest)
if err != nil {
log.Println("error deleting verification request:", err)
}
token, err := utils.CreateVerificationToken(params.Email, params.Identifier)
if err != nil {
log.Println(`error generating token`, err)
}
db.Mgr.AddVerification(db.VerificationRequest{
Token: token,
Identifier: verificationRequest.Identifier,
Identifier: params.Identifier,
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
Email: params.Email,
})

View File

@ -39,13 +39,13 @@ func ResetPassword(ctx context.Context, params model.ResetPasswordInput) (*model
}
password, _ := utils.HashPassword(params.Password)
user.Password = password
user.Password = &password
signupMethod := user.SignupMethod
signupMethod := user.SignupMethods
if !strings.Contains(signupMethod, enum.BasicAuth.String()) {
signupMethod = signupMethod + "," + enum.BasicAuth.String()
}
user.SignupMethod = signupMethod
user.SignupMethods = signupMethod
// delete from verification table
db.Mgr.DeleteVerificationRequest(verificationRequest)

View File

@ -3,7 +3,6 @@ package resolvers
import (
"context"
"fmt"
"strings"
"time"
"github.com/authorizerdev/authorizer/server/constants"
@ -14,7 +13,7 @@ import (
"github.com/authorizerdev/authorizer/server/utils"
)
func Token(ctx context.Context, roles []string) (*model.AuthResponse, error) {
func Session(ctx context.Context, roles []string) (*model.AuthResponse, error) {
var res *model.AuthResponse
gc, err := utils.GinContextFromContext(ctx)
@ -67,34 +66,15 @@ func Token(ctx context.Context, roles []string) (*model.AuthResponse, error) {
session.DeleteVerificationRequest(userIdStr, token)
token, expiresAt, _ = utils.CreateAuthToken(user, enum.AccessToken, claimRoles)
session.SetToken(userIdStr, token, currentRefreshToken)
go func() {
sessionData := db.Session{
UserID: user.ID,
UserAgent: utils.GetUserAgent(gc.Request),
IP: utils.GetIP(gc.Request),
}
db.Mgr.AddSession(sessionData)
}()
utils.CreateSession(user.ID, gc)
}
utils.SetCookie(gc, token)
res = &model.AuthResponse{
Message: `Token verified`,
AccessToken: &token,
AccessTokenExpiresAt: &expiresAt,
User: &model.User{
ID: userIdStr,
Email: user.Email,
Image: &user.Image,
FirstName: &user.FirstName,
LastName: &user.LastName,
Roles: strings.Split(user.Roles, ","),
CreatedAt: &user.CreatedAt,
UpdatedAt: &user.UpdatedAt,
SignupMethod: user.SignupMethod,
EmailVerifiedAt: &user.EmailVerifiedAt,
},
ExpiresAt: &expiresAt,
User: utils.GetResponseUserData(user),
}
return res, nil
}

View File

@ -54,10 +54,10 @@ func Signup(ctx context.Context, params model.SignUpInput) (*model.AuthResponse,
log.Println("user with email " + params.Email + " not found")
}
if existingUser.EmailVerifiedAt > 0 {
if existingUser.EmailVerifiedAt != nil {
// email is verified
return res, fmt.Errorf(`%s has already signed up`, params.Email)
} else if existingUser.ID != "" && existingUser.EmailVerifiedAt <= 0 {
} else if existingUser.ID != "" && existingUser.EmailVerifiedAt == nil {
return res, fmt.Errorf("%s has already signed up. please complete the email verification process or reset the password", params.Email)
}
@ -68,19 +68,44 @@ func Signup(ctx context.Context, params model.SignUpInput) (*model.AuthResponse,
user.Roles = strings.Join(inputRoles, ",")
password, _ := utils.HashPassword(params.Password)
user.Password = password
user.Password = &password
if params.FirstName != nil {
user.FirstName = *params.FirstName
if params.GivenName != nil {
user.GivenName = params.GivenName
}
if params.LastName != nil {
user.LastName = *params.LastName
if params.FamilyName != nil {
user.FamilyName = params.FamilyName
}
user.SignupMethod = enum.BasicAuth.String()
if params.MiddleName != nil {
user.MiddleName = params.MiddleName
}
if params.Nickname != nil {
user.Nickname = params.Nickname
}
if params.Gender != nil {
user.Gender = params.Gender
}
if params.Birthdate != nil {
user.Birthdate = params.Birthdate
}
if params.PhoneNumber != nil {
user.PhoneNumber = params.PhoneNumber
}
if params.Picture != nil {
user.Picture = params.Picture
}
user.SignupMethods = enum.BasicAuth.String()
if constants.DISABLE_EMAIL_VERIFICATION {
user.EmailVerifiedAt = time.Now().Unix()
now := time.Now().Unix()
user.EmailVerifiedAt = &now
}
user, err = db.Mgr.AddUser(user)
if err != nil {
@ -88,18 +113,7 @@ func Signup(ctx context.Context, params model.SignUpInput) (*model.AuthResponse,
}
userIdStr := fmt.Sprintf("%v", user.ID)
roles := strings.Split(user.Roles, ",")
userToReturn := &model.User{
ID: userIdStr,
Email: user.Email,
Image: &user.Image,
FirstName: &user.FirstName,
LastName: &user.LastName,
SignupMethod: user.SignupMethod,
EmailVerifiedAt: &user.EmailVerifiedAt,
Roles: strings.Split(user.Roles, ","),
CreatedAt: &user.CreatedAt,
UpdatedAt: &user.UpdatedAt,
}
userToReturn := utils.GetResponseUserData(user)
if !constants.DISABLE_EMAIL_VERIFICATION {
// insert verification request
@ -131,19 +145,11 @@ func Signup(ctx context.Context, params model.SignUpInput) (*model.AuthResponse,
accessToken, expiresAt, _ := utils.CreateAuthToken(user, enum.AccessToken, roles)
session.SetToken(userIdStr, accessToken, refreshToken)
go func() {
sessionData := db.Session{
UserID: user.ID,
UserAgent: utils.GetUserAgent(gc.Request),
IP: utils.GetIP(gc.Request),
}
db.Mgr.AddSession(sessionData)
}()
utils.CreateSession(user.ID, gc)
res = &model.AuthResponse{
Message: `Signed up successfully.`,
AccessToken: &accessToken,
AccessTokenExpiresAt: &expiresAt,
ExpiresAt: &expiresAt,
User: userToReturn,
}

View File

@ -40,7 +40,7 @@ func UpdateProfile(ctx context.Context, params model.UpdateProfileInput) (*model
}
// validate if all params are not empty
if params.FirstName == nil && params.LastName == nil && params.Image == nil && params.OldPassword == nil && params.Email == nil {
if params.GivenName == nil && params.FamilyName == nil && params.Picture == nil && params.MiddleName == nil && params.Nickname == nil && params.OldPassword == nil && params.Email == nil && params.Birthdate == nil && params.Gender == nil && params.PhoneNumber == nil {
return res, fmt.Errorf("please enter atleast one param to update")
}
@ -50,20 +50,40 @@ func UpdateProfile(ctx context.Context, params model.UpdateProfileInput) (*model
return res, err
}
if params.FirstName != nil && user.FirstName != *params.FirstName {
user.FirstName = *params.FirstName
if params.GivenName != nil && user.GivenName != params.GivenName {
user.GivenName = params.GivenName
}
if params.LastName != nil && user.LastName != *params.LastName {
user.LastName = *params.LastName
if params.FamilyName != nil && user.FamilyName != params.FamilyName {
user.FamilyName = params.FamilyName
}
if params.Image != nil && user.Image != *params.Image {
user.Image = *params.Image
if params.MiddleName != nil && user.MiddleName != params.MiddleName {
user.MiddleName = params.MiddleName
}
if params.Nickname != nil && user.Nickname != params.Nickname {
user.Nickname = params.Nickname
}
if params.Birthdate != nil && user.Birthdate != params.Birthdate {
user.Birthdate = params.Birthdate
}
if params.Gender != nil && user.Gender != params.Gender {
user.Gender = params.Gender
}
if params.PhoneNumber != nil && user.PhoneNumber != params.PhoneNumber {
user.PhoneNumber = params.PhoneNumber
}
if params.Picture != nil && user.Picture != params.Picture {
user.Picture = params.Picture
}
if params.OldPassword != nil {
if err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(*params.OldPassword)); err != nil {
if err = bcrypt.CompareHashAndPassword([]byte(*user.Password), []byte(*params.OldPassword)); err != nil {
return res, fmt.Errorf("incorrect old password")
}
@ -81,7 +101,7 @@ func UpdateProfile(ctx context.Context, params model.UpdateProfileInput) (*model
password, _ := utils.HashPassword(*params.NewPassword)
user.Password = password
user.Password = &password
}
hasEmailChanged := false
@ -93,7 +113,8 @@ func UpdateProfile(ctx context.Context, params model.UpdateProfileInput) (*model
}
newEmail := strings.ToLower(*params.Email)
// check if user with new email exists
_, err = db.Mgr.GetUserByEmail(newEmail)
_, err := db.Mgr.GetUserByEmail(newEmail)
// err = nil means user exists
if err == nil {
return res, fmt.Errorf("user with this email address already exists")
@ -103,7 +124,7 @@ func UpdateProfile(ctx context.Context, params model.UpdateProfileInput) (*model
utils.DeleteCookie(gc)
user.Email = newEmail
user.EmailVerifiedAt = 0
user.EmailVerifiedAt = nil
hasEmailChanged = true
// insert verification request
verificationType := enum.UpdateEmail.String()

View File

@ -15,7 +15,7 @@ import (
"github.com/authorizerdev/authorizer/server/utils"
)
func AdminUpdateUser(ctx context.Context, params model.AdminUpdateUserInput) (*model.User, error) {
func UpdateUser(ctx context.Context, params model.UpdateUserInput) (*model.User, error) {
gc, err := utils.GinContextFromContext(ctx)
var res *model.User
if err != nil {
@ -26,7 +26,7 @@ func AdminUpdateUser(ctx context.Context, params model.AdminUpdateUserInput) (*m
return res, fmt.Errorf("unauthorized")
}
if params.FirstName == nil && params.LastName == nil && params.Image == nil && params.Email == nil && params.Roles == nil {
if params.GivenName == nil && params.FamilyName == nil && params.Picture == nil && params.MiddleName == nil && params.Nickname == nil && params.Email == nil && params.Birthdate == nil && params.Gender == nil && params.PhoneNumber == nil && params.Roles == nil {
return res, fmt.Errorf("please enter atleast one param to update")
}
@ -35,16 +35,36 @@ func AdminUpdateUser(ctx context.Context, params model.AdminUpdateUserInput) (*m
return res, fmt.Errorf(`User not found`)
}
if params.FirstName != nil && user.FirstName != *params.FirstName {
user.FirstName = *params.FirstName
if params.GivenName != nil && user.GivenName != params.GivenName {
user.GivenName = params.GivenName
}
if params.LastName != nil && user.LastName != *params.LastName {
user.LastName = *params.LastName
if params.FamilyName != nil && user.FamilyName != params.FamilyName {
user.FamilyName = params.FamilyName
}
if params.Image != nil && user.Image != *params.Image {
user.Image = *params.Image
if params.MiddleName != nil && user.MiddleName != params.MiddleName {
user.MiddleName = params.MiddleName
}
if params.Nickname != nil && user.Nickname != params.Nickname {
user.Nickname = params.Nickname
}
if params.Birthdate != nil && user.Birthdate != params.Birthdate {
user.Birthdate = params.Birthdate
}
if params.Gender != nil && user.Gender != params.Gender {
user.Gender = params.Gender
}
if params.PhoneNumber != nil && user.PhoneNumber != params.PhoneNumber {
user.PhoneNumber = params.PhoneNumber
}
if params.Picture != nil && user.Picture != params.Picture {
user.Picture = params.Picture
}
if params.Email != nil && user.Email != *params.Email {
@ -64,7 +84,7 @@ func AdminUpdateUser(ctx context.Context, params model.AdminUpdateUserInput) (*m
utils.DeleteCookie(gc)
user.Email = newEmail
user.EmailVerifiedAt = 0
user.EmailVerifiedAt = nil
// insert verification request
verificationType := enum.UpdateEmail.String()
token, err := utils.CreateVerificationToken(newEmail, verificationType)
@ -117,9 +137,9 @@ func AdminUpdateUser(ctx context.Context, params model.AdminUpdateUserInput) (*m
res = &model.User{
ID: params.ID,
Email: user.Email,
Image: &user.Image,
FirstName: &user.FirstName,
LastName: &user.LastName,
Picture: user.Picture,
GivenName: user.GivenName,
FamilyName: user.FamilyName,
Roles: strings.Split(user.Roles, ","),
CreatedAt: &user.CreatedAt,
UpdatedAt: &user.UpdatedAt,

View File

@ -3,7 +3,6 @@ package resolvers
import (
"context"
"fmt"
"strings"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/graph/model"
@ -27,17 +26,7 @@ func Users(ctx context.Context) ([]*model.User, error) {
}
for i := 0; i < len(users); i++ {
res = append(res, &model.User{
ID: fmt.Sprintf("%v", users[i].ID),
Email: users[i].Email,
SignupMethod: users[i].SignupMethod,
FirstName: &users[i].FirstName,
LastName: &users[i].LastName,
EmailVerifiedAt: &users[i].EmailVerifiedAt,
Roles: strings.Split(users[i].Roles, ","),
CreatedAt: &users[i].CreatedAt,
UpdatedAt: &users[i].UpdatedAt,
})
res = append(res, utils.GetResponseUserData(users[i]))
}
return res, nil

View File

@ -37,44 +37,25 @@ func VerifyEmail(ctx context.Context, params model.VerifyEmailInput) (*model.Aut
}
// update email_verified_at in users table
user.EmailVerifiedAt = time.Now().Unix()
now := time.Now().Unix()
user.EmailVerifiedAt = &now
db.Mgr.UpdateUser(user)
// delete from verification table
db.Mgr.DeleteVerificationRequest(verificationRequest)
userIdStr := fmt.Sprintf("%v", user.ID)
roles := strings.Split(user.Roles, ",")
refreshToken, _, _ := utils.CreateAuthToken(user, enum.RefreshToken, roles)
accessToken, expiresAt, _ := utils.CreateAuthToken(user, enum.AccessToken, roles)
session.SetToken(userIdStr, accessToken, refreshToken)
go func() {
sessionData := db.Session{
UserID: user.ID,
UserAgent: utils.GetUserAgent(gc.Request),
IP: utils.GetIP(gc.Request),
}
db.Mgr.AddSession(sessionData)
}()
session.SetToken(user.ID, accessToken, refreshToken)
utils.CreateSession(user.ID, gc)
res = &model.AuthResponse{
Message: `Email verified successfully.`,
AccessToken: &accessToken,
AccessTokenExpiresAt: &expiresAt,
User: &model.User{
ID: userIdStr,
Email: user.Email,
Image: &user.Image,
FirstName: &user.FirstName,
LastName: &user.LastName,
SignupMethod: user.SignupMethod,
EmailVerifiedAt: &user.EmailVerifiedAt,
Roles: strings.Split(user.Roles, ","),
CreatedAt: &user.CreatedAt,
UpdatedAt: &user.UpdatedAt,
},
ExpiresAt: &expiresAt,
User: utils.GetResponseUserData(user),
}
utils.SetCookie(gc, accessToken)

23
server/router/router.go Normal file
View File

@ -0,0 +1,23 @@
package router
import (
"github.com/authorizerdev/authorizer/server/handlers"
"github.com/authorizerdev/authorizer/server/middlewares"
"github.com/gin-contrib/location"
"github.com/gin-gonic/gin"
)
func InitRouter() *gin.Engine {
router := gin.Default()
router.Use(location.Default())
router.Use(middlewares.GinContextToContextMiddleware())
router.Use(middlewares.CORSMiddleware())
router.GET("/", handlers.PlaygroundHandler())
router.POST("/graphql", handlers.GraphqlHandler())
router.GET("/verify_email", handlers.VerifyEmailHandler())
router.GET("/oauth_login/:oauth_provider", handlers.OAuthLoginHandler())
router.GET("/oauth_callback/:oauth_provider", handlers.OAuthCallbackHandler())
return router
}

View File

@ -26,34 +26,31 @@ func CreateAuthToken(user db.User, tokenType enum.TokenType, roles []string) (st
expiresAt := time.Now().Add(expiryBound).Unix()
resUser := GetResponseUserData(user)
userBytes, _ := json.Marshal(&resUser)
var userMap map[string]interface{}
json.Unmarshal(userBytes, &userMap)
customClaims := jwt.MapClaims{
"exp": expiresAt,
"iat": time.Now().Unix(),
"token_type": tokenType.String(),
"email": user.Email,
"id": user.ID,
"allowed_roles": strings.Split(user.Roles, ","),
constants.JWT_ROLE_CLAIM: roles,
}
// check for the extra access token script
accessTokenScript := os.Getenv("CUSTOM_ACCESS_TOKEN_SCRIPT")
if accessTokenScript != "" {
userInfo := map[string]interface{}{
"id": user.ID,
"email": user.Email,
"firstName": user.FirstName,
"lastName": user.LastName,
"image": user.Image,
"roles": strings.Split(user.Roles, ","),
"signUpMethods": strings.Split(user.SignupMethod, ","),
for k, v := range userMap {
if k != "roles" {
customClaims[k] = v
}
}
// check for the extra access token script
accessTokenScript := os.Getenv("CUSTOM_ACCESS_TOKEN_SCRIPT")
if accessTokenScript != "" {
vm := otto.New()
userBytes, _ := json.Marshal(userInfo)
claimBytes, _ := json.Marshal(customClaims)
claimBytes, _ := json.Marshal(customClaims)
vm.Run(fmt.Sprintf(`
var user = %s;
var tokenPayload = %s;

View File

@ -0,0 +1,16 @@
package utils
import (
"github.com/authorizerdev/authorizer/server/db"
"github.com/gin-gonic/gin"
)
func CreateSession(userId string, c *gin.Context) {
sessionData := db.Session{
UserID: userId,
UserAgent: GetUserAgent(c.Request),
IP: GetIP(c.Request),
}
db.Mgr.AddSession(sessionData)
}

View File

@ -0,0 +1,32 @@
package utils
import (
"strings"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/graph/model"
)
func GetResponseUserData(user db.User) *model.User {
isEmailVerified := user.EmailVerifiedAt != nil
isPhoneVerified := user.PhoneNumberVerifiedAt != nil
return &model.User{
ID: user.ID,
Email: user.Email,
EmailVerified: isEmailVerified,
SignupMethods: user.SignupMethods,
GivenName: user.GivenName,
FamilyName: user.FamilyName,
MiddleName: user.MiddleName,
Nickname: user.Nickname,
PreferredUsername: &user.Email,
Gender: user.Gender,
Birthdate: user.Birthdate,
PhoneNumber: user.PhoneNumber,
PhoneNumberVerified: &isPhoneVerified,
Picture: user.Picture,
Roles: strings.Split(user.Roles, ","),
CreatedAt: &user.CreatedAt,
UpdatedAt: &user.UpdatedAt,
}
}

View File

@ -13,9 +13,8 @@ func GetMetaInfo() model.Meta {
IsGoogleLoginEnabled: constants.GOOGLE_CLIENT_ID != "" && constants.GOOGLE_CLIENT_SECRET != "",
IsGithubLoginEnabled: constants.GITHUB_CLIENT_ID != "" && constants.GOOGLE_CLIENT_SECRET != "",
IsFacebookLoginEnabled: constants.FACEBOOK_CLIENT_ID != "" && constants.FACEBOOK_CLIENT_SECRET != "",
IsTwitterLoginEnabled: constants.TWITTER_CLIENT_ID != "" && constants.TWITTER_CLIENT_SECRET != "",
IsBasicAuthenticationEnabled: !constants.DISABLE_BASIC_AUTHENTICATION,
IsEmailVerificationEnabled: !constants.DISABLE_EMAIL_VERIFICATION,
IsMagicLoginEnabled: !constants.DISABLE_MAGIC_LOGIN,
IsMagicLinkLoginEnabled: !constants.DISABLE_MAGIC_LINK_LOGIN,
}
}

View File

@ -6,6 +6,7 @@ import (
"strings"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/enum"
"github.com/gin-gonic/gin"
)
@ -69,6 +70,13 @@ func IsValidRoles(userRoles []string, roles []string) bool {
return valid
}
func IsValidVerificationIdentifier(identifier string) bool {
if identifier != enum.BasicAuthSignup.String() && identifier != enum.ForgotPassword.String() && identifier != enum.UpdateEmail.String() {
return false
}
return true
}
func IsStringArrayEqual(a, b []string) bool {
if len(a) != len(b) {
return false

View File

@ -1,34 +0,0 @@
package utils
import (
"testing"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/stretchr/testify/assert"
)
func TestIsValidEmail(t *testing.T) {
validEmail := "lakhan@gmail.com"
invalidEmail1 := "lakhan"
invalidEmail2 := "lakhan.me"
assert.True(t, IsValidEmail(validEmail), "it should be valid email")
assert.False(t, IsValidEmail(invalidEmail1), "it should be invalid email")
assert.False(t, IsValidEmail(invalidEmail2), "it should be invalid email")
}
func TestIsValidOrigin(t *testing.T) {
// don't use portocal(http/https) for ALLOWED_ORIGINS while testing,
// as we trim them off while running the main function
constants.ALLOWED_ORIGINS = []string{"localhost:8080", "*.google.com", "*.google.in", "*abc.*"}
assert.False(t, IsValidOrigin("http://myapp.com"), "it should be invalid origin")
assert.False(t, IsValidOrigin("http://appgoogle.com"), "it should be invalid origin")
assert.True(t, IsValidOrigin("http://app.google.com"), "it should be valid origin")
assert.False(t, IsValidOrigin("http://app.google.ind"), "it should be invalid origin")
assert.True(t, IsValidOrigin("http://app.google.in"), "it should be valid origin")
assert.True(t, IsValidOrigin("http://xyx.abc.com"), "it should be valid origin")
assert.True(t, IsValidOrigin("http://xyx.abc.in"), "it should be valid origin")
assert.True(t, IsValidOrigin("http://xyxabc.in"), "it should be valid origin")
assert.True(t, IsValidOrigin("http://localhost:8080"), "it should be valid origin")
}

View File

@ -24,7 +24,6 @@ func CreateVerificationToken(email string, tokenType string) (string, error) {
t.Claims = &CustomClaim{
&jwt.StandardClaims{
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
},
tokenType,